summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml10
-rw-r--r--CONTRIBUTING.md5
-rw-r--r--Makefile.in16
-rw-r--r--bootstrap/bin/no_dot_erlang.bootbin6544 -> 6563 bytes
-rw-r--r--bootstrap/bin/start.bootbin6544 -> 6563 bytes
-rw-r--r--bootstrap/bin/start_clean.bootbin6544 -> 6563 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_a.beambin3200 -> 3188 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_asm.beambin11052 -> 11084 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_block.beambin3444 -> 3896 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_call_types.beambin0 -> 7672 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_clean.beambin3516 -> 3856 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_dict.beambin4644 -> 4696 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_disasm.beambin20868 -> 20836 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_except.beambin4204 -> 0 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_flatten.beambin1928 -> 1924 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_jump.beambin10012 -> 9928 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_kernel_to_ssa.beambin28684 -> 28768 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_listing.beambin1580 -> 1564 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_opcodes.beambin7548 -> 7588 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_peep.beambin3588 -> 3532 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_ssa.beambin12168 -> 12144 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_ssa_bsm.beambin17888 -> 17704 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_ssa_codegen.beambin37688 -> 38368 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_ssa_dead.beambin12004 -> 12292 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_ssa_funs.beambin2556 -> 2556 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_ssa_lint.beambin7528 -> 7880 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_ssa_opt.beambin39960 -> 40332 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_ssa_pp.beambin5500 -> 5444 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_ssa_pre_codegen.beambin45604 -> 47208 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_ssa_recv.beambin3884 -> 3812 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_ssa_share.beambin5348 -> 5328 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_ssa_type.beambin28636 -> 24064 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_trim.beambin8676 -> 8672 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_types.beambin0 -> 10160 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_utils.beambin3548 -> 3536 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_validator.beambin50216 -> 44920 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/beam_z.beambin3604 -> 3588 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/cerl.beambin28236 -> 28060 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/cerl_clauses.beambin2808 -> 2700 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/cerl_inline.beambin34712 -> 34364 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/cerl_sets.beambin2728 -> 2648 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/cerl_trees.beambin20408 -> 20096 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/compile.beambin41388 -> 41152 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/compiler.app5
-rw-r--r--bootstrap/lib/compiler/ebin/core_lib.beambin3720 -> 3652 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/core_lint.beambin12472 -> 12400 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/core_parse.beambin63064 -> 62656 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/core_pp.beambin11472 -> 11332 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/core_scan.beambin6248 -> 6124 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/erl_bifs.beambin2080 -> 2080 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/rec_env.beambin4468 -> 4412 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/sys_core_alias.beambin5548 -> 5380 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/sys_core_bsm.beambin1672 -> 1648 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/sys_core_fold.beambin45228 -> 41132 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/sys_core_fold_lists.beambin4020 -> 4020 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/sys_core_inline.beambin3908 -> 3856 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/sys_pre_attributes.beambin2468 -> 2444 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/v3_core.beambin50668 -> 50204 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/v3_kernel.beambin50556 -> 52068 bytes
-rw-r--r--bootstrap/lib/compiler/ebin/v3_kernel_pp.beambin12052 -> 11932 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/application.beambin3852 -> 3844 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/application_controller.beambin30032 -> 29488 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/application_master.beambin6108 -> 6068 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/application_starter.beambin1180 -> 1168 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/auth.beambin6148 -> 6116 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/code.beambin12688 -> 12760 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/code_server.beambin22544 -> 22288 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/disk_log.beambin29556 -> 29240 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/disk_log_1.beambin22352 -> 22348 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/disk_log_server.beambin6136 -> 6000 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/disk_log_sup.beambin556 -> 556 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/dist_ac.beambin23356 -> 23160 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/dist_util.beambin12304 -> 12220 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/erl_boot_server.beambin5572 -> 5560 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/erl_compile_server.beambin0 -> 4928 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/erl_ddll.beambin2744 -> 2736 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/erl_distribution.beambin1872 -> 1872 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/erl_epmd.beambin7036 -> 7128 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/erl_reply.beambin888 -> 856 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/erl_signal_handler.beambin1100 -> 1100 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/error_handler.beambin1576 -> 1576 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/error_logger.beambin6112 -> 6112 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/erts_debug.beambin9228 -> 9184 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/file.beambin13348 -> 13444 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/file_io_server.beambin15292 -> 15356 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/file_server.beambin4912 -> 4888 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/gen_sctp.beambin3212 -> 3212 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/gen_tcp.beambin2096 -> 2096 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/gen_udp.beambin1324 -> 1636 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/global.beambin28700 -> 28308 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/global_group.beambin15708 -> 15600 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/global_search.beambin2920 -> 2912 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/group.beambin14200 -> 14044 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/group_history.beambin5496 -> 5492 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/heart.beambin5140 -> 5124 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/hipe_unified_loader.beambin12336 -> 12280 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet.beambin23232 -> 23288 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet6_sctp.beambin1440 -> 1436 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet6_tcp.beambin3016 -> 3004 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet6_tcp_dist.beambin864 -> 864 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet6_udp.beambin1720 -> 2092 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_config.beambin7236 -> 7212 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_db.beambin25360 -> 24804 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_dns.beambin18564 -> 18396 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_gethost_native.beambin9708 -> 9620 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_hosts.beambin1896 -> 1892 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_parse.beambin13396 -> 13172 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_res.beambin13192 -> 12956 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_sctp.beambin2148 -> 2140 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_tcp.beambin2784 -> 2772 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_tcp_dist.beambin7476 -> 7408 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/inet_udp.beambin1908 -> 2176 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/kernel.app4
-rw-r--r--bootstrap/lib/kernel/ebin/kernel.beambin3628 -> 3744 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/kernel_config.beambin2604 -> 2604 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/kernel_refc.beambin2288 -> 2292 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/local_tcp.beambin2180 -> 2184 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/local_udp.beambin1372 -> 1388 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/logger.beambin15060 -> 14996 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/logger_backend.beambin2544 -> 2544 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/logger_config.beambin3176 -> 3156 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/logger_disk_log_h.beambin3348 -> 3340 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/logger_filters.beambin1748 -> 1744 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/logger_formatter.beambin9060 -> 8980 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/logger_h_common.beambin7696 -> 7640 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/logger_handler_watcher.beambin1344 -> 1340 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/logger_olp.beambin8308 -> 8268 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/logger_proxy.beambin2884 -> 2884 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/logger_server.beambin11352 -> 11256 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/logger_simple_h.beambin4212 -> 4204 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/logger_std_h.beambin9544 -> 9556 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/logger_sup.beambin636 -> 636 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/net.beambin0 -> 1624 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/net_adm.beambin2824 -> 2816 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/net_kernel.beambin24164 -> 23956 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/os.beambin5112 -> 5052 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/pg2.beambin7568 -> 7536 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/ram_file.beambin6008 -> 6004 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/raw_file_io.beambin1660 -> 1668 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/raw_file_io_compressed.beambin2308 -> 2344 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/raw_file_io_deflate.beambin2592 -> 2592 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/raw_file_io_delayed.beambin5200 -> 5248 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/raw_file_io_inflate.beambin4140 -> 4128 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/raw_file_io_list.beambin2460 -> 2564 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/raw_file_io_raw.beambin396 -> 396 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/rpc.beambin7704 -> 7676 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/seq_trace.beambin1600 -> 1632 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/standard_error.beambin3724 -> 3708 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/user.beambin10980 -> 10932 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/user_drv.beambin11020 -> 10796 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/user_sup.beambin1704 -> 1700 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/wrap_log_reader.beambin2940 -> 2912 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/array.beambin11568 -> 11492 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/base64.beambin6496 -> 6152 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/beam_lib.beambin18852 -> 18732 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/binary.beambin2844 -> 2832 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/c.beambin16968 -> 16800 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/calendar.beambin8104 -> 8028 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/dets.beambin45120 -> 44904 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/dets_server.beambin6444 -> 6344 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/dets_sup.beambin544 -> 544 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/dets_utils.beambin25696 -> 25396 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/dets_v9.beambin45360 -> 44876 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/dict.beambin8836 -> 8648 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/digraph.beambin7500 -> 7432 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/digraph_utils.beambin6732 -> 6300 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/edlin.beambin10472 -> 10376 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/edlin_expand.beambin3748 -> 3676 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/epp.beambin28328 -> 27956 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_abstract_code.beambin968 -> 960 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_anno.beambin3484 -> 3452 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_bits.beambin2348 -> 2348 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_compile.beambin6676 -> 6756 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_error.beambin8252 -> 8192 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_eval.beambin35048 -> 34452 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_expand_records.beambin19344 -> 19144 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_internal.beambin6732 -> 6716 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_lint.beambin86340 -> 84896 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_parse.beambin96136 -> 95208 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_posix_msg.beambin5168 -> 5168 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_pp.beambin27160 -> 26644 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_scan.beambin25696 -> 24048 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_tar.beambin30972 -> 30364 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/error_logger_file_h.beambin3944 -> 3928 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/error_logger_tty_h.beambin4776 -> 4764 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/escript.beambin15628 -> 15532 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/ets.beambin21604 -> 21228 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/eval_bits.beambin7660 -> 7624 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/file_sorter.beambin27268 -> 26984 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/filelib.beambin10188 -> 10100 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/filename.beambin14772 -> 14656 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gb_sets.beambin7712 -> 7676 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gb_trees.beambin5084 -> 5088 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gen.beambin4896 -> 4856 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gen_event.beambin13012 -> 12632 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gen_fsm.beambin12424 -> 12116 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gen_server.beambin15328 -> 14760 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/gen_statem.beambin20408 -> 20304 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/io.beambin5792 -> 5792 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/io_lib.beambin13636 -> 13572 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/io_lib_format.beambin14868 -> 14720 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/io_lib_fread.beambin6508 -> 6420 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/io_lib_pretty.beambin21116 -> 20928 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/lists.beambin29272 -> 28788 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/log_mf_h.beambin2352 -> 2352 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/maps.beambin3188 -> 3172 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/math.beambin1288 -> 1288 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/ms_transform.beambin18452 -> 18376 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/orddict.beambin2900 -> 2884 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/ordsets.beambin1940 -> 1844 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/otp_internal.beambin8256 -> 8172 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/pool.beambin3664 -> 3664 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/proc_lib.beambin12624 -> 12540 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/proplists.beambin4596 -> 4552 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/qlc.beambin65048 -> 64332 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/qlc_pt.beambin70588 -> 69736 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/queue.beambin5908 -> 5904 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/rand.beambin29048 -> 28624 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/random.beambin1768 -> 1756 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/re.beambin12308 -> 12360 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/sets.beambin6152 -> 6124 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/shell.beambin28396 -> 28204 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/shell_default.beambin4064 -> 4064 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/slave.beambin4740 -> 4728 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/sofs.beambin35276 -> 34764 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/stdlib.app4
-rw-r--r--bootstrap/lib/stdlib/ebin/string.beambin35872 -> 35316 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/supervisor.beambin21588 -> 21256 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/supervisor_bridge.beambin2332 -> 2332 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/sys.beambin9104 -> 9028 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/timer.beambin5272 -> 5228 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/unicode.beambin14076 -> 13656 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/unicode_util.beambin200320 -> 200256 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/uri_string.beambin25084 -> 25072 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/win32reg.beambin5120 -> 5072 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/zip.beambin24188 -> 22924 bytes
-rw-r--r--erts/configure.in24
-rw-r--r--erts/doc/src/crash_dump.xml25
-rw-r--r--erts/doc/src/erl_nif.xml51
-rw-r--r--erts/doc/src/erlang.xml37
-rw-r--r--erts/doc/src/erlc.xml65
-rw-r--r--erts/doc/src/notes.xml361
-rw-r--r--erts/doc/src/socket.xml79
-rw-r--r--erts/doc/src/socket_usage.xml23
-rw-r--r--erts/emulator/Makefile.in9
-rw-r--r--erts/emulator/beam/atom.names3
-rw-r--r--erts/emulator/beam/beam_bif_load.c143
-rw-r--r--erts/emulator/beam/beam_bp.c393
-rw-r--r--erts/emulator/beam/beam_bp.h16
-rw-r--r--erts/emulator/beam/beam_debug.c2
-rw-r--r--erts/emulator/beam/beam_emu.c562
-rw-r--r--erts/emulator/beam/beam_load.c235
-rw-r--r--erts/emulator/beam/beam_load.h2
-rw-r--r--erts/emulator/beam/bif.c92
-rw-r--r--erts/emulator/beam/bif.tab33
-rw-r--r--erts/emulator/beam/bif_instrs.tab150
-rw-r--r--erts/emulator/beam/binary.c80
-rw-r--r--erts/emulator/beam/bs_instrs.tab23
-rw-r--r--erts/emulator/beam/copy.c49
-rw-r--r--erts/emulator/beam/dist.c28
-rw-r--r--erts/emulator/beam/dist.h16
-rw-r--r--erts/emulator/beam/erl_alloc.c6
-rw-r--r--erts/emulator/beam/erl_alloc.types4
-rw-r--r--erts/emulator/beam/erl_alloc_util.c69
-rw-r--r--erts/emulator/beam/erl_bif_binary.c184
-rw-r--r--erts/emulator/beam/erl_bif_info.c97
-rw-r--r--erts/emulator/beam/erl_bif_lists.c68
-rw-r--r--erts/emulator/beam/erl_bif_persistent.c101
-rw-r--r--erts/emulator/beam/erl_bif_port.c1
-rw-r--r--erts/emulator/beam/erl_bif_trace.c178
-rw-r--r--erts/emulator/beam/erl_binary.h119
-rw-r--r--erts/emulator/beam/erl_bits.c95
-rw-r--r--erts/emulator/beam/erl_bits.h18
-rw-r--r--erts/emulator/beam/erl_db.c107
-rw-r--r--erts/emulator/beam/erl_db.h4
-rw-r--r--erts/emulator/beam/erl_db_catree.c328
-rw-r--r--erts/emulator/beam/erl_db_catree.h7
-rw-r--r--erts/emulator/beam/erl_db_hash.c146
-rw-r--r--erts/emulator/beam/erl_db_hash.h3
-rw-r--r--erts/emulator/beam/erl_db_tree.c55
-rw-r--r--erts/emulator/beam/erl_db_tree.h4
-rw-r--r--erts/emulator/beam/erl_db_tree_util.h4
-rw-r--r--erts/emulator/beam/erl_db_util.c25
-rw-r--r--erts/emulator/beam/erl_db_util.h37
-rw-r--r--erts/emulator/beam/erl_fun.c91
-rw-r--r--erts/emulator/beam/erl_gc.c31
-rw-r--r--erts/emulator/beam/erl_init.c1
-rw-r--r--erts/emulator/beam/erl_monitor_link.c49
-rw-r--r--erts/emulator/beam/erl_monitor_link.h17
-rw-r--r--erts/emulator/beam/erl_nfunc_sched.c89
-rw-r--r--erts/emulator/beam/erl_nfunc_sched.h144
-rw-r--r--erts/emulator/beam/erl_nif.c163
-rw-r--r--erts/emulator/beam/erl_node_tables.c249
-rw-r--r--erts/emulator/beam/erl_node_tables.h39
-rw-r--r--erts/emulator/beam/erl_printf_term.c29
-rw-r--r--erts/emulator/beam/erl_proc_sig_queue.c8
-rw-r--r--erts/emulator/beam/erl_process.c128
-rw-r--r--erts/emulator/beam/erl_process.h25
-rw-r--r--erts/emulator/beam/erl_trace.c62
-rw-r--r--erts/emulator/beam/erl_trace.h14
-rw-r--r--erts/emulator/beam/erl_unicode.c34
-rw-r--r--erts/emulator/beam/error.h14
-rw-r--r--erts/emulator/beam/export.c66
-rw-r--r--erts/emulator/beam/export.h83
-rw-r--r--erts/emulator/beam/external.c4
-rw-r--r--erts/emulator/beam/global.h54
-rw-r--r--erts/emulator/beam/hash.c107
-rw-r--r--erts/emulator/beam/hash.h66
-rw-r--r--erts/emulator/beam/index.c45
-rw-r--r--erts/emulator/beam/instrs.tab224
-rw-r--r--erts/emulator/beam/io.c100
-rw-r--r--erts/emulator/beam/macros.tab130
-rw-r--r--erts/emulator/beam/ops.tab177
-rw-r--r--erts/emulator/beam/register.c118
-rw-r--r--erts/emulator/beam/register.h1
-rw-r--r--erts/emulator/beam/sys.h11
-rw-r--r--erts/emulator/beam/trace_instrs.tab19
-rw-r--r--erts/emulator/hipe/hipe_debug.c1
-rw-r--r--erts/emulator/hipe/hipe_instrs.tab9
-rw-r--r--erts/emulator/hipe/hipe_mode_switch.c8
-rw-r--r--erts/emulator/hipe/hipe_x86_signal.c14
-rw-r--r--erts/emulator/internal_doc/beam_makeops.md20
-rw-r--r--erts/emulator/nifs/common/prim_file_nif.c53
-rw-r--r--erts/emulator/nifs/common/prim_file_nif.h1
-rw-r--r--erts/emulator/nifs/common/socket_nif.c5897
-rw-r--r--erts/emulator/nifs/common/socket_util.c18
-rw-r--r--erts/emulator/nifs/unix/unix_prim_file.c102
-rw-r--r--erts/emulator/nifs/win32/win_prim_file.c199
-rw-r--r--erts/emulator/sys/common/erl_osenv.h10
-rw-r--r--erts/emulator/sys/unix/erl_child_setup.c5
-rw-r--r--erts/emulator/sys/unix/sys_drivers.c27
-rw-r--r--erts/emulator/sys/win32/sys.c1
-rw-r--r--erts/emulator/sys/win32/sys_env.c1
-rw-r--r--erts/emulator/test/bif_SUITE.erl71
-rw-r--r--erts/emulator/test/binary_SUITE.erl17
-rw-r--r--erts/emulator/test/call_trace_SUITE.erl72
-rw-r--r--erts/emulator/test/dirty_bif_SUITE.erl4
-rwxr-xr-xerts/emulator/test/esock_ttest/esock-ttest4
-rwxr-xr-xerts/emulator/test/esock_ttest/esock-ttest-client29
-rwxr-xr-xerts/emulator/test/esock_ttest/esock-ttest-server-sock14
-rw-r--r--erts/emulator/test/fun_SUITE.erl10
-rw-r--r--erts/emulator/test/hibernate_SUITE.erl9
-rw-r--r--erts/emulator/test/lttng_SUITE.erl168
-rw-r--r--erts/emulator/test/match_spec_SUITE.erl3
-rw-r--r--erts/emulator/test/node_container_SUITE.erl44
-rw-r--r--erts/emulator/test/nofrag_SUITE.erl5
-rw-r--r--erts/emulator/test/socket_SUITE.erl2997
-rw-r--r--erts/emulator/test/socket_test_lib.erl201
-rw-r--r--erts/emulator/test/socket_test_ttest_tcp_client.erl5
-rw-r--r--erts/emulator/test/socket_test_ttest_tcp_client_socket.erl22
-rw-r--r--erts/emulator/test/socket_test_ttest_tcp_gen.erl26
-rw-r--r--erts/emulator/test/socket_test_ttest_tcp_server.erl104
-rw-r--r--erts/emulator/test/socket_test_ttest_tcp_socket.erl14
-rw-r--r--erts/emulator/test/trace_call_time_SUITE.erl70
-rwxr-xr-xerts/emulator/utils/beam_makeops6
-rwxr-xr-xerts/emulator/utils/make_tables74
-rw-r--r--erts/etc/common/Makefile.in12
-rw-r--r--erts/etc/common/erlc.c541
-rw-r--r--erts/etc/unix/etp-commands.in105
-rw-r--r--erts/lib_src/pthread/ethread.c4
-rw-r--r--erts/preloaded/ebin/erlang.beambin100200 -> 103624 bytes
-rw-r--r--erts/preloaded/ebin/erts_internal.beambin17760 -> 20156 bytes
-rw-r--r--erts/preloaded/ebin/prim_file.beambin27984 -> 28780 bytes
-rw-r--r--erts/preloaded/ebin/prim_inet.beambin81424 -> 81508 bytes
-rw-r--r--erts/preloaded/ebin/socket.beambin76064 -> 75892 bytes
-rw-r--r--erts/preloaded/src/Makefile10
-rw-r--r--erts/preloaded/src/erlang.erl141
-rw-r--r--erts/preloaded/src/erts_internal.erl71
-rw-r--r--erts/preloaded/src/prim_file.erl38
-rw-r--r--erts/preloaded/src/prim_inet.erl2
-rw-r--r--erts/preloaded/src/socket.erl77
-rw-r--r--erts/test/erlc_SUITE.erl23
-rw-r--r--erts/test/otp_SUITE.erl11
-rw-r--r--erts/vsn.mk2
-rw-r--r--lib/asn1/Makefile7
-rw-r--r--lib/common_test/Makefile3
-rw-r--r--lib/common_test/doc/src/ct_netconfc.xml376
-rw-r--r--lib/common_test/doc/src/notes.xml58
-rw-r--r--lib/common_test/src/Makefile3
-rw-r--r--lib/common_test/src/common_test.app.src4
-rw-r--r--lib/common_test/src/ct_event.erl12
-rw-r--r--lib/common_test/src/ct_framework.erl3
-rw-r--r--lib/common_test/src/ct_netconfc.erl1564
-rw-r--r--lib/common_test/src/ct_property_test.erl42
-rw-r--r--lib/common_test/src/ct_run.erl91
-rw-r--r--lib/common_test/src/ct_util.erl7
-rw-r--r--lib/common_test/src/ct_webtool.erl1214
-rw-r--r--lib/common_test/src/ct_webtool_sup.erl76
-rw-r--r--lib/common_test/src/vts.erl927
-rw-r--r--lib/common_test/test/ct_test_support.erl13
-rw-r--r--lib/common_test/vsn.mk2
-rw-r--r--lib/compiler/Makefile2
-rw-r--r--lib/compiler/doc/src/notes.xml93
-rw-r--r--lib/compiler/src/beam_call_types.erl63
-rw-r--r--lib/compiler/src/beam_jump.erl2
-rw-r--r--lib/compiler/src/beam_kernel_to_ssa.erl9
-rw-r--r--lib/compiler/src/beam_ssa_codegen.erl18
-rw-r--r--lib/compiler/src/beam_ssa_dead.erl87
-rw-r--r--lib/compiler/src/beam_ssa_lint.erl198
-rw-r--r--lib/compiler/src/beam_ssa_opt.erl18
-rw-r--r--lib/compiler/src/beam_ssa_pre_codegen.erl179
-rw-r--r--lib/compiler/src/beam_ssa_share.erl8
-rw-r--r--lib/compiler/src/beam_ssa_type.erl409
-rw-r--r--lib/compiler/src/beam_validator.erl728
-rw-r--r--lib/compiler/src/cerl_sets.erl65
-rw-r--r--lib/compiler/src/compile.erl7
-rw-r--r--lib/compiler/src/erl_bifs.erl2
-rw-r--r--lib/compiler/src/v3_kernel.erl228
-rw-r--r--lib/compiler/test/Makefile15
-rw-r--r--lib/compiler/test/beam_except_SUITE.erl14
-rw-r--r--lib/compiler/test/beam_ssa_SUITE.erl135
-rw-r--r--lib/compiler/test/beam_type_SUITE.erl25
-rw-r--r--lib/compiler/test/beam_validator_SUITE.erl87
-rw-r--r--lib/compiler/test/beam_validator_SUITE_data/branch_to_try_handler.S48
-rw-r--r--lib/compiler/test/beam_validator_SUITE_data/call_last.S21
-rw-r--r--lib/compiler/test/beam_validator_SUITE_data/call_without_stack.S21
-rw-r--r--lib/compiler/test/beam_validator_SUITE_data/freg_uninit.S2
-rw-r--r--lib/compiler/test/beam_validator_SUITE_data/merge_undefined.S77
-rw-r--r--lib/compiler/test/bs_match_SUITE.erl162
-rw-r--r--lib/compiler/test/misc_SUITE.erl24
-rw-r--r--lib/compiler/test/receive_SUITE.erl35
-rw-r--r--lib/compiler/test/test_lib.erl3
-rw-r--r--lib/compiler/vsn.mk2
-rw-r--r--lib/crypto/c_src/atoms.c4
-rw-r--r--lib/crypto/c_src/atoms.h2
-rw-r--r--lib/crypto/c_src/crypto.c2
-rw-r--r--lib/crypto/c_src/evp.c27
-rw-r--r--lib/crypto/doc/src/crypto.xml18
-rw-r--r--lib/crypto/doc/src/notes.xml58
-rw-r--r--lib/crypto/src/crypto.erl22
-rw-r--r--lib/crypto/test/crypto_SUITE.erl36
-rw-r--r--lib/crypto/vsn.mk2
-rw-r--r--lib/debugger/Makefile2
-rw-r--r--lib/dialyzer/Makefile2
-rw-r--r--lib/dialyzer/doc/src/notes.xml17
-rw-r--r--lib/dialyzer/vsn.mk2
-rw-r--r--lib/diameter/Makefile5
-rw-r--r--lib/edoc/Makefile6
-rw-r--r--lib/edoc/src/edoc.app.src2
-rw-r--r--lib/edoc/src/edoc.erl7
-rw-r--r--lib/edoc/src/edoc_lib.erl140
-rw-r--r--lib/eldap/Makefile2
-rw-r--r--lib/eldap/doc/src/eldap.xml2
-rw-r--r--lib/erl_docgen/Makefile1
-rw-r--r--lib/erl_docgen/doc/src/notes.xml17
-rw-r--r--lib/erl_docgen/priv/xsl/db_pdf.xsl6
-rw-r--r--lib/erl_docgen/vsn.mk2
-rw-r--r--lib/erl_interface/doc/src/Makefile2
-rw-r--r--lib/erl_interface/doc/src/notes.xml35
-rw-r--r--lib/erl_interface/src/Makefile2
-rw-r--r--lib/erl_interface/src/decode/decode_fun.c5
-rw-r--r--lib/erl_interface/vsn.mk2
-rw-r--r--lib/et/Makefile2
-rw-r--r--lib/eunit/Makefile8
-rw-r--r--lib/eunit/doc/src/notes.xml15
-rw-r--r--lib/eunit/src/eunit_proc.erl2
-rw-r--r--lib/eunit/src/eunit_surefire.erl5
-rw-r--r--lib/eunit/test/Makefile1
-rw-r--r--lib/eunit/test/eunit_SUITE.erl23
-rw-r--r--lib/eunit/test/tc0.erl14
-rw-r--r--lib/eunit/vsn.mk2
-rw-r--r--lib/ftp/Makefile40
-rw-r--r--lib/ftp/doc/src/notes.xml18
-rw-r--r--lib/ftp/src/ftp.erl7
-rw-r--r--lib/ftp/vsn.mk2
-rw-r--r--lib/hipe/Makefile2
-rw-r--r--lib/inets/Makefile40
-rw-r--r--lib/inets/doc/src/httpc.xml16
-rw-r--r--lib/inets/doc/src/notes.xml44
-rw-r--r--lib/inets/src/http_server/mod_esi.erl22
-rw-r--r--lib/inets/test/httpd_SUITE.erl28
-rw-r--r--lib/inets/vsn.mk2
-rw-r--r--lib/jinterface/doc/src/notes.xml16
-rw-r--r--lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java8
-rw-r--r--lib/jinterface/vsn.mk2
-rw-r--r--lib/kernel/doc/src/file.xml13
-rw-r--r--lib/kernel/doc/src/gen_udp.xml9
-rw-r--r--lib/kernel/doc/src/inet.xml12
-rw-r--r--lib/kernel/doc/src/logger_disk_log_h.xml2
-rw-r--r--lib/kernel/doc/src/notes.xml113
-rw-r--r--lib/kernel/examples/gen_tcp_dist/src/gen_tcp_dist.erl17
-rw-r--r--lib/kernel/src/Makefile1
-rw-r--r--lib/kernel/src/erl_compile_server.erl253
-rw-r--r--lib/kernel/src/erts_debug.erl9
-rw-r--r--lib/kernel/src/file.erl24
-rw-r--r--lib/kernel/src/file_io_server.erl8
-rw-r--r--lib/kernel/src/group.erl8
-rw-r--r--lib/kernel/src/group_history.erl30
-rw-r--r--lib/kernel/src/kernel.app.src1
-rw-r--r--lib/kernel/src/kernel.appup.src6
-rw-r--r--lib/kernel/src/kernel.erl17
-rw-r--r--lib/kernel/src/logger.erl10
-rw-r--r--lib/kernel/src/logger_internal.hrl7
-rw-r--r--lib/kernel/src/logger_std_h.erl4
-rw-r--r--lib/kernel/src/net_kernel.erl98
-rw-r--r--lib/kernel/src/raw_file_io_compressed.erl7
-rw-r--r--lib/kernel/src/raw_file_io_delayed.erl7
-rw-r--r--lib/kernel/src/raw_file_io_list.erl7
-rw-r--r--lib/kernel/src/user.erl53
-rw-r--r--lib/kernel/test/file_SUITE.erl190
-rw-r--r--lib/kernel/test/gen_tcp_misc_SUITE.erl53
-rw-r--r--lib/kernel/test/global_SUITE.erl13
-rw-r--r--lib/kernel/test/interactive_shell_SUITE.erl88
-rw-r--r--lib/kernel/test/interactive_shell_SUITE_data/.gitignore1
-rw-r--r--lib/kernel/test/interactive_shell_SUITE_data/io_columns.erl6
-rw-r--r--lib/kernel/test/interactive_shell_SUITE_data/io_rows.erl6
-rw-r--r--lib/kernel/test/logger_SUITE.erl2
-rw-r--r--lib/kernel/vsn.mk2
-rw-r--r--lib/megaco/Makefile34
-rw-r--r--lib/megaco/doc/src/notes.xml19
-rw-r--r--lib/megaco/src/engine/megaco_monitor.erl2
-rw-r--r--lib/megaco/test/Makefile5
-rw-r--r--lib/megaco/test/megaco_SUITE.erl46
-rw-r--r--lib/megaco/test/megaco_actions_test.erl46
-rw-r--r--lib/megaco/test/megaco_app_test.erl17
-rw-r--r--lib/megaco/test/megaco_appup_test.erl25
-rw-r--r--lib/megaco/test/megaco_call_flow_test.erl94
-rw-r--r--lib/megaco/test/megaco_codec_test.erl15
-rw-r--r--lib/megaco/test/megaco_codec_test_lib.erl10
-rw-r--r--lib/megaco/test/megaco_config_test.erl99
-rw-r--r--lib/megaco/test/megaco_digit_map_test.erl74
-rw-r--r--lib/megaco/test/megaco_examples_test.erl15
-rw-r--r--lib/megaco/test/megaco_flex_test.erl17
-rw-r--r--lib/megaco/test/megaco_load_test.erl119
-rw-r--r--lib/megaco/test/megaco_mess_test.erl526
-rw-r--r--lib/megaco/test/megaco_mess_user_test.erl16
-rw-r--r--lib/megaco/test/megaco_mib_test.erl35
-rw-r--r--lib/megaco/test/megaco_mreq_test.erl68
-rw-r--r--lib/megaco/test/megaco_pending_limit_test.erl46
-rw-r--r--lib/megaco/test/megaco_segment_test.erl51
-rw-r--r--lib/megaco/test/megaco_tcp_test.erl141
-rw-r--r--lib/megaco/test/megaco_test_deliver.erl4
-rw-r--r--lib/megaco/test/megaco_test_generator.erl54
-rw-r--r--lib/megaco/test/megaco_test_generic_transport.erl20
-rw-r--r--lib/megaco/test/megaco_test_lib.erl127
-rw-r--r--lib/megaco/test/megaco_test_lib.hrl11
-rw-r--r--lib/megaco/test/megaco_test_megaco_generator.erl15
-rw-r--r--lib/megaco/test/megaco_test_mg.erl96
-rw-r--r--lib/megaco/test/megaco_test_mgc.erl103
-rw-r--r--lib/megaco/test/megaco_timer_test.erl15
-rw-r--r--lib/megaco/test/megaco_trans_test.erl35
-rw-r--r--lib/megaco/test/megaco_udp_test.erl17
-rw-r--r--lib/megaco/vsn.mk2
-rw-r--r--lib/mnesia/doc/src/Mnesia_chap7.xmlsrc2
-rw-r--r--lib/mnesia/doc/src/notes.xml31
-rw-r--r--lib/mnesia/src/mnesia_controller.erl81
-rw-r--r--lib/mnesia/src/mnesia_locker.erl2
-rw-r--r--lib/mnesia/src/mnesia_monitor.erl6
-rw-r--r--lib/mnesia/src/mnesia_schema.erl70
-rw-r--r--lib/mnesia/src/mnesia_text.erl4
-rw-r--r--lib/mnesia/src/mnesia_tm.erl99
-rw-r--r--lib/mnesia/test/mnesia_isolation_test.erl91
-rwxr-xr-xlib/mnesia/test/mt38
-rw-r--r--lib/mnesia/vsn.mk2
-rw-r--r--lib/observer/Makefile1
-rw-r--r--lib/observer/doc/src/notes.xml28
-rw-r--r--lib/observer/src/cdv_bin_cb.erl37
-rw-r--r--lib/observer/src/cdv_html_wx.erl15
-rw-r--r--lib/observer/src/cdv_mod_cb.erl3
-rw-r--r--lib/observer/src/cdv_persistent_cb.erl5
-rw-r--r--lib/observer/src/cdv_proc_cb.erl2
-rw-r--r--lib/observer/src/cdv_term_cb.erl23
-rw-r--r--lib/observer/src/cdv_virtual_list_wx.erl9
-rw-r--r--lib/observer/src/cdv_wx.erl3
-rw-r--r--lib/observer/src/observer_app_wx.erl30
-rw-r--r--lib/observer/src/observer_defs.hrl1
-rw-r--r--lib/observer/src/observer_html_lib.erl76
-rw-r--r--lib/observer/src/observer_lib.erl60
-rw-r--r--lib/observer/src/observer_perf_wx.erl30
-rw-r--r--lib/observer/src/observer_port_wx.erl14
-rw-r--r--lib/observer/src/observer_pro_wx.erl2
-rw-r--r--lib/observer/src/observer_procinfo.erl19
-rw-r--r--lib/observer/src/observer_tv_table.erl5
-rw-r--r--lib/observer/src/observer_tv_wx.erl2
-rw-r--r--lib/observer/test/crashdump_helper.erl6
-rw-r--r--lib/observer/vsn.mk2
-rw-r--r--lib/odbc/Makefile6
-rw-r--r--lib/os_mon/c_src/cpu_sup.c2
-rw-r--r--lib/os_mon/doc/src/notes.xml23
-rw-r--r--lib/os_mon/vsn.mk2
-rw-r--r--lib/parsetools/doc/src/leex.xml6
-rw-r--r--lib/public_key/Makefile2
-rw-r--r--lib/public_key/doc/src/notes.xml27
-rw-r--r--lib/public_key/src/pubkey_cert.erl2
-rw-r--r--lib/public_key/vsn.mk2
-rw-r--r--lib/reltool/Makefile2
-rw-r--r--lib/runtime_tools/Makefile1
-rw-r--r--lib/runtime_tools/doc/src/notes.xml16
-rw-r--r--lib/runtime_tools/src/dbg.erl2
-rw-r--r--lib/runtime_tools/test/dbg_SUITE.erl46
-rw-r--r--lib/runtime_tools/vsn.mk2
-rw-r--r--lib/sasl/Makefile2
-rw-r--r--lib/sasl/doc/src/notes.xml16
-rw-r--r--lib/sasl/src/sasl.appup.src8
-rw-r--r--lib/sasl/src/systools_lib.erl6
-rw-r--r--lib/sasl/test/installer.erl2
-rw-r--r--lib/sasl/test/release_handler_SUITE.erl2
-rw-r--r--lib/sasl/test/rh_test_lib.erl2
-rw-r--r--lib/sasl/vsn.mk2
-rw-r--r--lib/snmp/Makefile46
-rw-r--r--lib/snmp/doc/src/notes.xml57
-rw-r--r--lib/snmp/src/agent/snmp_generic.erl8
-rw-r--r--lib/snmp/src/agent/snmpa_general_db.erl12
-rw-r--r--lib/snmp/src/agent/snmpa_supervisor.erl35
-rw-r--r--lib/snmp/src/compile/snmpc_misc.erl5
-rw-r--r--lib/snmp/src/manager/snmpm.erl11
-rw-r--r--lib/snmp/src/manager/snmpm_supervisor.erl40
-rw-r--r--lib/snmp/src/misc/snmp_conf.erl3
-rw-r--r--lib/snmp/src/misc/snmp_config.erl3
-rw-r--r--lib/snmp/test/snmp_agent_test.erl71
-rw-r--r--lib/snmp/test/snmp_agent_test_lib.erl13
-rw-r--r--lib/snmp/test/snmp_manager_test.erl1364
-rw-r--r--lib/snmp/test/snmp_test_lib.erl128
-rw-r--r--lib/snmp/test/snmp_test_lib.hrl10
-rw-r--r--lib/snmp/test/snmp_test_mgr.erl320
-rw-r--r--lib/snmp/vsn.mk2
-rw-r--r--lib/ssh/Makefile1
-rw-r--r--lib/ssh/doc/src/notes.xml96
-rw-r--r--lib/ssh/doc/src/ssh.xml46
-rw-r--r--lib/ssh/doc/src/ssh_client_channel.xml14
-rw-r--r--lib/ssh/doc/src/ssh_connection.xml446
-rw-r--r--lib/ssh/doc/src/ssh_server_channel.xml8
-rw-r--r--lib/ssh/doc/src/ssh_sftp.xml468
-rw-r--r--lib/ssh/doc/src/ssh_sftpd.xml33
-rw-r--r--lib/ssh/doc/src/using_ssh.xml14
-rw-r--r--lib/ssh/src/Makefile4
-rw-r--r--lib/ssh/src/ssh.app.src2
-rw-r--r--lib/ssh/src/ssh.erl184
-rw-r--r--lib/ssh/src/ssh_channel_sup.erl (renamed from lib/ssh/src/ssh_server_channel_sup.erl)40
-rw-r--r--lib/ssh/src/ssh_client_channel.erl2
-rw-r--r--lib/ssh/src/ssh_connect.hrl3
-rw-r--r--lib/ssh/src/ssh_connection.erl267
-rw-r--r--lib/ssh/src/ssh_connection_handler.erl286
-rw-r--r--lib/ssh/src/ssh_info.erl35
-rw-r--r--lib/ssh/src/ssh_options.erl282
-rw-r--r--lib/ssh/src/ssh_server_channel.erl2
-rw-r--r--lib/ssh/src/ssh_sftp.erl391
-rw-r--r--lib/ssh/src/ssh_sftpd.erl12
-rw-r--r--lib/ssh/src/ssh_subsystem_sup.erl29
-rw-r--r--lib/ssh/src/ssh_system_sup.erl45
-rw-r--r--lib/ssh/src/sshc_sup.erl46
-rw-r--r--lib/ssh/src/sshd_sup.erl2
-rw-r--r--lib/ssh/test/ssh_sup_SUITE.erl73
-rw-r--r--lib/ssh/vsn.mk2
-rw-r--r--lib/ssl/Makefile2
-rw-r--r--lib/ssl/doc/src/notes.xml85
-rw-r--r--lib/ssl/doc/src/ssl.xml23
-rw-r--r--lib/ssl/src/dtls_connection.erl56
-rw-r--r--lib/ssl/src/dtls_handshake.erl32
-rw-r--r--lib/ssl/src/dtls_record.erl20
-rw-r--r--lib/ssl/src/ssl.erl448
-rw-r--r--lib/ssl/src/ssl_cipher.erl30
-rw-r--r--lib/ssl/src/ssl_config.erl26
-rw-r--r--lib/ssl/src/ssl_connection.erl242
-rw-r--r--lib/ssl/src/ssl_connection.hrl2
-rw-r--r--lib/ssl/src/ssl_handshake.erl224
-rw-r--r--lib/ssl/src/ssl_internal.hrl2
-rw-r--r--lib/ssl/src/ssl_session.erl16
-rw-r--r--lib/ssl/src/tls_connection.erl63
-rw-r--r--lib/ssl/src/tls_handshake.erl42
-rw-r--r--lib/ssl/src/tls_handshake_1_3.erl146
-rw-r--r--lib/ssl/src/tls_handshake_1_3.hrl47
-rw-r--r--lib/ssl/src/tls_record.erl31
-rw-r--r--lib/ssl/src/tls_record_1_3.erl9
-rw-r--r--lib/ssl/src/tls_socket.erl4
-rw-r--r--lib/ssl/test/Makefile28
-rw-r--r--lib/ssl/test/openssl_alpn_SUITE.erl421
-rw-r--r--lib/ssl/test/openssl_cipher_suite_SUITE.erl (renamed from lib/ssl/test/openssl_server_cipher_suite_SUITE.erl)40
-rw-r--r--lib/ssl/test/openssl_client_cert_SUITE.erl350
-rw-r--r--lib/ssl/test/openssl_npn_SUITE.erl314
-rw-r--r--lib/ssl/test/openssl_reject_SUITE.erl209
-rw-r--r--lib/ssl/test/openssl_renegotiate_SUITE.erl334
-rw-r--r--lib/ssl/test/openssl_server_cert_SUITE.erl373
-rw-r--r--lib/ssl/test/openssl_session_SUITE.erl259
-rw-r--r--lib/ssl/test/openssl_sni_SUITE.erl251
-rw-r--r--lib/ssl/test/openssl_tls_1_3_version_SUITE.erl172
-rw-r--r--lib/ssl/test/property_test/ssl_eqc_handshake.erl105
-rw-r--r--lib/ssl/test/ssl_alert_SUITE.erl100
-rw-r--r--lib/ssl/test/ssl_alpn_SUITE.erl (renamed from lib/ssl/test/ssl_alpn_handshake_SUITE.erl)28
-rw-r--r--lib/ssl/test/ssl_api_SUITE.erl1978
-rw-r--r--lib/ssl/test/ssl_api_SUITE_data/dHParam.pem (renamed from lib/ssl/test/ssl_basic_SUITE_data/dHParam.pem)0
-rw-r--r--lib/ssl/test/ssl_app_env_SUITE.erl171
-rw-r--r--lib/ssl/test/ssl_basic_SUITE.erl6841
-rw-r--r--lib/ssl/test/ssl_bench_SUITE.erl7
-rw-r--r--lib/ssl/test/ssl_cert_SUITE.erl922
-rw-r--r--lib/ssl/test/ssl_cert_tests.erl386
-rw-r--r--lib/ssl/test/ssl_certificate_verify_SUITE.erl1212
-rw-r--r--lib/ssl/test/ssl_cipher_suite_SUITE.erl20
-rw-r--r--lib/ssl/test/ssl_dist_SUITE.erl4
-rw-r--r--lib/ssl/test/ssl_handshake_SUITE.erl4
-rw-r--r--lib/ssl/test/ssl_npn_SUITE.erl (renamed from lib/ssl/test/ssl_npn_handshake_SUITE.erl)2
-rw-r--r--lib/ssl/test/ssl_npn_hello_SUITE.erl21
-rw-r--r--lib/ssl/test/ssl_pem_cache_SUITE.erl55
-rw-r--r--lib/ssl/test/ssl_renegotiate_SUITE.erl499
-rw-r--r--lib/ssl/test/ssl_session_SUITE.erl377
-rw-r--r--lib/ssl/test/ssl_session_cache_SUITE.erl4
-rw-r--r--lib/ssl/test/ssl_sni_SUITE.erl68
-rw-r--r--lib/ssl/test/ssl_socket_SUITE.erl437
-rw-r--r--lib/ssl/test/ssl_test_lib.erl402
-rw-r--r--lib/ssl/test/ssl_to_openssl_SUITE.erl2021
-rw-r--r--lib/ssl/test/tls_1_3_record_SUITE.erl973
-rw-r--r--lib/ssl/test/tls_1_3_version_SUITE.erl153
-rw-r--r--lib/ssl/test/tls_api_SUITE.erl880
-rw-r--r--lib/ssl/vsn.mk2
-rw-r--r--lib/stdlib/Makefile2
-rw-r--r--lib/stdlib/doc/src/digraph.xml21
-rw-r--r--lib/stdlib/doc/src/ets.xml89
-rw-r--r--lib/stdlib/doc/src/gen_statem.xml183
-rw-r--r--lib/stdlib/doc/src/io_protocol.xml43
-rw-r--r--lib/stdlib/doc/src/notes.xml142
-rw-r--r--lib/stdlib/doc/src/sys.xml28
-rw-r--r--lib/stdlib/src/edlin.erl5
-rw-r--r--lib/stdlib/src/erl_compile.erl187
-rw-r--r--lib/stdlib/src/erl_internal.erl3
-rw-r--r--lib/stdlib/src/escript.erl29
-rw-r--r--lib/stdlib/src/ets.erl2
-rw-r--r--lib/stdlib/src/filelib.erl32
-rw-r--r--lib/stdlib/src/gen_statem.erl410
-rw-r--r--lib/stdlib/src/io.erl12
-rw-r--r--lib/stdlib/src/io_lib.erl29
-rw-r--r--lib/stdlib/src/io_lib_pretty.erl3
-rw-r--r--lib/stdlib/src/ms_transform.erl2
-rw-r--r--lib/stdlib/src/stdlib.app.src3
-rw-r--r--lib/stdlib/src/stdlib.appup.src6
-rw-r--r--lib/stdlib/src/sys.erl2
-rw-r--r--lib/stdlib/test/binary_module_SUITE.erl32
-rw-r--r--lib/stdlib/test/escript_SUITE.erl17
-rwxr-xr-xlib/stdlib/test/escript_SUITE_data/bad_io_server11
-rw-r--r--lib/stdlib/test/ets_SUITE.erl149
-rw-r--r--lib/stdlib/test/filelib_SUITE.erl34
-rw-r--r--lib/stdlib/test/gen_statem_SUITE.erl119
-rw-r--r--lib/stdlib/test/io_proto_SUITE.erl20
-rw-r--r--lib/stdlib/test/ms_transform_SUITE.erl4
-rw-r--r--lib/stdlib/test/rand_SUITE.erl17
-rw-r--r--lib/stdlib/test/shell_SUITE.erl9
-rw-r--r--lib/stdlib/test/supervisor_SUITE.erl6
-rw-r--r--lib/stdlib/test/unicode_util_SUITE_data/GraphemeBreakTest.txt78
-rw-r--r--lib/stdlib/test/unicode_util_SUITE_data/LineBreakTest.txt6
-rw-r--r--lib/stdlib/test/unicode_util_SUITE_data/NormalizationTest.txt34
-rw-r--r--lib/stdlib/uc_spec/CaseFolding.txt13
-rw-r--r--lib/stdlib/uc_spec/CompositionExclusions.txt6
-rw-r--r--lib/stdlib/uc_spec/GraphemeBreakProperty.txt39
-rw-r--r--lib/stdlib/uc_spec/PropList.txt77
-rw-r--r--lib/stdlib/uc_spec/README-UPDATE.txt10
-rw-r--r--lib/stdlib/uc_spec/SpecialCasing.txt6
-rw-r--r--lib/stdlib/uc_spec/UnicodeData.txt565
-rw-r--r--lib/stdlib/uc_spec/emoji-data.txt305
-rw-r--r--lib/stdlib/uc_spec/gen_unicode_mod.escript2
-rw-r--r--lib/stdlib/vsn.mk2
-rw-r--r--lib/syntax_tools/doc/src/notes.xml33
-rw-r--r--lib/syntax_tools/src/erl_syntax.erl48
-rw-r--r--lib/syntax_tools/test/syntax_tools_SUITE.erl38
-rw-r--r--lib/syntax_tools/test/syntax_tools_SUITE_data/type_specs.erl3
-rw-r--r--lib/syntax_tools/vsn.mk2
-rw-r--r--lib/tftp/Makefile34
-rw-r--r--lib/tools/Makefile2
-rw-r--r--lib/tools/doc/src/notes.xml15
-rw-r--r--lib/tools/vsn.mk2
-rw-r--r--lib/wx/doc/src/notes.xml16
-rw-r--r--lib/wx/vsn.mk2
-rw-r--r--lib/xmerl/Makefile7
-rw-r--r--lib/xmerl/doc/src/notes.xml17
-rw-r--r--lib/xmerl/vsn.mk2
-rw-r--r--make/app_targets.mk43
-rw-r--r--make/otp.mk.in2
-rw-r--r--make/otp_patch_solve_forward_merge_version2
-rw-r--r--make/otp_version_tickets_in_merge125
-rw-r--r--otp_versions.table4
-rwxr-xr-xscripts/build-otp3
-rw-r--r--system/doc/design_principles/statem.xml158
770 files changed, 34207 insertions, 26062 deletions
diff --git a/.travis.yml b/.travis.yml
index 00fe85fc04..51453639b0 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -5,6 +5,8 @@ sudo: false
os:
- linux
+dist: xenial
+
addons:
apt:
packages:
@@ -12,10 +14,8 @@ addons:
- libncurses-dev
- build-essential
- libssl-dev
- - libwxgtk2.8-dev
- - libgl1-mesa-dev
+ - libwxgtk3.0-dev
- libglu1-mesa-dev
- - libpng3
- default-jdk
- g++
- xsltproc
@@ -64,9 +64,7 @@ matrix:
- build-essential
- libssl-dev
- libwxgtk3.0-dev
- - libgl1-mesa-dev
- libglu1-mesa-dev
- - libpng3
- default-jdk
- g++
- xsltproc
@@ -80,7 +78,7 @@ matrix:
repo: erlang/cd
target-branch: master
skip-cleanup: true
- keep-history: true
+ keep-history: false
verbose: true
github-token: $ERLANG_CD_GITHUB_TOKEN
on:
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 8814e506f9..b754fbd662 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -30,7 +30,7 @@ By making a contribution to this project, I certify that:
Erlang/otp is licensed under the
Apache License 2.0
-As stated in: LICENSE.txt
+As stated in: [LICENSE.txt](LICENSE.txt)
http://developercertificate.org/
@@ -95,6 +95,9 @@ a discussion on the mailing list.
* Make sure existing test cases don't fail. It is not necessary to run all tests (that would take many hours),
but you should at least run the tests for the application you have changed.
See [Running tests](https://github.com/erlang/otp/wiki/Running-tests).
+* Make sure the documentation builds and is according to the dtd. eg. `make xmllint` or `cd lib/stdlib/ && make xmllint`
+* Make sure no new dialyzer warnings have been added. eg. `make dialyzer` or `cd lib/stdlib/ && make dialyzer`
+* Make sure that travis passes, if you go to https://travis-ci.org/$YOUR_GITHUB_USER/otp/ you can enable travis builds for you otp fork.
Make sure that your branch contains clean commits:
diff --git a/Makefile.in b/Makefile.in
index 3c4a6da85e..133ea0f4d6 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -338,7 +338,8 @@ endif
else
# Cross compiling
-all: cross_check_erl depend emulator libs start_scripts check_dev_rt_dep
+all: cross_check_erl depend build_erl_interface \
+ emulator libs start_scripts check_dev_rt_dep
endif
@@ -376,11 +377,17 @@ endif
# With all bootstraps we mean all bootstrapping that is done when
# the system is delivered in open source, the primary
# bootstrap is not included, it requires a pre built emulator...
-all_bootstraps: emulator \
+all_bootstraps: build_erl_interface emulator \
bootstrap_setup \
secondary_bootstrap_build secondary_bootstrap_copy \
tertiary_bootstrap_build tertiary_bootstrap_copy
+.PHONY: build_erl_interface
+
+build_erl_interface:
+ $(make_verbose)cd lib/erl_interface && \
+ ERL_TOP=$(ERL_TOP) PATH=$(BOOT_PREFIX)"$${PATH}" \
+ $(MAKE) $(TYPE)
#
# Use these targets when you want to use the erl and erlc
# binaries in your PATH instead of those created from the
@@ -1142,7 +1149,10 @@ bootstrap_clean:
# ----------------------------------------------------------------------
-.PHONY: test
+.PHONY: test dialyzer
test: all release release_tests
$(ERL_TOP)/make/test_target_script.sh $(ERL_TOP)
+
+dialyzer: all
+ $(ERL_TOP)/scripts/run-dialyzer
diff --git a/bootstrap/bin/no_dot_erlang.boot b/bootstrap/bin/no_dot_erlang.boot
index 1f7f088a77..26cd9e4d5e 100644
--- a/bootstrap/bin/no_dot_erlang.boot
+++ b/bootstrap/bin/no_dot_erlang.boot
Binary files differ
diff --git a/bootstrap/bin/start.boot b/bootstrap/bin/start.boot
index 1f7f088a77..26cd9e4d5e 100644
--- a/bootstrap/bin/start.boot
+++ b/bootstrap/bin/start.boot
Binary files differ
diff --git a/bootstrap/bin/start_clean.boot b/bootstrap/bin/start_clean.boot
index 1f7f088a77..26cd9e4d5e 100644
--- a/bootstrap/bin/start_clean.boot
+++ b/bootstrap/bin/start_clean.boot
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_a.beam b/bootstrap/lib/compiler/ebin/beam_a.beam
index ceb83ba48b..160855f0a1 100644
--- a/bootstrap/lib/compiler/ebin/beam_a.beam
+++ b/bootstrap/lib/compiler/ebin/beam_a.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_asm.beam b/bootstrap/lib/compiler/ebin/beam_asm.beam
index 07acbb1da7..41b50928be 100644
--- a/bootstrap/lib/compiler/ebin/beam_asm.beam
+++ b/bootstrap/lib/compiler/ebin/beam_asm.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_block.beam b/bootstrap/lib/compiler/ebin/beam_block.beam
index 1db56cb5f1..0faa2a981c 100644
--- a/bootstrap/lib/compiler/ebin/beam_block.beam
+++ b/bootstrap/lib/compiler/ebin/beam_block.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_call_types.beam b/bootstrap/lib/compiler/ebin/beam_call_types.beam
new file mode 100644
index 0000000000..e73383b4ed
--- /dev/null
+++ b/bootstrap/lib/compiler/ebin/beam_call_types.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_clean.beam b/bootstrap/lib/compiler/ebin/beam_clean.beam
index 4d567eca88..74a73324b9 100644
--- a/bootstrap/lib/compiler/ebin/beam_clean.beam
+++ b/bootstrap/lib/compiler/ebin/beam_clean.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_dict.beam b/bootstrap/lib/compiler/ebin/beam_dict.beam
index b9317a55ee..3dd6c8e6d7 100644
--- a/bootstrap/lib/compiler/ebin/beam_dict.beam
+++ b/bootstrap/lib/compiler/ebin/beam_dict.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_disasm.beam b/bootstrap/lib/compiler/ebin/beam_disasm.beam
index eb2a13d620..34619b82b9 100644
--- a/bootstrap/lib/compiler/ebin/beam_disasm.beam
+++ b/bootstrap/lib/compiler/ebin/beam_disasm.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_except.beam b/bootstrap/lib/compiler/ebin/beam_except.beam
deleted file mode 100644
index 1894483f71..0000000000
--- a/bootstrap/lib/compiler/ebin/beam_except.beam
+++ /dev/null
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_flatten.beam b/bootstrap/lib/compiler/ebin/beam_flatten.beam
index d7780861c2..8874d5d6e8 100644
--- a/bootstrap/lib/compiler/ebin/beam_flatten.beam
+++ b/bootstrap/lib/compiler/ebin/beam_flatten.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_jump.beam b/bootstrap/lib/compiler/ebin/beam_jump.beam
index 289c08630a..3419693975 100644
--- a/bootstrap/lib/compiler/ebin/beam_jump.beam
+++ b/bootstrap/lib/compiler/ebin/beam_jump.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_kernel_to_ssa.beam b/bootstrap/lib/compiler/ebin/beam_kernel_to_ssa.beam
index 96362c8cdd..2d239b9e40 100644
--- a/bootstrap/lib/compiler/ebin/beam_kernel_to_ssa.beam
+++ b/bootstrap/lib/compiler/ebin/beam_kernel_to_ssa.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_listing.beam b/bootstrap/lib/compiler/ebin/beam_listing.beam
index 097ba5e7ff..747ee9e685 100644
--- a/bootstrap/lib/compiler/ebin/beam_listing.beam
+++ b/bootstrap/lib/compiler/ebin/beam_listing.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_opcodes.beam b/bootstrap/lib/compiler/ebin/beam_opcodes.beam
index 52a23f7f6c..31a700c6e9 100644
--- a/bootstrap/lib/compiler/ebin/beam_opcodes.beam
+++ b/bootstrap/lib/compiler/ebin/beam_opcodes.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_peep.beam b/bootstrap/lib/compiler/ebin/beam_peep.beam
index 9246bd1c27..293a6bb264 100644
--- a/bootstrap/lib/compiler/ebin/beam_peep.beam
+++ b/bootstrap/lib/compiler/ebin/beam_peep.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_ssa.beam b/bootstrap/lib/compiler/ebin/beam_ssa.beam
index 9ba124a92d..7d59829998 100644
--- a/bootstrap/lib/compiler/ebin/beam_ssa.beam
+++ b/bootstrap/lib/compiler/ebin/beam_ssa.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_ssa_bsm.beam b/bootstrap/lib/compiler/ebin/beam_ssa_bsm.beam
index 50412bc8de..9674c3c352 100644
--- a/bootstrap/lib/compiler/ebin/beam_ssa_bsm.beam
+++ b/bootstrap/lib/compiler/ebin/beam_ssa_bsm.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_ssa_codegen.beam b/bootstrap/lib/compiler/ebin/beam_ssa_codegen.beam
index e59340de4f..7f2b8349fc 100644
--- a/bootstrap/lib/compiler/ebin/beam_ssa_codegen.beam
+++ b/bootstrap/lib/compiler/ebin/beam_ssa_codegen.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_ssa_dead.beam b/bootstrap/lib/compiler/ebin/beam_ssa_dead.beam
index f6bd1b7a69..3879c3c9d8 100644
--- a/bootstrap/lib/compiler/ebin/beam_ssa_dead.beam
+++ b/bootstrap/lib/compiler/ebin/beam_ssa_dead.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_ssa_funs.beam b/bootstrap/lib/compiler/ebin/beam_ssa_funs.beam
index 6370e1eb78..2efca72292 100644
--- a/bootstrap/lib/compiler/ebin/beam_ssa_funs.beam
+++ b/bootstrap/lib/compiler/ebin/beam_ssa_funs.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_ssa_lint.beam b/bootstrap/lib/compiler/ebin/beam_ssa_lint.beam
index e0a43057fc..d9f74f89dd 100644
--- a/bootstrap/lib/compiler/ebin/beam_ssa_lint.beam
+++ b/bootstrap/lib/compiler/ebin/beam_ssa_lint.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_ssa_opt.beam b/bootstrap/lib/compiler/ebin/beam_ssa_opt.beam
index c9838883ef..781820872b 100644
--- a/bootstrap/lib/compiler/ebin/beam_ssa_opt.beam
+++ b/bootstrap/lib/compiler/ebin/beam_ssa_opt.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_ssa_pp.beam b/bootstrap/lib/compiler/ebin/beam_ssa_pp.beam
index f76f15aa70..19bdaa1a9e 100644
--- a/bootstrap/lib/compiler/ebin/beam_ssa_pp.beam
+++ b/bootstrap/lib/compiler/ebin/beam_ssa_pp.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_ssa_pre_codegen.beam b/bootstrap/lib/compiler/ebin/beam_ssa_pre_codegen.beam
index 3de363dbb6..2d8301e71b 100644
--- a/bootstrap/lib/compiler/ebin/beam_ssa_pre_codegen.beam
+++ b/bootstrap/lib/compiler/ebin/beam_ssa_pre_codegen.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_ssa_recv.beam b/bootstrap/lib/compiler/ebin/beam_ssa_recv.beam
index 578e8db0eb..d67d6db761 100644
--- a/bootstrap/lib/compiler/ebin/beam_ssa_recv.beam
+++ b/bootstrap/lib/compiler/ebin/beam_ssa_recv.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_ssa_share.beam b/bootstrap/lib/compiler/ebin/beam_ssa_share.beam
index ea8e83d919..c59f105c65 100644
--- a/bootstrap/lib/compiler/ebin/beam_ssa_share.beam
+++ b/bootstrap/lib/compiler/ebin/beam_ssa_share.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_ssa_type.beam b/bootstrap/lib/compiler/ebin/beam_ssa_type.beam
index c13b25bac3..a52959ec5a 100644
--- a/bootstrap/lib/compiler/ebin/beam_ssa_type.beam
+++ b/bootstrap/lib/compiler/ebin/beam_ssa_type.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_trim.beam b/bootstrap/lib/compiler/ebin/beam_trim.beam
index 00f3224106..fefffbac73 100644
--- a/bootstrap/lib/compiler/ebin/beam_trim.beam
+++ b/bootstrap/lib/compiler/ebin/beam_trim.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_types.beam b/bootstrap/lib/compiler/ebin/beam_types.beam
new file mode 100644
index 0000000000..7658ed8dcd
--- /dev/null
+++ b/bootstrap/lib/compiler/ebin/beam_types.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_utils.beam b/bootstrap/lib/compiler/ebin/beam_utils.beam
index 996525efc9..c26a840398 100644
--- a/bootstrap/lib/compiler/ebin/beam_utils.beam
+++ b/bootstrap/lib/compiler/ebin/beam_utils.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_validator.beam b/bootstrap/lib/compiler/ebin/beam_validator.beam
index 9d0c34a94a..b7eef824d1 100644
--- a/bootstrap/lib/compiler/ebin/beam_validator.beam
+++ b/bootstrap/lib/compiler/ebin/beam_validator.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/beam_z.beam b/bootstrap/lib/compiler/ebin/beam_z.beam
index 21ca7bcf46..13a77364b8 100644
--- a/bootstrap/lib/compiler/ebin/beam_z.beam
+++ b/bootstrap/lib/compiler/ebin/beam_z.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/cerl.beam b/bootstrap/lib/compiler/ebin/cerl.beam
index 948c4759b0..ef121c4d2f 100644
--- a/bootstrap/lib/compiler/ebin/cerl.beam
+++ b/bootstrap/lib/compiler/ebin/cerl.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/cerl_clauses.beam b/bootstrap/lib/compiler/ebin/cerl_clauses.beam
index a20a4ca43b..09ce6fe0a8 100644
--- a/bootstrap/lib/compiler/ebin/cerl_clauses.beam
+++ b/bootstrap/lib/compiler/ebin/cerl_clauses.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/cerl_inline.beam b/bootstrap/lib/compiler/ebin/cerl_inline.beam
index 7bf92a0dd7..e109cf60cc 100644
--- a/bootstrap/lib/compiler/ebin/cerl_inline.beam
+++ b/bootstrap/lib/compiler/ebin/cerl_inline.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/cerl_sets.beam b/bootstrap/lib/compiler/ebin/cerl_sets.beam
index bda70130a6..58efd3f8e3 100644
--- a/bootstrap/lib/compiler/ebin/cerl_sets.beam
+++ b/bootstrap/lib/compiler/ebin/cerl_sets.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/cerl_trees.beam b/bootstrap/lib/compiler/ebin/cerl_trees.beam
index 831eddc405..c7efc37edf 100644
--- a/bootstrap/lib/compiler/ebin/cerl_trees.beam
+++ b/bootstrap/lib/compiler/ebin/cerl_trees.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/compile.beam b/bootstrap/lib/compiler/ebin/compile.beam
index 57cfc6b932..64fe613410 100644
--- a/bootstrap/lib/compiler/ebin/compile.beam
+++ b/bootstrap/lib/compiler/ebin/compile.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/compiler.app b/bootstrap/lib/compiler/ebin/compiler.app
index c0483e7801..4d6b9b484d 100644
--- a/bootstrap/lib/compiler/ebin/compiler.app
+++ b/bootstrap/lib/compiler/ebin/compiler.app
@@ -19,15 +19,15 @@
{application, compiler,
[{description, "ERTS CXC 138 10"},
- {vsn, "7.3.2"},
+ {vsn, "7.4.4"},
{modules, [
beam_a,
beam_asm,
beam_block,
+ beam_call_types,
beam_clean,
beam_dict,
beam_disasm,
- beam_except,
beam_flatten,
beam_jump,
beam_kernel_to_ssa,
@@ -47,6 +47,7 @@
beam_ssa_share,
beam_ssa_type,
beam_trim,
+ beam_types,
beam_utils,
beam_validator,
beam_z,
diff --git a/bootstrap/lib/compiler/ebin/core_lib.beam b/bootstrap/lib/compiler/ebin/core_lib.beam
index 19b0c3dfcb..2179dceeca 100644
--- a/bootstrap/lib/compiler/ebin/core_lib.beam
+++ b/bootstrap/lib/compiler/ebin/core_lib.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/core_lint.beam b/bootstrap/lib/compiler/ebin/core_lint.beam
index ecad4f33e0..76007eb4a3 100644
--- a/bootstrap/lib/compiler/ebin/core_lint.beam
+++ b/bootstrap/lib/compiler/ebin/core_lint.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/core_parse.beam b/bootstrap/lib/compiler/ebin/core_parse.beam
index f14357cf02..10c3f135e0 100644
--- a/bootstrap/lib/compiler/ebin/core_parse.beam
+++ b/bootstrap/lib/compiler/ebin/core_parse.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/core_pp.beam b/bootstrap/lib/compiler/ebin/core_pp.beam
index 2423c9b2ea..7ed91b8614 100644
--- a/bootstrap/lib/compiler/ebin/core_pp.beam
+++ b/bootstrap/lib/compiler/ebin/core_pp.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/core_scan.beam b/bootstrap/lib/compiler/ebin/core_scan.beam
index 8e44464cd7..4505c07159 100644
--- a/bootstrap/lib/compiler/ebin/core_scan.beam
+++ b/bootstrap/lib/compiler/ebin/core_scan.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/erl_bifs.beam b/bootstrap/lib/compiler/ebin/erl_bifs.beam
index e626e91f4f..32fc39a28b 100644
--- a/bootstrap/lib/compiler/ebin/erl_bifs.beam
+++ b/bootstrap/lib/compiler/ebin/erl_bifs.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/rec_env.beam b/bootstrap/lib/compiler/ebin/rec_env.beam
index 6d0c5baa04..a3e441e12b 100644
--- a/bootstrap/lib/compiler/ebin/rec_env.beam
+++ b/bootstrap/lib/compiler/ebin/rec_env.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/sys_core_alias.beam b/bootstrap/lib/compiler/ebin/sys_core_alias.beam
index 622221a8b0..0cd375f706 100644
--- a/bootstrap/lib/compiler/ebin/sys_core_alias.beam
+++ b/bootstrap/lib/compiler/ebin/sys_core_alias.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/sys_core_bsm.beam b/bootstrap/lib/compiler/ebin/sys_core_bsm.beam
index 6f4204e0a1..c92a95d2d6 100644
--- a/bootstrap/lib/compiler/ebin/sys_core_bsm.beam
+++ b/bootstrap/lib/compiler/ebin/sys_core_bsm.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/sys_core_fold.beam b/bootstrap/lib/compiler/ebin/sys_core_fold.beam
index d4746e3664..e65b66aa99 100644
--- a/bootstrap/lib/compiler/ebin/sys_core_fold.beam
+++ b/bootstrap/lib/compiler/ebin/sys_core_fold.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/sys_core_fold_lists.beam b/bootstrap/lib/compiler/ebin/sys_core_fold_lists.beam
index f4aac9ac13..fad3a34ad5 100644
--- a/bootstrap/lib/compiler/ebin/sys_core_fold_lists.beam
+++ b/bootstrap/lib/compiler/ebin/sys_core_fold_lists.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/sys_core_inline.beam b/bootstrap/lib/compiler/ebin/sys_core_inline.beam
index 5647c8fcb5..a5c1fd8784 100644
--- a/bootstrap/lib/compiler/ebin/sys_core_inline.beam
+++ b/bootstrap/lib/compiler/ebin/sys_core_inline.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/sys_pre_attributes.beam b/bootstrap/lib/compiler/ebin/sys_pre_attributes.beam
index 002b4f2914..96280ba9ec 100644
--- a/bootstrap/lib/compiler/ebin/sys_pre_attributes.beam
+++ b/bootstrap/lib/compiler/ebin/sys_pre_attributes.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/v3_core.beam b/bootstrap/lib/compiler/ebin/v3_core.beam
index 243b911ac6..214aacff2d 100644
--- a/bootstrap/lib/compiler/ebin/v3_core.beam
+++ b/bootstrap/lib/compiler/ebin/v3_core.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/v3_kernel.beam b/bootstrap/lib/compiler/ebin/v3_kernel.beam
index 6ce6777e55..1a7bf1455b 100644
--- a/bootstrap/lib/compiler/ebin/v3_kernel.beam
+++ b/bootstrap/lib/compiler/ebin/v3_kernel.beam
Binary files differ
diff --git a/bootstrap/lib/compiler/ebin/v3_kernel_pp.beam b/bootstrap/lib/compiler/ebin/v3_kernel_pp.beam
index 5177b65198..4bbc799593 100644
--- a/bootstrap/lib/compiler/ebin/v3_kernel_pp.beam
+++ b/bootstrap/lib/compiler/ebin/v3_kernel_pp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/application.beam b/bootstrap/lib/kernel/ebin/application.beam
index 84c5b7636e..c72d5c645f 100644
--- a/bootstrap/lib/kernel/ebin/application.beam
+++ b/bootstrap/lib/kernel/ebin/application.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/application_controller.beam b/bootstrap/lib/kernel/ebin/application_controller.beam
index b5d6545ca7..20a7077f9d 100644
--- a/bootstrap/lib/kernel/ebin/application_controller.beam
+++ b/bootstrap/lib/kernel/ebin/application_controller.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/application_master.beam b/bootstrap/lib/kernel/ebin/application_master.beam
index 77bb9544e2..5a840ea9c6 100644
--- a/bootstrap/lib/kernel/ebin/application_master.beam
+++ b/bootstrap/lib/kernel/ebin/application_master.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/application_starter.beam b/bootstrap/lib/kernel/ebin/application_starter.beam
index 0daab463ab..9abb2b8a0c 100644
--- a/bootstrap/lib/kernel/ebin/application_starter.beam
+++ b/bootstrap/lib/kernel/ebin/application_starter.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/auth.beam b/bootstrap/lib/kernel/ebin/auth.beam
index c77178bf7c..724d74bcdc 100644
--- a/bootstrap/lib/kernel/ebin/auth.beam
+++ b/bootstrap/lib/kernel/ebin/auth.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/code.beam b/bootstrap/lib/kernel/ebin/code.beam
index 41e4bd4ab2..2164f0d6fd 100644
--- a/bootstrap/lib/kernel/ebin/code.beam
+++ b/bootstrap/lib/kernel/ebin/code.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/code_server.beam b/bootstrap/lib/kernel/ebin/code_server.beam
index 637e40f5d8..2c9e8c08ea 100644
--- a/bootstrap/lib/kernel/ebin/code_server.beam
+++ b/bootstrap/lib/kernel/ebin/code_server.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/disk_log.beam b/bootstrap/lib/kernel/ebin/disk_log.beam
index 25142ee575..b17090c99a 100644
--- a/bootstrap/lib/kernel/ebin/disk_log.beam
+++ b/bootstrap/lib/kernel/ebin/disk_log.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/disk_log_1.beam b/bootstrap/lib/kernel/ebin/disk_log_1.beam
index 17f65d6ceb..ee39281793 100644
--- a/bootstrap/lib/kernel/ebin/disk_log_1.beam
+++ b/bootstrap/lib/kernel/ebin/disk_log_1.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/disk_log_server.beam b/bootstrap/lib/kernel/ebin/disk_log_server.beam
index 3b961dcefa..ca977a296e 100644
--- a/bootstrap/lib/kernel/ebin/disk_log_server.beam
+++ b/bootstrap/lib/kernel/ebin/disk_log_server.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/disk_log_sup.beam b/bootstrap/lib/kernel/ebin/disk_log_sup.beam
index 67099f7212..58ca3f887f 100644
--- a/bootstrap/lib/kernel/ebin/disk_log_sup.beam
+++ b/bootstrap/lib/kernel/ebin/disk_log_sup.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/dist_ac.beam b/bootstrap/lib/kernel/ebin/dist_ac.beam
index e82fc7dc75..77c3862368 100644
--- a/bootstrap/lib/kernel/ebin/dist_ac.beam
+++ b/bootstrap/lib/kernel/ebin/dist_ac.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/dist_util.beam b/bootstrap/lib/kernel/ebin/dist_util.beam
index 8fa128dd23..7f764d68bb 100644
--- a/bootstrap/lib/kernel/ebin/dist_util.beam
+++ b/bootstrap/lib/kernel/ebin/dist_util.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/erl_boot_server.beam b/bootstrap/lib/kernel/ebin/erl_boot_server.beam
index 801e3d8b88..5005621c01 100644
--- a/bootstrap/lib/kernel/ebin/erl_boot_server.beam
+++ b/bootstrap/lib/kernel/ebin/erl_boot_server.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/erl_compile_server.beam b/bootstrap/lib/kernel/ebin/erl_compile_server.beam
new file mode 100644
index 0000000000..43c77c4d7e
--- /dev/null
+++ b/bootstrap/lib/kernel/ebin/erl_compile_server.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/erl_ddll.beam b/bootstrap/lib/kernel/ebin/erl_ddll.beam
index c04bc43872..ec6900016a 100644
--- a/bootstrap/lib/kernel/ebin/erl_ddll.beam
+++ b/bootstrap/lib/kernel/ebin/erl_ddll.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/erl_distribution.beam b/bootstrap/lib/kernel/ebin/erl_distribution.beam
index 4490f9c81f..71f01fbf30 100644
--- a/bootstrap/lib/kernel/ebin/erl_distribution.beam
+++ b/bootstrap/lib/kernel/ebin/erl_distribution.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/erl_epmd.beam b/bootstrap/lib/kernel/ebin/erl_epmd.beam
index 6ed9862a0e..7d9d5cfba3 100644
--- a/bootstrap/lib/kernel/ebin/erl_epmd.beam
+++ b/bootstrap/lib/kernel/ebin/erl_epmd.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/erl_reply.beam b/bootstrap/lib/kernel/ebin/erl_reply.beam
index 7658a35642..e74194895d 100644
--- a/bootstrap/lib/kernel/ebin/erl_reply.beam
+++ b/bootstrap/lib/kernel/ebin/erl_reply.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/erl_signal_handler.beam b/bootstrap/lib/kernel/ebin/erl_signal_handler.beam
index da03769e2e..8876298e22 100644
--- a/bootstrap/lib/kernel/ebin/erl_signal_handler.beam
+++ b/bootstrap/lib/kernel/ebin/erl_signal_handler.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/error_handler.beam b/bootstrap/lib/kernel/ebin/error_handler.beam
index 19739b0043..2f1c9075cc 100644
--- a/bootstrap/lib/kernel/ebin/error_handler.beam
+++ b/bootstrap/lib/kernel/ebin/error_handler.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/error_logger.beam b/bootstrap/lib/kernel/ebin/error_logger.beam
index 10b13c35b6..a8694eedaa 100644
--- a/bootstrap/lib/kernel/ebin/error_logger.beam
+++ b/bootstrap/lib/kernel/ebin/error_logger.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/erts_debug.beam b/bootstrap/lib/kernel/ebin/erts_debug.beam
index e0b759eee3..f731417e46 100644
--- a/bootstrap/lib/kernel/ebin/erts_debug.beam
+++ b/bootstrap/lib/kernel/ebin/erts_debug.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/file.beam b/bootstrap/lib/kernel/ebin/file.beam
index a7a82a1f12..c151bcebbc 100644
--- a/bootstrap/lib/kernel/ebin/file.beam
+++ b/bootstrap/lib/kernel/ebin/file.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/file_io_server.beam b/bootstrap/lib/kernel/ebin/file_io_server.beam
index b8f1272be4..1300423dc3 100644
--- a/bootstrap/lib/kernel/ebin/file_io_server.beam
+++ b/bootstrap/lib/kernel/ebin/file_io_server.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/file_server.beam b/bootstrap/lib/kernel/ebin/file_server.beam
index cc2face79c..6f7ab15d43 100644
--- a/bootstrap/lib/kernel/ebin/file_server.beam
+++ b/bootstrap/lib/kernel/ebin/file_server.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/gen_sctp.beam b/bootstrap/lib/kernel/ebin/gen_sctp.beam
index 9fc5800f0c..610f20716c 100644
--- a/bootstrap/lib/kernel/ebin/gen_sctp.beam
+++ b/bootstrap/lib/kernel/ebin/gen_sctp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/gen_tcp.beam b/bootstrap/lib/kernel/ebin/gen_tcp.beam
index 736b28c68f..af3ad4bb74 100644
--- a/bootstrap/lib/kernel/ebin/gen_tcp.beam
+++ b/bootstrap/lib/kernel/ebin/gen_tcp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/gen_udp.beam b/bootstrap/lib/kernel/ebin/gen_udp.beam
index f6022dca19..1c75188e26 100644
--- a/bootstrap/lib/kernel/ebin/gen_udp.beam
+++ b/bootstrap/lib/kernel/ebin/gen_udp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/global.beam b/bootstrap/lib/kernel/ebin/global.beam
index aadb5bf080..d4e39916f8 100644
--- a/bootstrap/lib/kernel/ebin/global.beam
+++ b/bootstrap/lib/kernel/ebin/global.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/global_group.beam b/bootstrap/lib/kernel/ebin/global_group.beam
index 83885cdeb5..20c80de369 100644
--- a/bootstrap/lib/kernel/ebin/global_group.beam
+++ b/bootstrap/lib/kernel/ebin/global_group.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/global_search.beam b/bootstrap/lib/kernel/ebin/global_search.beam
index a94d9da454..e56ccd0f53 100644
--- a/bootstrap/lib/kernel/ebin/global_search.beam
+++ b/bootstrap/lib/kernel/ebin/global_search.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/group.beam b/bootstrap/lib/kernel/ebin/group.beam
index 396f1166f1..63a28baf7a 100644
--- a/bootstrap/lib/kernel/ebin/group.beam
+++ b/bootstrap/lib/kernel/ebin/group.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/group_history.beam b/bootstrap/lib/kernel/ebin/group_history.beam
index b9df7cfc14..456ed63c84 100644
--- a/bootstrap/lib/kernel/ebin/group_history.beam
+++ b/bootstrap/lib/kernel/ebin/group_history.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/heart.beam b/bootstrap/lib/kernel/ebin/heart.beam
index 1b63142893..058290ee4e 100644
--- a/bootstrap/lib/kernel/ebin/heart.beam
+++ b/bootstrap/lib/kernel/ebin/heart.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam b/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam
index 7ff507242b..35b9f544c0 100644
--- a/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam
+++ b/bootstrap/lib/kernel/ebin/hipe_unified_loader.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet.beam b/bootstrap/lib/kernel/ebin/inet.beam
index b0c67afe90..5ebc8d93fe 100644
--- a/bootstrap/lib/kernel/ebin/inet.beam
+++ b/bootstrap/lib/kernel/ebin/inet.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet6_sctp.beam b/bootstrap/lib/kernel/ebin/inet6_sctp.beam
index a6220a9d69..496fc8d777 100644
--- a/bootstrap/lib/kernel/ebin/inet6_sctp.beam
+++ b/bootstrap/lib/kernel/ebin/inet6_sctp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet6_tcp.beam b/bootstrap/lib/kernel/ebin/inet6_tcp.beam
index 7cacde3399..0d98803e2d 100644
--- a/bootstrap/lib/kernel/ebin/inet6_tcp.beam
+++ b/bootstrap/lib/kernel/ebin/inet6_tcp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet6_tcp_dist.beam b/bootstrap/lib/kernel/ebin/inet6_tcp_dist.beam
index d1ca0b4f0d..aa53ec19db 100644
--- a/bootstrap/lib/kernel/ebin/inet6_tcp_dist.beam
+++ b/bootstrap/lib/kernel/ebin/inet6_tcp_dist.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet6_udp.beam b/bootstrap/lib/kernel/ebin/inet6_udp.beam
index c12c6f4afc..fb41f9f6ab 100644
--- a/bootstrap/lib/kernel/ebin/inet6_udp.beam
+++ b/bootstrap/lib/kernel/ebin/inet6_udp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_config.beam b/bootstrap/lib/kernel/ebin/inet_config.beam
index c44b389b75..dde14ade38 100644
--- a/bootstrap/lib/kernel/ebin/inet_config.beam
+++ b/bootstrap/lib/kernel/ebin/inet_config.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_db.beam b/bootstrap/lib/kernel/ebin/inet_db.beam
index 20d795e75f..7b0604e3e8 100644
--- a/bootstrap/lib/kernel/ebin/inet_db.beam
+++ b/bootstrap/lib/kernel/ebin/inet_db.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_dns.beam b/bootstrap/lib/kernel/ebin/inet_dns.beam
index d88fa2185d..e2fb5beec5 100644
--- a/bootstrap/lib/kernel/ebin/inet_dns.beam
+++ b/bootstrap/lib/kernel/ebin/inet_dns.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_gethost_native.beam b/bootstrap/lib/kernel/ebin/inet_gethost_native.beam
index afc4999c42..3d367d492e 100644
--- a/bootstrap/lib/kernel/ebin/inet_gethost_native.beam
+++ b/bootstrap/lib/kernel/ebin/inet_gethost_native.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_hosts.beam b/bootstrap/lib/kernel/ebin/inet_hosts.beam
index 6797eecb29..6435b84f69 100644
--- a/bootstrap/lib/kernel/ebin/inet_hosts.beam
+++ b/bootstrap/lib/kernel/ebin/inet_hosts.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_parse.beam b/bootstrap/lib/kernel/ebin/inet_parse.beam
index 84c0169d4b..7da09a44c9 100644
--- a/bootstrap/lib/kernel/ebin/inet_parse.beam
+++ b/bootstrap/lib/kernel/ebin/inet_parse.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_res.beam b/bootstrap/lib/kernel/ebin/inet_res.beam
index c964152d79..0fd60cb47e 100644
--- a/bootstrap/lib/kernel/ebin/inet_res.beam
+++ b/bootstrap/lib/kernel/ebin/inet_res.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_sctp.beam b/bootstrap/lib/kernel/ebin/inet_sctp.beam
index 0e659bd738..fecea26319 100644
--- a/bootstrap/lib/kernel/ebin/inet_sctp.beam
+++ b/bootstrap/lib/kernel/ebin/inet_sctp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_tcp.beam b/bootstrap/lib/kernel/ebin/inet_tcp.beam
index c273d47012..f0eb540e4f 100644
--- a/bootstrap/lib/kernel/ebin/inet_tcp.beam
+++ b/bootstrap/lib/kernel/ebin/inet_tcp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_tcp_dist.beam b/bootstrap/lib/kernel/ebin/inet_tcp_dist.beam
index d09aa250c8..b9a84e75c7 100644
--- a/bootstrap/lib/kernel/ebin/inet_tcp_dist.beam
+++ b/bootstrap/lib/kernel/ebin/inet_tcp_dist.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/inet_udp.beam b/bootstrap/lib/kernel/ebin/inet_udp.beam
index 62156a3284..e29d3e7c95 100644
--- a/bootstrap/lib/kernel/ebin/inet_udp.beam
+++ b/bootstrap/lib/kernel/ebin/inet_udp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/kernel.app b/bootstrap/lib/kernel/ebin/kernel.app
index 5ec7c41dbe..7483d1bae3 100644
--- a/bootstrap/lib/kernel/ebin/kernel.app
+++ b/bootstrap/lib/kernel/ebin/kernel.app
@@ -22,7 +22,7 @@
{application, kernel,
[
{description, "ERTS CXC 138 10"},
- {vsn, "6.3.1"},
+ {vsn, "6.4.1"},
{modules, [application,
application_controller,
application_master,
@@ -32,6 +32,7 @@
code_server,
dist_util,
erl_boot_server,
+ erl_compile_server,
erl_distribution,
erl_reply,
erl_signal_handler,
@@ -74,6 +75,7 @@
logger_simple_h,
logger_std_h,
logger_sup,
+ net,
net_adm,
net_kernel,
os,
diff --git a/bootstrap/lib/kernel/ebin/kernel.beam b/bootstrap/lib/kernel/ebin/kernel.beam
index 11be83a49e..78c073ed5c 100644
--- a/bootstrap/lib/kernel/ebin/kernel.beam
+++ b/bootstrap/lib/kernel/ebin/kernel.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/kernel_config.beam b/bootstrap/lib/kernel/ebin/kernel_config.beam
index e749cc6c96..ba45bb95d1 100644
--- a/bootstrap/lib/kernel/ebin/kernel_config.beam
+++ b/bootstrap/lib/kernel/ebin/kernel_config.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/kernel_refc.beam b/bootstrap/lib/kernel/ebin/kernel_refc.beam
index 04306c6e2c..0e2db688ff 100644
--- a/bootstrap/lib/kernel/ebin/kernel_refc.beam
+++ b/bootstrap/lib/kernel/ebin/kernel_refc.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/local_tcp.beam b/bootstrap/lib/kernel/ebin/local_tcp.beam
index 2958d19524..3df208b31f 100644
--- a/bootstrap/lib/kernel/ebin/local_tcp.beam
+++ b/bootstrap/lib/kernel/ebin/local_tcp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/local_udp.beam b/bootstrap/lib/kernel/ebin/local_udp.beam
index cc5f46f746..b1efe6c251 100644
--- a/bootstrap/lib/kernel/ebin/local_udp.beam
+++ b/bootstrap/lib/kernel/ebin/local_udp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/logger.beam b/bootstrap/lib/kernel/ebin/logger.beam
index 1dc1c4fef4..0da88b6003 100644
--- a/bootstrap/lib/kernel/ebin/logger.beam
+++ b/bootstrap/lib/kernel/ebin/logger.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/logger_backend.beam b/bootstrap/lib/kernel/ebin/logger_backend.beam
index 40a3c4d80a..ddda136839 100644
--- a/bootstrap/lib/kernel/ebin/logger_backend.beam
+++ b/bootstrap/lib/kernel/ebin/logger_backend.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/logger_config.beam b/bootstrap/lib/kernel/ebin/logger_config.beam
index 51c7f78237..21661ea33d 100644
--- a/bootstrap/lib/kernel/ebin/logger_config.beam
+++ b/bootstrap/lib/kernel/ebin/logger_config.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/logger_disk_log_h.beam b/bootstrap/lib/kernel/ebin/logger_disk_log_h.beam
index dd0ca5f204..dafc128e2a 100644
--- a/bootstrap/lib/kernel/ebin/logger_disk_log_h.beam
+++ b/bootstrap/lib/kernel/ebin/logger_disk_log_h.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/logger_filters.beam b/bootstrap/lib/kernel/ebin/logger_filters.beam
index 2d8035278d..c34c1efa4a 100644
--- a/bootstrap/lib/kernel/ebin/logger_filters.beam
+++ b/bootstrap/lib/kernel/ebin/logger_filters.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/logger_formatter.beam b/bootstrap/lib/kernel/ebin/logger_formatter.beam
index e3e40460f0..ef791d2318 100644
--- a/bootstrap/lib/kernel/ebin/logger_formatter.beam
+++ b/bootstrap/lib/kernel/ebin/logger_formatter.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/logger_h_common.beam b/bootstrap/lib/kernel/ebin/logger_h_common.beam
index 9e8c25b6b9..0e1a36663b 100644
--- a/bootstrap/lib/kernel/ebin/logger_h_common.beam
+++ b/bootstrap/lib/kernel/ebin/logger_h_common.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/logger_handler_watcher.beam b/bootstrap/lib/kernel/ebin/logger_handler_watcher.beam
index fb20fd1818..7e5a439746 100644
--- a/bootstrap/lib/kernel/ebin/logger_handler_watcher.beam
+++ b/bootstrap/lib/kernel/ebin/logger_handler_watcher.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/logger_olp.beam b/bootstrap/lib/kernel/ebin/logger_olp.beam
index ed06b6ae5e..4b21635585 100644
--- a/bootstrap/lib/kernel/ebin/logger_olp.beam
+++ b/bootstrap/lib/kernel/ebin/logger_olp.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/logger_proxy.beam b/bootstrap/lib/kernel/ebin/logger_proxy.beam
index ed0ed0f56b..7f97031ab8 100644
--- a/bootstrap/lib/kernel/ebin/logger_proxy.beam
+++ b/bootstrap/lib/kernel/ebin/logger_proxy.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/logger_server.beam b/bootstrap/lib/kernel/ebin/logger_server.beam
index dcff9beac7..069cf723e6 100644
--- a/bootstrap/lib/kernel/ebin/logger_server.beam
+++ b/bootstrap/lib/kernel/ebin/logger_server.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/logger_simple_h.beam b/bootstrap/lib/kernel/ebin/logger_simple_h.beam
index 583c77b290..9ccd980af7 100644
--- a/bootstrap/lib/kernel/ebin/logger_simple_h.beam
+++ b/bootstrap/lib/kernel/ebin/logger_simple_h.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/logger_std_h.beam b/bootstrap/lib/kernel/ebin/logger_std_h.beam
index b0f56d9a62..614888589b 100644
--- a/bootstrap/lib/kernel/ebin/logger_std_h.beam
+++ b/bootstrap/lib/kernel/ebin/logger_std_h.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/logger_sup.beam b/bootstrap/lib/kernel/ebin/logger_sup.beam
index 60192ef4a1..98d52f7e93 100644
--- a/bootstrap/lib/kernel/ebin/logger_sup.beam
+++ b/bootstrap/lib/kernel/ebin/logger_sup.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/net.beam b/bootstrap/lib/kernel/ebin/net.beam
new file mode 100644
index 0000000000..a1c8d8b526
--- /dev/null
+++ b/bootstrap/lib/kernel/ebin/net.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/net_adm.beam b/bootstrap/lib/kernel/ebin/net_adm.beam
index 5810577c6a..4fb48aba84 100644
--- a/bootstrap/lib/kernel/ebin/net_adm.beam
+++ b/bootstrap/lib/kernel/ebin/net_adm.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/net_kernel.beam b/bootstrap/lib/kernel/ebin/net_kernel.beam
index 4c57f5f6c8..40c6ea85bd 100644
--- a/bootstrap/lib/kernel/ebin/net_kernel.beam
+++ b/bootstrap/lib/kernel/ebin/net_kernel.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/os.beam b/bootstrap/lib/kernel/ebin/os.beam
index f05007041b..41a45ca812 100644
--- a/bootstrap/lib/kernel/ebin/os.beam
+++ b/bootstrap/lib/kernel/ebin/os.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/pg2.beam b/bootstrap/lib/kernel/ebin/pg2.beam
index d293ea18f3..ce1c5270a9 100644
--- a/bootstrap/lib/kernel/ebin/pg2.beam
+++ b/bootstrap/lib/kernel/ebin/pg2.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/ram_file.beam b/bootstrap/lib/kernel/ebin/ram_file.beam
index 02c4c6454a..e00c4d3d34 100644
--- a/bootstrap/lib/kernel/ebin/ram_file.beam
+++ b/bootstrap/lib/kernel/ebin/ram_file.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/raw_file_io.beam b/bootstrap/lib/kernel/ebin/raw_file_io.beam
index 16dc314ba5..03c5935a42 100644
--- a/bootstrap/lib/kernel/ebin/raw_file_io.beam
+++ b/bootstrap/lib/kernel/ebin/raw_file_io.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/raw_file_io_compressed.beam b/bootstrap/lib/kernel/ebin/raw_file_io_compressed.beam
index 3c41f8528e..c3f9da47dc 100644
--- a/bootstrap/lib/kernel/ebin/raw_file_io_compressed.beam
+++ b/bootstrap/lib/kernel/ebin/raw_file_io_compressed.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/raw_file_io_deflate.beam b/bootstrap/lib/kernel/ebin/raw_file_io_deflate.beam
index 11b8f4a6db..dd095a4b0b 100644
--- a/bootstrap/lib/kernel/ebin/raw_file_io_deflate.beam
+++ b/bootstrap/lib/kernel/ebin/raw_file_io_deflate.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/raw_file_io_delayed.beam b/bootstrap/lib/kernel/ebin/raw_file_io_delayed.beam
index b3381cc397..97f7023c1d 100644
--- a/bootstrap/lib/kernel/ebin/raw_file_io_delayed.beam
+++ b/bootstrap/lib/kernel/ebin/raw_file_io_delayed.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/raw_file_io_inflate.beam b/bootstrap/lib/kernel/ebin/raw_file_io_inflate.beam
index 7c47f2cf10..6bc4a1e7a0 100644
--- a/bootstrap/lib/kernel/ebin/raw_file_io_inflate.beam
+++ b/bootstrap/lib/kernel/ebin/raw_file_io_inflate.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/raw_file_io_list.beam b/bootstrap/lib/kernel/ebin/raw_file_io_list.beam
index b544fd394e..eeafb97ea5 100644
--- a/bootstrap/lib/kernel/ebin/raw_file_io_list.beam
+++ b/bootstrap/lib/kernel/ebin/raw_file_io_list.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/raw_file_io_raw.beam b/bootstrap/lib/kernel/ebin/raw_file_io_raw.beam
index b47a67055d..fab63a6629 100644
--- a/bootstrap/lib/kernel/ebin/raw_file_io_raw.beam
+++ b/bootstrap/lib/kernel/ebin/raw_file_io_raw.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/rpc.beam b/bootstrap/lib/kernel/ebin/rpc.beam
index b53525e2ca..493223db2f 100644
--- a/bootstrap/lib/kernel/ebin/rpc.beam
+++ b/bootstrap/lib/kernel/ebin/rpc.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/seq_trace.beam b/bootstrap/lib/kernel/ebin/seq_trace.beam
index f81a39ddc0..fdd399092e 100644
--- a/bootstrap/lib/kernel/ebin/seq_trace.beam
+++ b/bootstrap/lib/kernel/ebin/seq_trace.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/standard_error.beam b/bootstrap/lib/kernel/ebin/standard_error.beam
index 18582d22f2..96dc4d5433 100644
--- a/bootstrap/lib/kernel/ebin/standard_error.beam
+++ b/bootstrap/lib/kernel/ebin/standard_error.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/user.beam b/bootstrap/lib/kernel/ebin/user.beam
index f3573d95af..59a7cdaf6d 100644
--- a/bootstrap/lib/kernel/ebin/user.beam
+++ b/bootstrap/lib/kernel/ebin/user.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/user_drv.beam b/bootstrap/lib/kernel/ebin/user_drv.beam
index 1b59423b71..0dab31f7d1 100644
--- a/bootstrap/lib/kernel/ebin/user_drv.beam
+++ b/bootstrap/lib/kernel/ebin/user_drv.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/user_sup.beam b/bootstrap/lib/kernel/ebin/user_sup.beam
index c82fca70da..68f3a330c2 100644
--- a/bootstrap/lib/kernel/ebin/user_sup.beam
+++ b/bootstrap/lib/kernel/ebin/user_sup.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/wrap_log_reader.beam b/bootstrap/lib/kernel/ebin/wrap_log_reader.beam
index ceb8dd9a78..7f719ee8a5 100644
--- a/bootstrap/lib/kernel/ebin/wrap_log_reader.beam
+++ b/bootstrap/lib/kernel/ebin/wrap_log_reader.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/array.beam b/bootstrap/lib/stdlib/ebin/array.beam
index 9e783452f5..c0a14da511 100644
--- a/bootstrap/lib/stdlib/ebin/array.beam
+++ b/bootstrap/lib/stdlib/ebin/array.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/base64.beam b/bootstrap/lib/stdlib/ebin/base64.beam
index 5872970da7..246bbc8fea 100644
--- a/bootstrap/lib/stdlib/ebin/base64.beam
+++ b/bootstrap/lib/stdlib/ebin/base64.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/beam_lib.beam b/bootstrap/lib/stdlib/ebin/beam_lib.beam
index a96ccb6f8c..e815fc298e 100644
--- a/bootstrap/lib/stdlib/ebin/beam_lib.beam
+++ b/bootstrap/lib/stdlib/ebin/beam_lib.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/binary.beam b/bootstrap/lib/stdlib/ebin/binary.beam
index fc1bbe163a..0712d0c64b 100644
--- a/bootstrap/lib/stdlib/ebin/binary.beam
+++ b/bootstrap/lib/stdlib/ebin/binary.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/c.beam b/bootstrap/lib/stdlib/ebin/c.beam
index c61132b2de..6cea2d1e9d 100644
--- a/bootstrap/lib/stdlib/ebin/c.beam
+++ b/bootstrap/lib/stdlib/ebin/c.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/calendar.beam b/bootstrap/lib/stdlib/ebin/calendar.beam
index 89102adda8..6c5773d852 100644
--- a/bootstrap/lib/stdlib/ebin/calendar.beam
+++ b/bootstrap/lib/stdlib/ebin/calendar.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/dets.beam b/bootstrap/lib/stdlib/ebin/dets.beam
index 2eadde21ff..4af2c67a93 100644
--- a/bootstrap/lib/stdlib/ebin/dets.beam
+++ b/bootstrap/lib/stdlib/ebin/dets.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/dets_server.beam b/bootstrap/lib/stdlib/ebin/dets_server.beam
index 8cd247e3be..ee1b586ef2 100644
--- a/bootstrap/lib/stdlib/ebin/dets_server.beam
+++ b/bootstrap/lib/stdlib/ebin/dets_server.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/dets_sup.beam b/bootstrap/lib/stdlib/ebin/dets_sup.beam
index 0b9fb6379f..b198d1f872 100644
--- a/bootstrap/lib/stdlib/ebin/dets_sup.beam
+++ b/bootstrap/lib/stdlib/ebin/dets_sup.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/dets_utils.beam b/bootstrap/lib/stdlib/ebin/dets_utils.beam
index e38eb384a1..31eeb4375c 100644
--- a/bootstrap/lib/stdlib/ebin/dets_utils.beam
+++ b/bootstrap/lib/stdlib/ebin/dets_utils.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/dets_v9.beam b/bootstrap/lib/stdlib/ebin/dets_v9.beam
index 046de3bd5a..67abb787dc 100644
--- a/bootstrap/lib/stdlib/ebin/dets_v9.beam
+++ b/bootstrap/lib/stdlib/ebin/dets_v9.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/dict.beam b/bootstrap/lib/stdlib/ebin/dict.beam
index dc390024a4..c3155b9129 100644
--- a/bootstrap/lib/stdlib/ebin/dict.beam
+++ b/bootstrap/lib/stdlib/ebin/dict.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/digraph.beam b/bootstrap/lib/stdlib/ebin/digraph.beam
index 60abe020fd..d584c51083 100644
--- a/bootstrap/lib/stdlib/ebin/digraph.beam
+++ b/bootstrap/lib/stdlib/ebin/digraph.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/digraph_utils.beam b/bootstrap/lib/stdlib/ebin/digraph_utils.beam
index 772332cd08..badb46d0af 100644
--- a/bootstrap/lib/stdlib/ebin/digraph_utils.beam
+++ b/bootstrap/lib/stdlib/ebin/digraph_utils.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/edlin.beam b/bootstrap/lib/stdlib/ebin/edlin.beam
index b6a7ecc9a5..473dcb6dc1 100644
--- a/bootstrap/lib/stdlib/ebin/edlin.beam
+++ b/bootstrap/lib/stdlib/ebin/edlin.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/edlin_expand.beam b/bootstrap/lib/stdlib/ebin/edlin_expand.beam
index 865066c3ac..f2572a212b 100644
--- a/bootstrap/lib/stdlib/ebin/edlin_expand.beam
+++ b/bootstrap/lib/stdlib/ebin/edlin_expand.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/epp.beam b/bootstrap/lib/stdlib/ebin/epp.beam
index e3d8c21edf..67c737dddd 100644
--- a/bootstrap/lib/stdlib/ebin/epp.beam
+++ b/bootstrap/lib/stdlib/ebin/epp.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_abstract_code.beam b/bootstrap/lib/stdlib/ebin/erl_abstract_code.beam
index 973ad9fb80..fefdd4f54a 100644
--- a/bootstrap/lib/stdlib/ebin/erl_abstract_code.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_abstract_code.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_anno.beam b/bootstrap/lib/stdlib/ebin/erl_anno.beam
index a40fcb3f92..c4117509dd 100644
--- a/bootstrap/lib/stdlib/ebin/erl_anno.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_anno.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_bits.beam b/bootstrap/lib/stdlib/ebin/erl_bits.beam
index 1beb0d5ebb..462b2677e1 100644
--- a/bootstrap/lib/stdlib/ebin/erl_bits.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_bits.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_compile.beam b/bootstrap/lib/stdlib/ebin/erl_compile.beam
index fa223657bf..ca55b187e8 100644
--- a/bootstrap/lib/stdlib/ebin/erl_compile.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_compile.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_error.beam b/bootstrap/lib/stdlib/ebin/erl_error.beam
index 5d1b91aeef..0c3275465d 100644
--- a/bootstrap/lib/stdlib/ebin/erl_error.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_error.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_eval.beam b/bootstrap/lib/stdlib/ebin/erl_eval.beam
index 90b3858207..4743c2a730 100644
--- a/bootstrap/lib/stdlib/ebin/erl_eval.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_eval.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_expand_records.beam b/bootstrap/lib/stdlib/ebin/erl_expand_records.beam
index e527f98776..7597146d22 100644
--- a/bootstrap/lib/stdlib/ebin/erl_expand_records.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_expand_records.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_internal.beam b/bootstrap/lib/stdlib/ebin/erl_internal.beam
index 9015ad8936..820ff0e779 100644
--- a/bootstrap/lib/stdlib/ebin/erl_internal.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_internal.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_lint.beam b/bootstrap/lib/stdlib/ebin/erl_lint.beam
index 62838ed771..d56a730da1 100644
--- a/bootstrap/lib/stdlib/ebin/erl_lint.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_lint.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_parse.beam b/bootstrap/lib/stdlib/ebin/erl_parse.beam
index 41369ca147..1c910f9f38 100644
--- a/bootstrap/lib/stdlib/ebin/erl_parse.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_parse.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_posix_msg.beam b/bootstrap/lib/stdlib/ebin/erl_posix_msg.beam
index 5f6c03bcae..5696cbc072 100644
--- a/bootstrap/lib/stdlib/ebin/erl_posix_msg.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_posix_msg.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_pp.beam b/bootstrap/lib/stdlib/ebin/erl_pp.beam
index 1965b9e656..1f6baa7653 100644
--- a/bootstrap/lib/stdlib/ebin/erl_pp.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_pp.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_scan.beam b/bootstrap/lib/stdlib/ebin/erl_scan.beam
index 3b30d3e8c7..eac7242485 100644
--- a/bootstrap/lib/stdlib/ebin/erl_scan.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_scan.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/erl_tar.beam b/bootstrap/lib/stdlib/ebin/erl_tar.beam
index 3c5004871d..dbffb05446 100644
--- a/bootstrap/lib/stdlib/ebin/erl_tar.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_tar.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/error_logger_file_h.beam b/bootstrap/lib/stdlib/ebin/error_logger_file_h.beam
index 64de26f7a4..ce2d0a95d2 100644
--- a/bootstrap/lib/stdlib/ebin/error_logger_file_h.beam
+++ b/bootstrap/lib/stdlib/ebin/error_logger_file_h.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/error_logger_tty_h.beam b/bootstrap/lib/stdlib/ebin/error_logger_tty_h.beam
index e7ee41cc2e..66258d0fb9 100644
--- a/bootstrap/lib/stdlib/ebin/error_logger_tty_h.beam
+++ b/bootstrap/lib/stdlib/ebin/error_logger_tty_h.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/escript.beam b/bootstrap/lib/stdlib/ebin/escript.beam
index e846ca420e..69e3b7dddc 100644
--- a/bootstrap/lib/stdlib/ebin/escript.beam
+++ b/bootstrap/lib/stdlib/ebin/escript.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/ets.beam b/bootstrap/lib/stdlib/ebin/ets.beam
index b1cf286cea..fbd823cc39 100644
--- a/bootstrap/lib/stdlib/ebin/ets.beam
+++ b/bootstrap/lib/stdlib/ebin/ets.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/eval_bits.beam b/bootstrap/lib/stdlib/ebin/eval_bits.beam
index 08f86f86ff..063393d5d8 100644
--- a/bootstrap/lib/stdlib/ebin/eval_bits.beam
+++ b/bootstrap/lib/stdlib/ebin/eval_bits.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/file_sorter.beam b/bootstrap/lib/stdlib/ebin/file_sorter.beam
index 0a42a40d7e..a2e8fef834 100644
--- a/bootstrap/lib/stdlib/ebin/file_sorter.beam
+++ b/bootstrap/lib/stdlib/ebin/file_sorter.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/filelib.beam b/bootstrap/lib/stdlib/ebin/filelib.beam
index 62b8c3588e..fe62f6636c 100644
--- a/bootstrap/lib/stdlib/ebin/filelib.beam
+++ b/bootstrap/lib/stdlib/ebin/filelib.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/filename.beam b/bootstrap/lib/stdlib/ebin/filename.beam
index d353a11f3e..1461e1cbc8 100644
--- a/bootstrap/lib/stdlib/ebin/filename.beam
+++ b/bootstrap/lib/stdlib/ebin/filename.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/gb_sets.beam b/bootstrap/lib/stdlib/ebin/gb_sets.beam
index 8d1e2c11e0..ae19fe9d10 100644
--- a/bootstrap/lib/stdlib/ebin/gb_sets.beam
+++ b/bootstrap/lib/stdlib/ebin/gb_sets.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/gb_trees.beam b/bootstrap/lib/stdlib/ebin/gb_trees.beam
index 0aae509cb6..49fd69632f 100644
--- a/bootstrap/lib/stdlib/ebin/gb_trees.beam
+++ b/bootstrap/lib/stdlib/ebin/gb_trees.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/gen.beam b/bootstrap/lib/stdlib/ebin/gen.beam
index 85a9c25618..92e4e2dab8 100644
--- a/bootstrap/lib/stdlib/ebin/gen.beam
+++ b/bootstrap/lib/stdlib/ebin/gen.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/gen_event.beam b/bootstrap/lib/stdlib/ebin/gen_event.beam
index f3376d2ee2..45200c3ddd 100644
--- a/bootstrap/lib/stdlib/ebin/gen_event.beam
+++ b/bootstrap/lib/stdlib/ebin/gen_event.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/gen_fsm.beam b/bootstrap/lib/stdlib/ebin/gen_fsm.beam
index 5c4a0aa3d5..8f17cee54d 100644
--- a/bootstrap/lib/stdlib/ebin/gen_fsm.beam
+++ b/bootstrap/lib/stdlib/ebin/gen_fsm.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/gen_server.beam b/bootstrap/lib/stdlib/ebin/gen_server.beam
index 06c3d6de19..fea013f8cf 100644
--- a/bootstrap/lib/stdlib/ebin/gen_server.beam
+++ b/bootstrap/lib/stdlib/ebin/gen_server.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/gen_statem.beam b/bootstrap/lib/stdlib/ebin/gen_statem.beam
index 0aab776d07..0d7aa92875 100644
--- a/bootstrap/lib/stdlib/ebin/gen_statem.beam
+++ b/bootstrap/lib/stdlib/ebin/gen_statem.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/io.beam b/bootstrap/lib/stdlib/ebin/io.beam
index 631863b587..005c89a1e3 100644
--- a/bootstrap/lib/stdlib/ebin/io.beam
+++ b/bootstrap/lib/stdlib/ebin/io.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/io_lib.beam b/bootstrap/lib/stdlib/ebin/io_lib.beam
index 45692978e4..47201dba0c 100644
--- a/bootstrap/lib/stdlib/ebin/io_lib.beam
+++ b/bootstrap/lib/stdlib/ebin/io_lib.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/io_lib_format.beam b/bootstrap/lib/stdlib/ebin/io_lib_format.beam
index 66047b5070..c2f8a11e40 100644
--- a/bootstrap/lib/stdlib/ebin/io_lib_format.beam
+++ b/bootstrap/lib/stdlib/ebin/io_lib_format.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/io_lib_fread.beam b/bootstrap/lib/stdlib/ebin/io_lib_fread.beam
index 22690ce685..f81265ebd9 100644
--- a/bootstrap/lib/stdlib/ebin/io_lib_fread.beam
+++ b/bootstrap/lib/stdlib/ebin/io_lib_fread.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/io_lib_pretty.beam b/bootstrap/lib/stdlib/ebin/io_lib_pretty.beam
index 57c700cb31..d7bd6172e4 100644
--- a/bootstrap/lib/stdlib/ebin/io_lib_pretty.beam
+++ b/bootstrap/lib/stdlib/ebin/io_lib_pretty.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/lists.beam b/bootstrap/lib/stdlib/ebin/lists.beam
index 5a330bece0..9bacdd52b0 100644
--- a/bootstrap/lib/stdlib/ebin/lists.beam
+++ b/bootstrap/lib/stdlib/ebin/lists.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/log_mf_h.beam b/bootstrap/lib/stdlib/ebin/log_mf_h.beam
index b812621c0a..f4561164a3 100644
--- a/bootstrap/lib/stdlib/ebin/log_mf_h.beam
+++ b/bootstrap/lib/stdlib/ebin/log_mf_h.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/maps.beam b/bootstrap/lib/stdlib/ebin/maps.beam
index 193a06241d..a49a65714f 100644
--- a/bootstrap/lib/stdlib/ebin/maps.beam
+++ b/bootstrap/lib/stdlib/ebin/maps.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/math.beam b/bootstrap/lib/stdlib/ebin/math.beam
index 7e61673b35..e388b2c304 100644
--- a/bootstrap/lib/stdlib/ebin/math.beam
+++ b/bootstrap/lib/stdlib/ebin/math.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/ms_transform.beam b/bootstrap/lib/stdlib/ebin/ms_transform.beam
index bb98ae56b3..1a07331fe9 100644
--- a/bootstrap/lib/stdlib/ebin/ms_transform.beam
+++ b/bootstrap/lib/stdlib/ebin/ms_transform.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/orddict.beam b/bootstrap/lib/stdlib/ebin/orddict.beam
index d6f267ad8f..cc32d43f7a 100644
--- a/bootstrap/lib/stdlib/ebin/orddict.beam
+++ b/bootstrap/lib/stdlib/ebin/orddict.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/ordsets.beam b/bootstrap/lib/stdlib/ebin/ordsets.beam
index 9e49566404..4f2465c9bb 100644
--- a/bootstrap/lib/stdlib/ebin/ordsets.beam
+++ b/bootstrap/lib/stdlib/ebin/ordsets.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/otp_internal.beam b/bootstrap/lib/stdlib/ebin/otp_internal.beam
index 26d495af72..41e88c4eaa 100644
--- a/bootstrap/lib/stdlib/ebin/otp_internal.beam
+++ b/bootstrap/lib/stdlib/ebin/otp_internal.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/pool.beam b/bootstrap/lib/stdlib/ebin/pool.beam
index 408ee3a0c9..2b83887ba9 100644
--- a/bootstrap/lib/stdlib/ebin/pool.beam
+++ b/bootstrap/lib/stdlib/ebin/pool.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/proc_lib.beam b/bootstrap/lib/stdlib/ebin/proc_lib.beam
index b51898901d..c960faf2d3 100644
--- a/bootstrap/lib/stdlib/ebin/proc_lib.beam
+++ b/bootstrap/lib/stdlib/ebin/proc_lib.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/proplists.beam b/bootstrap/lib/stdlib/ebin/proplists.beam
index a8ddeb2d57..89371c49f4 100644
--- a/bootstrap/lib/stdlib/ebin/proplists.beam
+++ b/bootstrap/lib/stdlib/ebin/proplists.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/qlc.beam b/bootstrap/lib/stdlib/ebin/qlc.beam
index cca174e3cd..13ec8cfe1f 100644
--- a/bootstrap/lib/stdlib/ebin/qlc.beam
+++ b/bootstrap/lib/stdlib/ebin/qlc.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/qlc_pt.beam b/bootstrap/lib/stdlib/ebin/qlc_pt.beam
index 4d86fdfec2..cb4ea94478 100644
--- a/bootstrap/lib/stdlib/ebin/qlc_pt.beam
+++ b/bootstrap/lib/stdlib/ebin/qlc_pt.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/queue.beam b/bootstrap/lib/stdlib/ebin/queue.beam
index 94728e30cc..e6195d98c1 100644
--- a/bootstrap/lib/stdlib/ebin/queue.beam
+++ b/bootstrap/lib/stdlib/ebin/queue.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/rand.beam b/bootstrap/lib/stdlib/ebin/rand.beam
index f4d803b1ee..0202b60efe 100644
--- a/bootstrap/lib/stdlib/ebin/rand.beam
+++ b/bootstrap/lib/stdlib/ebin/rand.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/random.beam b/bootstrap/lib/stdlib/ebin/random.beam
index a4bc2b6128..dee398f355 100644
--- a/bootstrap/lib/stdlib/ebin/random.beam
+++ b/bootstrap/lib/stdlib/ebin/random.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/re.beam b/bootstrap/lib/stdlib/ebin/re.beam
index 5662594d4c..3a1b44a49b 100644
--- a/bootstrap/lib/stdlib/ebin/re.beam
+++ b/bootstrap/lib/stdlib/ebin/re.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/sets.beam b/bootstrap/lib/stdlib/ebin/sets.beam
index e3609ea527..dd9009d114 100644
--- a/bootstrap/lib/stdlib/ebin/sets.beam
+++ b/bootstrap/lib/stdlib/ebin/sets.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/shell.beam b/bootstrap/lib/stdlib/ebin/shell.beam
index 319bb9aebf..66409894dd 100644
--- a/bootstrap/lib/stdlib/ebin/shell.beam
+++ b/bootstrap/lib/stdlib/ebin/shell.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/shell_default.beam b/bootstrap/lib/stdlib/ebin/shell_default.beam
index afd0c65921..ff4d1071d5 100644
--- a/bootstrap/lib/stdlib/ebin/shell_default.beam
+++ b/bootstrap/lib/stdlib/ebin/shell_default.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/slave.beam b/bootstrap/lib/stdlib/ebin/slave.beam
index 259d300155..6cbc413f9d 100644
--- a/bootstrap/lib/stdlib/ebin/slave.beam
+++ b/bootstrap/lib/stdlib/ebin/slave.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/sofs.beam b/bootstrap/lib/stdlib/ebin/sofs.beam
index f64faafa52..ee8dddf2cf 100644
--- a/bootstrap/lib/stdlib/ebin/sofs.beam
+++ b/bootstrap/lib/stdlib/ebin/sofs.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/stdlib.app b/bootstrap/lib/stdlib/ebin/stdlib.app
index ffb56f8306..41c1f14bbc 100644
--- a/bootstrap/lib/stdlib/ebin/stdlib.app
+++ b/bootstrap/lib/stdlib/ebin/stdlib.app
@@ -20,7 +20,7 @@
%%
{application, stdlib,
[{description, "ERTS CXC 138 10"},
- {vsn, "3.8.1"},
+ {vsn, "3.9.2"},
{modules, [array,
base64,
beam_lib,
@@ -108,7 +108,7 @@
dets]},
{applications, [kernel]},
{env, []},
- {runtime_dependencies, ["sasl-3.0","kernel-6.0","erts-@OTP-15128@","crypto-3.3",
+ {runtime_dependencies, ["sasl-3.0","kernel-6.0","erts-@OTP-15831:OTP-15836:OTP-15889@","crypto-3.3",
"compiler-5.0"]}
]}.
diff --git a/bootstrap/lib/stdlib/ebin/string.beam b/bootstrap/lib/stdlib/ebin/string.beam
index 5d1d66892b..0a48eb1a46 100644
--- a/bootstrap/lib/stdlib/ebin/string.beam
+++ b/bootstrap/lib/stdlib/ebin/string.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/supervisor.beam b/bootstrap/lib/stdlib/ebin/supervisor.beam
index 1816fe6c41..9eecd35256 100644
--- a/bootstrap/lib/stdlib/ebin/supervisor.beam
+++ b/bootstrap/lib/stdlib/ebin/supervisor.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/supervisor_bridge.beam b/bootstrap/lib/stdlib/ebin/supervisor_bridge.beam
index 761907b40c..f05aaef893 100644
--- a/bootstrap/lib/stdlib/ebin/supervisor_bridge.beam
+++ b/bootstrap/lib/stdlib/ebin/supervisor_bridge.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/sys.beam b/bootstrap/lib/stdlib/ebin/sys.beam
index a46ade8602..4158d032c2 100644
--- a/bootstrap/lib/stdlib/ebin/sys.beam
+++ b/bootstrap/lib/stdlib/ebin/sys.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/timer.beam b/bootstrap/lib/stdlib/ebin/timer.beam
index 2953299e8b..f018f4f750 100644
--- a/bootstrap/lib/stdlib/ebin/timer.beam
+++ b/bootstrap/lib/stdlib/ebin/timer.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/unicode.beam b/bootstrap/lib/stdlib/ebin/unicode.beam
index d61ed589aa..5f483b8358 100644
--- a/bootstrap/lib/stdlib/ebin/unicode.beam
+++ b/bootstrap/lib/stdlib/ebin/unicode.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/unicode_util.beam b/bootstrap/lib/stdlib/ebin/unicode_util.beam
index c0523f2e52..182472b5d9 100644
--- a/bootstrap/lib/stdlib/ebin/unicode_util.beam
+++ b/bootstrap/lib/stdlib/ebin/unicode_util.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/uri_string.beam b/bootstrap/lib/stdlib/ebin/uri_string.beam
index b57c84a302..51c823eb51 100644
--- a/bootstrap/lib/stdlib/ebin/uri_string.beam
+++ b/bootstrap/lib/stdlib/ebin/uri_string.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/win32reg.beam b/bootstrap/lib/stdlib/ebin/win32reg.beam
index 136b65f9fb..dc6246f9ca 100644
--- a/bootstrap/lib/stdlib/ebin/win32reg.beam
+++ b/bootstrap/lib/stdlib/ebin/win32reg.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/zip.beam b/bootstrap/lib/stdlib/ebin/zip.beam
index cd6274efe5..751707a27c 100644
--- a/bootstrap/lib/stdlib/ebin/zip.beam
+++ b/bootstrap/lib/stdlib/ebin/zip.beam
Binary files differ
diff --git a/erts/configure.in b/erts/configure.in
index e8923d0dcf..122a90557b 100644
--- a/erts/configure.in
+++ b/erts/configure.in
@@ -2729,9 +2729,25 @@ if test X${enable_hipe} != Xno; then
AC_MSG_ERROR([HiPE needs mprotect() on $ARCH])
else
enable_hipe=no
- AC_MSG_WARN([Disable HiPE due to lack of mprotect()])
+ AC_MSG_WARN([HiPE disabled due to lack of mprotect()])
fi
fi
+ AC_MSG_CHECKING([for safe signal delivery])
+ AC_TRY_COMPILE([#include <signal.h>],
+ [#if defined(__APPLE__) && defined(__MACH__) && !defined(__DARWIN__)
+ #define __DARWIN__ 1
+ #endif
+
+ #if !(defined(__GLIBC__) || defined(__DARWIN__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__sun__))
+ /*
+ * Unknown libc -- assume musl, which does not allow safe signals
+ */
+ #error "HiPE does not work without a libc that can guarantee that sigaltstack works"
+ #endif /* !(__GLIBC__ || __DARWIN__ || __NetBSD__ || __FreeBSD__ || __sun__) */],
+ [AC_MSG_RESULT([yes])],
+ [enable_hipe=no
+ AC_MSG_RESULT([no, musl probably used. Need glibc to work properly])
+ AC_MSG_WARN([HiPE disabled due to lack of safe signal delivery])])
fi
dnl check to auto-enable hipe here...
@@ -3256,7 +3272,11 @@ AS_HELP_STRING([--without-javac], [don't use any Java compiler]))
dnl
dnl Then there are a number of apps which needs a java compiler...
dnl
-need_java="jinterface ic/java_src"
+need_java="jinterface"
+
+if test -d $ERL_TOP/lib/ic; then
+ need_java="$need_java ic/java_src"
+fi
# Remove all SKIP files from previous runs
for a in $need_java ; do
diff --git a/erts/doc/src/crash_dump.xml b/erts/doc/src/crash_dump.xml
index 876834307a..b517f014c1 100644
--- a/erts/doc/src/crash_dump.xml
+++ b/erts/doc/src/crash_dump.xml
@@ -395,9 +395,9 @@ Slogan: &lt;reason&gt;</pre>
</item>
<tag><em>Heap fragment data</em></tag>
<item>
- <p>Size of fragmented heap data. This is data either created by
- messages sent to the process or by the Erlang BIFs. This
- amount depends on so many things that this field is utterly
+ <p>Size of fragmented heap data, in words. This is data either
+ created by messages sent to the process or by the Erlang BIFs.
+ This amount depends on so many things that this field is usually
uninteresting.</p>
</item>
<tag><em>Link list</em></tag>
@@ -415,11 +415,11 @@ Slogan: &lt;reason&gt;</pre>
</item>
<tag><em>Stack+heap</em></tag>
<item>
- <p>The size of the stack and heap (they share memory segment).</p>
+ <p>The size of the stack and heap, in words (they share memory segment).</p>
</item>
<tag><em>OldHeap</em></tag>
<item>
- <p>The size of the "old heap". The Erlang virtual machine uses
+ <p>The size of the "old heap", in words. The Erlang virtual machine uses
generational garbage collection with two generations. There is
one heap for new data items and one for the data that has
survived two garbage collections. The assumption (which is
@@ -432,14 +432,14 @@ Slogan: &lt;reason&gt;</pre>
</item>
<tag><em>Heap unused, OldHeap unused</em></tag>
<item>
- <p>The amount of unused memory on each heap. This information is
+ <p>The amount of unused memory on each heap, in words. This information is
usually useless.</p>
</item>
<tag><em>Memory</em></tag>
<item>
- <p>The total memory used by this process. This includes call stack,
- heap, and internal structures. Same as
- <seealso marker="erlang#process_info-2">
+ <p>The total memory used by this process, in bytes.
+ This includes call stack, heap, and internal structures.
+ Same as <seealso marker="erlang#process_info-2">
<c>erlang:process_info(Pid,memory)</c></seealso>.</p>
</item>
<tag><em>Program counter</em></tag>
@@ -531,8 +531,7 @@ Slogan: &lt;reason&gt;</pre>
</item>
<tag><em>Words</em></tag>
<item>
- <p>The number of words (usually 4 bytes/word) allocated to data
- in the table.</p>
+ <p>The number of words allocated to data in the table.</p>
</item>
<tag><em>Type</em></tag>
<item>
@@ -672,8 +671,6 @@ Slogan: &lt;reason&gt;</pre>
</item>
</taglist>
- <p>The memory use is in bytes.</p>
-
<p>Then, all loaded modules are listed. The following fields exist:</p>
<taglist>
@@ -687,7 +684,7 @@ Slogan: &lt;reason&gt;</pre>
</item>
<tag><em>Old size</em></tag>
<item>
- <p>Memory use for the old code, if any.</p>
+ <p>Memory use for the old code, in bytes.</p>
</item>
<tag><em>Current attributes</em></tag>
<item>
diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml
index d74ae23a93..4eae346ead 100644
--- a/erts/doc/src/erl_nif.xml
+++ b/erts/doc/src/erl_nif.xml
@@ -38,13 +38,9 @@
<p>A NIF library contains native implementation of some functions
of an Erlang module. The native implemented functions (NIFs) are
called like any other functions without any difference to the
- caller. Each NIF must have an implementation in Erlang that
- is invoked if the function is called before the NIF library
- is successfully loaded. A typical such stub implementation
- is to throw an exception. But it can also be used as a fallback
- implementation if the NIF library is not implemented for some
- architecture.</p>
-
+ caller. A NIF library is built as a dynamically linked library file
+ and loaded in runtime by calling <seealso marker="erlang#load_nif-2">
+ <c>erlang:load_nif/2</c></seealso>.</p>
<warning>
<marker id="WARNING"/>
<p><em>Use this functionality with extreme care.</em></p>
@@ -56,7 +52,7 @@
VM will misbehave.</p>
<list type="bulleted">
<item>
- <p>A native function that crash will crash the whole VM.</p>
+ <p>A native function that crashes will crash the whole VM.</p>
</item>
<item>
<p>An erroneously implemented native function can cause a VM
@@ -75,6 +71,9 @@
</item>
</list>
</warning>
+ </description>
+ <section>
+ <title>Example</title>
<p>A minimal example of a NIF library can look as follows:</p>
@@ -101,11 +100,13 @@ ERL_NIF_INIT(niftest,nif_funcs,NULL,NULL,NULL,NULL)</code>
-export([init/0, hello/0]).
+-on_load(init/0).
+
init() ->
erlang:load_nif("./niftest", 0).
hello() ->
- "NIF library not loaded".</code>
+ erlang:nif_error("NIF library not loaded").</code>
<p>Compile and test can look as follows (on Linux):</p>
@@ -116,28 +117,30 @@ $> erl
1> c(niftest).
{ok,niftest}
2> niftest:hello().
-"NIF library not loaded"
-3> niftest:init().
-ok
-4> niftest:hello().
"Hello world!"</code>
- <p>A better solution for a real module is to take advantage of the new
- directive <c>on_load</c> (see section
- <seealso marker="doc/reference_manual:code_loading#on_load">Running a
- Function When a Module is Loaded</seealso> in the Erlang Reference
- Manual) to load the NIF library automatically when the module is
- loaded.</p>
-
+ <p>
+ In the example above the <seealso marker="doc/reference_manual:code_loading#on_load">
+ <em><c>on_load</c></em></seealso> directive is used get function <c>init</c> called
+ automatically when the module is loaded. Function <c>init</c> in turn
+ calls <seealso marker="erlang#load_nif-2"><c>erlang:load_nif/2</c></seealso>
+ which loads the NIF library and replaces the <c>hello</c> function with its
+ native implementation in C. Once loaded, a NIF library is persistent. It
+ will not be unloaded until the module code version that it belongs to is
+ purged.</p>
+ <p>
+ Each NIF must have an implementation in Erlang to be invoked if the
+ function is called before the NIF library is successfully loaded. A
+ typical such stub implementation is to call <seealso marker="erlang#nif_error-1">
+ <c>erlang:nif_error</c></seealso> which will raise an exception. The
+ Erlang function can also be used as a fallback implementation if the NIF
+ library lacks implementation for some OS or hardware architecture for example.</p>
<note>
<p>A NIF does not have to be exported, it can be local to the module.
However, unused local stub functions will be optimized
away by the compiler, causing loading of the NIF library to fail.</p>
</note>
-
- <p>Once loaded, a NIF library is persistent. It will not be unloaded
- until the module code version that it belongs to is purged.</p>
- </description>
+ </section>
<section>
<title>Functionality</title>
diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml
index 0e82ceba7d..376deb2253 100644
--- a/erts/doc/src/erlang.xml
+++ b/erts/doc/src/erlang.xml
@@ -337,6 +337,16 @@ Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code>
</func>
<func>
+ <name name="atom_to_binary" arity="1" since="OTP @OTP-15995@"/>
+ <fsummary>Return the binary representation of an atom.</fsummary>
+ <desc>
+ <p>
+ The same as <seealso marker="#atom_to_binary/2"><c>atom_to_binary</c>
+ </seealso><c>(<anno>Atom</anno>, utf8)</c>.</p>
+ </desc>
+ </func>
+
+ <func>
<name name="atom_to_binary" arity="2" since=""/>
<fsummary>Return the binary representation of an atom.</fsummary>
<desc>
@@ -409,14 +419,21 @@ Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code>
</func>
<func>
+ <name name="binary_to_atom" arity="1" since="OTP @OTP-15995@"/>
+ <fsummary>Convert from text representation to an atom.</fsummary>
+ <desc>
+ <p>
+ The same as <seealso marker="#binary_to_atom/2"><c>binary_to_atom</c>
+ </seealso><c>(<anno>Binary</anno>, utf8)</c>.</p>
+ </desc>
+ </func>
+
+ <func>
<name name="binary_to_atom" arity="2" since=""/>
<fsummary>Convert from text representation to an atom.</fsummary>
<desc>
<p>Returns the atom whose text representation is
- <c><anno>Binary</anno></c>.
- If <c><anno>Encoding</anno></c> is <c>latin1</c>, no
- translation of bytes in the binary is done.
- If <c><anno>Encoding</anno></c>
+ <c><anno>Binary</anno></c>. If <c><anno>Encoding</anno></c>
is <c>utf8</c> or <c>unicode</c>, the binary must contain
valid UTF-8 sequences.</p>
<note>
@@ -438,6 +455,17 @@ Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code>
</func>
<func>
+ <name name="binary_to_existing_atom" arity="1" since="OTP @OTP-15995@"/>
+ <fsummary>Convert from text representation to an atom.</fsummary>
+ <desc>
+ <p>
+ The same as <seealso marker="#binary_to_existing_atom/2">
+ <c>binary_to_existing_atom</c></seealso>
+ <c>(<anno>Binary</anno>, utf8)</c>.</p>
+ </desc>
+ </func>
+
+ <func>
<name name="binary_to_existing_atom" arity="2" since=""/>
<fsummary>Convert from text representation to an atom.</fsummary>
<desc>
@@ -2553,6 +2581,7 @@ os_prompt%</pre>
true
> is_map_key(value,Map).
false</code>
+ <p>Allowed in guard tests.</p>
</desc>
</func>
diff --git a/erts/doc/src/erlc.xml b/erts/doc/src/erlc.xml
index be9b4e8d97..9d221a69ee 100644
--- a/erts/doc/src/erlc.xml
+++ b/erts/doc/src/erlc.xml
@@ -42,7 +42,7 @@
Regardless of which compiler is used, the same flags are used to provide
parameters, such as include paths and output directory.</p>
<p>The current working directory, <c>"."</c>, is not included
- in the code path when running the compiler. This to avoid loading
+ in the code path when running the compiler. This is to avoid loading
Beam files from the current working directory that could potentially
be in conflict with the compiler or the Erlang/OTP system used by the
compiler.</p>
@@ -138,6 +138,16 @@
for compiling native code, which must be compiled with the same
runtime system that it is to be run on.</p>
</item>
+ <tag><c>-no-server</c></tag>
+ <item>
+ <p>Do not use the
+ <seealso marker="#compile_server">compile server</seealso>.</p>
+ </item>
+ <tag><c>-server</c></tag>
+ <item>
+ <p>Use the
+ <seealso marker="#compile_server">compile server</seealso>.</p>
+ </item>
<tag><c>-M</c></tag>
<item>
<p>Produces a Makefile rule to track header dependencies. The
@@ -298,6 +308,52 @@ erlc +export_all file.erl</pre>
</section>
<section>
+ <marker id="compile_server"></marker>
+ <title>Compile Server</title>
+ <p>The compile server can be used to potentially speed up the
+ build of multi-file projects by avoiding to start an Erlang system
+ for each file to compile. Whether it will speed up the build
+ depends on the nature of the project and the build machine.</p>
+
+ <p>By default, the compile server is not used. It can be
+ enabled by giving <c>erlc</c> the option <c>-server</c> or by
+ setting the environment variable <c>ERLC_USE_SERVER</c> to
+ <c>yes</c> or <c>true</c>.</p>
+
+ <p>When the compile server is enabled, <c>erlc</c> will
+ automatically use the server if it is started and start the server
+ if has not already started. The server will terminate itself when
+ it has been idle for some number of seconds.</p>
+
+ <p><c>erlc</c> and the compile server communicate using the
+ Erlang distribution. The compile server is started as a hidden
+ node, with a name that includes the current user. Thus, each user
+ on a computer has their own compile server.</p>
+
+ <p>Using the compile server does not always speed up the build, as
+ the compile server sometimes must be restarted to ensure correctness.
+ Here are some examples of situtations that force a restart:</p>
+
+ <list type="bulleted">
+ <item><c>erlc</c> wants to use a different version of Erlang
+ than the compile server is using.</item>
+ <item><c>erlc</c> wants to use different options for <c>erl</c>
+ than the compile server was started with. (A change to code path
+ using the option <c>-pa</c> could cause different parse
+ transforms to be loaded. To be safe, the compile server will be
+ restarted when any <c>erl</c> option is changed.)</item>
+ <item>If the current working directory for <c>erlc</c> is
+ different from the working directory active when the compile
+ server was started, <strong>and</strong> if the compile server
+ has active jobs, it will be restarted as soon as those jobs have
+ finished. (Build systems that build files randomly across multiple
+ directories in parallel will probably not benefit from the
+ compile server.)</item>
+ </list>
+ </section>
+
+ <section>
+ <marker id="environment_variables"></marker>
<title>Environment Variables</title>
<taglist>
<tag><c>ERLC_EMULATOR</c></tag>
@@ -305,6 +361,13 @@ erlc +export_all file.erl</pre>
in the same directory as the <c>erlc</c> program itself,
or, if it does not exist, <c>erl</c> in any of the directories
specified in environment variable <c>PATH</c>.</item>
+ <tag><c>ERLC_USE_SERVER</c></tag>
+ <item>Allowed values are <c>yes</c> or <c>true</c> to use the
+ <seealso marker="#compile_server">compile
+ server</seealso>, and <c>no</c> or <c>false</c> to not use the
+ compile server. If other values are given, <c>erlc</c> will
+ print a warning message and continue.
+ </item>
</taglist>
</section>
diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml
index 5ca387ffd8..6bc242c246 100644
--- a/erts/doc/src/notes.xml
+++ b/erts/doc/src/notes.xml
@@ -31,6 +31,331 @@
</header>
<p>This document describes the changes made to the ERTS application.</p>
+<section><title>Erts 10.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ If you set <c>{linger,{true,0}}</c> on a <c>gen_tcp</c>
+ listen socket, accept a connection on that socket, and
+ then close the accepted socket, now the linger zero
+ setting is transferred to the accepted socket. Before
+ this correction that information was lost and the close
+ behaviour on the accepted socket incorrect.</p>
+ <p>
+ Own Id: OTP-15370 Aux Id: ERIERL-353 </p>
+ </item>
+ <item>
+ <p>
+ Sending ancillary data implemented in OTP-15747
+ accidentally left behind test code that caused all UDP
+ sends to fail on Windows. This has now been fixed.</p>
+ <p>
+ Own Id: OTP-15422 Aux Id: OTP-15747 </p>
+ </item>
+ <item>
+ <p>
+ In the socket nif, used invalid flags when if-def'ing for
+ supported TCP flags: TCP_MAXSEG and TCP_NODELAY (the
+ support function).</p>
+ <p>
+ Own Id: OTP-15827</p>
+ </item>
+ <item>
+ <p>
+ Fixed memory leaks in experimental socket module.</p>
+ <p>
+ Own Id: OTP-15830</p>
+ </item>
+ <item>
+ <p>
+ <c>re:run()</c> now yields when validating utf8 in a
+ large subject.</p>
+ <p>
+ Own Id: OTP-15836 Aux Id: ERL-876 </p>
+ </item>
+ <item>
+ <p>
+ Fixed bug in <c>seq_trace:set_token(label,Term)</c> which
+ could cause VM crash if <c>Term</c> was heap allocated
+ (not an atom, small integer, local pid or port). Bug
+ exists since OTP 21.0 when terms other than small
+ integers were first allowed as labels.</p>
+ <p>
+ Own Id: OTP-15849 Aux Id: ERL-700 </p>
+ </item>
+ <item>
+ <p>
+ Extra <c>-mode</c> flags given to <c>erl</c> are ignored
+ with a warning.</p>
+ <p>
+ Own Id: OTP-15852</p>
+ </item>
+ <item>
+ <p>
+ Don't loop indefinitely when <c>--enable-pgo</c> is given
+ to configure, but compiler does not support pgo.</p>
+ <p>
+ Own Id: OTP-15853 Aux Id: PR-2254 </p>
+ </item>
+ <item>
+ <p>
+ Fix <c>seq_trace:print/2</c> not to raise <c>badarg</c>
+ exception if label is not a small integer. Bug exists
+ since OTP 21.0.</p>
+ <p>
+ Own Id: OTP-15859 Aux Id: ERL-700 </p>
+ </item>
+ <item>
+ <p>
+ Fixed hipe_flush_icache_range for non-Linux OS on ARM.</p>
+ <p>
+ Own Id: OTP-15874 Aux Id: ERL-958, PR-2266 </p>
+ </item>
+ <item>
+ <p>The fix in OTP-15871 was too conservative and disabled
+ the offending load-time optimization in some cases where
+ it was safe.</p>
+ <p>
+ Own Id: OTP-15881</p>
+ </item>
+ <item>
+ <p>
+ Upgraded the ERTS internal PCRE library from version 8.42
+ to version 8.43. See <url
+ href="http://pcre.org/original/changelog.txt">http://pcre.org/original/changelog.txt</url>
+ for information about changes made to PCRE. This library
+ implements major parts of the <seealso
+ marker="stdlib:re"><c>re</c></seealso> regular
+ expressions module.</p>
+ <p>
+ Own Id: OTP-15889</p>
+ </item>
+ <item>
+ <p>
+ Fix race condition when closing a socket while using
+ <c>{active,N}</c> on Windows.</p>
+ <p>
+ Own Id: OTP-15901 Aux Id: ERL-960 PR-2272 </p>
+ </item>
+ <item>
+ <p>
+ Allow more than one <c>-config</c> command line option to
+ <c>erl</c> on Windows to conform with other OS.</p>
+ <p>
+ Own Id: OTP-15918 Aux Id: ERL-912 </p>
+ </item>
+ <item>
+ <p>
+ Fix so that ERL_FLAGS environment variable does not
+ interfere with command line arguments. Before this fix
+ you could write:</p>
+ <p>
+ <c>ERL_FLAGS="10" erl +S</c></p>
+ <p>
+ and erlang would start as if <c>+S</c> had been given the
+ argument <c>10</c>.</p>
+ <p>
+ Own Id: OTP-15931</p>
+ </item>
+ <item>
+ <p>
+ The bug with ID ERL-717 has been fixed. The functions
+ <c>io:columns()</c> and <c>io:rows()</c> only worked
+ correctly inside interactive erlang shells before this
+ fix. These functions returned <c>{error,enotsup}</c>
+ before this fix even if stdout and stdin were connected
+ to a terminal when they were invoked from an escript or a
+ program started with e.g., <c>erl -noshell</c>.</p>
+ <p>
+ Own Id: OTP-15959 Aux Id: ERL-717 </p>
+ </item>
+ <item>
+ <p>
+ Do not use named label in <c>ethread.c</c> inline
+ assemble. This allows erts to be compiled using gcc 9.1.0
+ with LTO enabled.</p>
+ <p>
+ Own Id: OTP-15971 Aux Id: PR-2333 </p>
+ </item>
+ <item>
+ <p><c>erlang:fun_to_list/1</c> will now escape the module
+ and function name when necessary.</p>
+ <p>
+ Own Id: OTP-15975 Aux Id: ERL-1009 </p>
+ </item>
+ <item>
+ <p><c>process_info(P,binary)</c> would neglect to look
+ through heap fragments, potentially missing a few
+ binaries associated with the process.</p>
+ <p>
+ Own Id: OTP-15978 Aux Id: ERIERL-366 </p>
+ </item>
+ <item>
+ <p>
+ HiPE is now automatically disabled on systems with
+ non-glibc implementation (for instance musl). This is
+ because musl does not provide the API's for guaranteeing
+ that signals are delivered on the correct native stack.</p>
+ <p>
+ Own Id: OTP-16037</p>
+ </item>
+ <item>
+ <p>
+ Fixed bug triggered if a process is killed during call to
+ <c>persistent_term:put</c> or
+ <c>persistent_term:erase</c>.</p>
+ <p>
+ Own Id: OTP-16041</p>
+ </item>
+ <item>
+ <p>
+ Add units to all memory slogans in the crash dump
+ documentation.</p>
+ <p>
+ Own Id: OTP-16042</p>
+ </item>
+ <item>
+ <p>
+ Fix a bug in <c>binary_to_term</c> that would crash the
+ emulator if a term larger than 16GB was to be decoded.</p>
+ <p>
+ Own Id: OTP-16058 Aux Id: PR-2382 </p>
+ </item>
+ <item>
+ <p>
+ Fixed bug related to an exiting process sending EXIT and
+ DOWN signals to remote linked/monitored processes. Bugs
+ exists since OTP 22.0.</p>
+ <p>
+ Own Id: OTP-16060</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p><c>erlc</c> can now automatically use a compile server
+ to avoid starting an Erlang system for each file to be
+ compiled in a multi-file project. See the documentation
+ for how to enable it.</p>
+ <p>
+ Own Id: OTP-15738 Aux Id: PR-2361 </p>
+ </item>
+ <item>
+ <p>
+ The possibility to send ancillary data, in particular the
+ TOS field, has been added to <c>gen_udp:send/4,5</c>.</p>
+ <p>
+ Own Id: OTP-15747 Aux Id: ERIERL-294 </p>
+ </item>
+ <item>
+ <p>
+ The net module has been split into 'net' (kernel) and
+ prim_net (preloaded).</p>
+ <p>
+ Own Id: OTP-15765</p>
+ </item>
+ <item>
+ <p>
+ Socket counters now works as expected and can also be
+ extracted with the (new) info function.</p>
+ <p>
+ Own Id: OTP-15818</p>
+ </item>
+ <item>
+ <p>
+ <c>re:run()</c> now avoids validating utf8 in the subject
+ more than once in the same call. This validation could
+ previously be performed multiple times when the
+ <c>global</c> option was passed.</p>
+ <p>
+ Own Id: OTP-15831 Aux Id: ERL-876 </p>
+ </item>
+ <item>
+ <p>
+ The un-documented function <c>erlang:dist_get_stat/1</c>
+ now returns the real value of what the distribution queue
+ contains instead of a boolean.</p>
+ <p>
+ Own Id: OTP-15905 Aux Id: PR-2270 </p>
+ </item>
+ <item>
+ <p>
+ ETS <c>ordered_set</c> tables with
+ <c>write_concurrency</c> enabled has got a performance
+ issue fixed. There were no limits for the values of
+ internal statistics counters before this fix. This could
+ result in that the data structure sometimes reacted
+ slowly to a change in how many parallel processes were
+ using it.</p>
+ <p>
+ Own Id: OTP-15906</p>
+ </item>
+ <item>
+ <p>
+ Optimize the reception of large distribution messages.</p>
+ <p>
+ Own Id: OTP-15926 Aux Id: PR-2291 </p>
+ </item>
+ <item>
+ <p>Binary matching and functions like
+ <c>split_binary/2</c> will now create heap binaries when
+ the results are small enough, reducing the chances of
+ small sub-binaries keeping large binaries alive.</p>
+ <p>
+ Own Id: OTP-15977 Aux Id: ERIERL-366 </p>
+ </item>
+ <item>
+ <p>Fixed rare emulator crash in
+ <c>instrument:allocations/0-1</c>.</p>
+ <p>
+ Own Id: OTP-15983</p>
+ </item>
+ <item>
+ <p>
+ Ports could pass very small binaries as reference counted
+ off heap binaries to processes. This could cause an
+ unnecessary large memory usage and an unnecessary load on
+ the binary allocator. Small binaries are now always
+ passed as heap binaries to processes.</p>
+ <p>
+ Own Id: OTP-16001 Aux Id: ERIERL-366 </p>
+ </item>
+ <item>
+ <p>
+ <c>unicode:characters_to_binary()</c> could return very
+ small binaries as reference counted off heap binaries.
+ This could cause an unnecessary large memory usage and an
+ unnecessary load on the binary allocator. Small binaries
+ are now always returned as heap binaries.</p>
+ <p>
+ Own Id: OTP-16002 Aux Id: ERIERL-366 </p>
+ </item>
+ <item>
+ <p>
+ Improved <c>erl_nif</c> documentation regarding
+ <c>on_load</c> and Erlang stub/fallback functions.</p>
+ <p>
+ Own Id: OTP-16028 Aux Id: PR-2362 </p>
+ </item>
+ <item>
+ <p>
+ New feature <c>ets:info(_, binary)</c> to get information
+ about all reference counted binaries kept by a table.
+ This is the same kind of debug information that
+ <c>process_info(_, binary)</c> returns for a process.</p>
+ <p>
+ Own Id: OTP-16035 Aux Id: ERIERL-366 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Erts 10.4.4</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -640,6 +965,42 @@
</section>
+<section><title>Erts 10.3.5.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p><c>process_info(P,binary)</c> would neglect to look
+ through heap fragments, potentially missing a few
+ binaries associated with the process.</p>
+ <p>
+ Own Id: OTP-15978 Aux Id: ERIERL-366 </p>
+ </item>
+ <item>
+ <p>
+ Fixed bug triggered if a process is killed during call to
+ <c>persistent_term:put</c> or
+ <c>persistent_term:erase</c>.</p>
+ <p>
+ Own Id: OTP-16041</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>Fixed rare emulator crash in
+ <c>instrument:allocations/0-1</c>.</p>
+ <p>
+ Own Id: OTP-15983</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Erts 10.3.5.4</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/erts/doc/src/socket.xml b/erts/doc/src/socket.xml
index da049b1a7f..708f87edf6 100644
--- a/erts/doc/src/socket.xml
+++ b/erts/doc/src/socket.xml
@@ -47,14 +47,16 @@
<seealso marker="#recv_async"><c>recv/3</c></seealso>
function with Timeout set to <c>nowait</c> (<c>recv(Sock, 0, nowait)</c>)
when there is actually nothing to read, it will return with
- <c>{ok, </c>
- <seealso marker="#type-select_info"><c>SelectInfo</c></seealso><c>}</c>.
+ <c>{select, </c>
+ <seealso marker="#type-select_info"><c>SelectInfo</c></seealso><c>}</c>
+ (<c>SelectInfo</c> contains the
+ <seealso marker="socket#type-select_ref">SelectRef</seealso>).
When data eventually arrives a 'select' message
will be sent to the caller: </p>
<taglist>
<!-- NOTE THAT THE EMPTY TAG IS INTENTIONAL -->
<tag></tag>
- <item><c>{'$socket', socket(), select, select_ref()}</c></item>
+ <item><c>{'$socket', socket(), select, SelectRef}</c></item>
</taglist>
<p>The caller can now make another
call to the recv function and now expect data.</p>
@@ -70,7 +72,7 @@
<p>This message indicates
that the (asynchronous) operation has been aborted.
If, for instance, the socket has been closed (by another process),
- <c>Info</c> will be <c>{select_ref(), closed}</c>. </p>
+ <c>Info</c> will be <c>{SelectRef, closed}</c>. </p>
</note>
<note>
<p>There is currently <em>no</em> support for Windows. </p>
@@ -325,7 +327,7 @@
</func>
<func>
- <name name="accept" arity="2" clause_i="1" anchor="accept_async" since="OTP @OTP-15731@"/>
+ <name name="accept" arity="2" clause_i="1" anchor="accept_async" since="OTP 22.1"/>
<fsummary>Accept a connection on a socket.</fsummary>
<desc>
<p>Accept a connection on a socket.</p>
@@ -338,7 +340,9 @@
<p>In the case when there is no connections waiting, the function
will return with the <c>SelectInfo</c>. The caller can then await a
select message, <c>{'$socket', Socket, select, Info}</c> (where
- <c>Info</c> is the <c>select_ref()</c> from the <c>SelectInfo</c>),
+ <c>Info</c> is the
+ <seealso marker="socket#type-select_ref"><c>ref</c></seealso>
+ field from the <c>SelectInfo</c>),
when a client connects (a subsequent call to accept will then return
the socket). </p>
</desc>
@@ -358,7 +362,7 @@
</func>
<func>
- <name name="cancel" arity="2" since="OTP @OTP-15731@"/>
+ <name name="cancel" arity="2" since="OTP 22.1"/>
<fsummary>Cancel an asynchronous request.</fsummary>
<desc>
<p>Cancel an asynchronous request.</p>
@@ -399,7 +403,7 @@
</func>
<func>
- <name name="connect" arity="3" clause_i="1" anchor="connect_async" since="OTP @OTP-15731@"/>
+ <name name="connect" arity="3" clause_i="1" anchor="connect_async" since="OTP 22.1"/>
<fsummary>Initiate a connection on a socket.</fsummary>
<desc>
<p>This function connects the socket to the address
@@ -410,7 +414,9 @@
<seealso marker="#type-select_info"><c>SelectInfo</c></seealso>.
The caller can then await a
select message, <c>{'$socket', Socket, select, Info}</c> (where
- <c>Info</c> is the <c>select_ref()</c> from the <c>SelectInfo</c>,
+ <c>Info</c> is the
+ <seealso marker="socket#type-select_ref"><c>ref</c></seealso>
+ field from the <c>SelectInfo</c>,
a subsequent call to connect will then
establish the connection). </p>
</desc>
@@ -465,7 +471,7 @@
</func>
<func>
- <name name="info" arity="1" since="OTP @OTP-15818@"/>
+ <name name="info" arity="1" since="OTP 22.1"/>
<fsummary>Get miscellaneous socket info.</fsummary>
<desc>
<p>Get miscellaneous info about the socket.</p>
@@ -538,8 +544,8 @@
</func>
<func>
- <name name="recv" arity="3" clause_i="2" anchor="recv_async" since="OTP @OTP-15731@"/>
- <name name="recv" arity="4" clause_i="1" since="OTP @OTP-15731@"/>
+ <name name="recv" arity="3" clause_i="2" anchor="recv_async" since="OTP 22.1"/>
+ <name name="recv" arity="4" clause_i="1" since="OTP 22.1"/>
<fsummary>Receive a message from a socket.</fsummary>
<desc>
<p>Receive a message from a socket.</p>
@@ -551,7 +557,9 @@
<p>In the case when there is no data waiting, the function
will return with the <c>SelectInfo</c>. The caller can then await a
select message, <c>{'$socket', Socket, select, Info}</c> (where
- <c>Info</c> is the <c>select_ref()</c> from the <c>SelectInfo</c>),
+ <c>Info</c> is the
+ <seealso marker="socket#type-select_ref"><c>ref</c></seealso>
+ field from the <c>SelectInfo</c>),
when data has arrived (a subsequent call to recv will then return
the data). </p>
<p>Note that if a length (<c>> 0</c>) is specified, and only part
@@ -588,9 +596,9 @@
</func>
<func>
- <name name="recvfrom" arity="3" clause_i="1" anchor="recvfrom_async" since="OTP @OTP-15731@"/>
- <name name="recvfrom" arity="3" clause_i="4" since="OTP @OTP-15731@"/>
- <name name="recvfrom" arity="4" clause_i="1" since="OTP @OTP-15731@"/>
+ <name name="recvfrom" arity="3" clause_i="1" anchor="recvfrom_async" since="OTP 22.1"/>
+ <name name="recvfrom" arity="3" clause_i="4" since="OTP 22.1"/>
+ <name name="recvfrom" arity="4" clause_i="1" since="OTP 22.1"/>
<fsummary>Receive a message from a socket.</fsummary>
<desc>
<p>Receive a message from a socket.</p>
@@ -610,7 +618,9 @@
<p>In the case when there is no data waiting, the function
will return with the <c>SelectInfo</c>. The caller can then await a
select message, <c>{'$socket', Socket, select, Info}</c> (where
- <c>Info</c> is the <c>select_ref()</c> from the <c>SelectInfo</c>),
+ <c>Info</c> is the
+ <seealso marker="socket#type-select_ref"><c>ref</c></seealso>
+ field from the <c>SelectInfo</c>),
when data has arrived (a subsequent call to recvfrom will then return
the data). </p>
</desc>
@@ -653,9 +663,9 @@
</func>
<func>
- <name name="recvmsg" arity="2" clause_i="2" anchor="recvmsg_async" since="OTP @OTP-15731@"/>
- <name name="recvmsg" arity="3" clause_i="1" since="OTP @OTP-15731@"/>
- <name name="recvmsg" arity="5" clause_i="1" since="OTP @OTP-15731@"/>
+ <name name="recvmsg" arity="2" clause_i="2" anchor="recvmsg_async" since="OTP 22.1"/>
+ <name name="recvmsg" arity="3" clause_i="1" since="OTP 22.1"/>
+ <name name="recvmsg" arity="5" clause_i="1" since="OTP 22.1"/>
<fsummary>Receive a message from a socket.</fsummary>
<desc>
<p>Receive a message from a socket.</p>
@@ -686,7 +696,9 @@
<p>In the case when there is no data waiting, the function
will return with the <c>SelectInfo</c>. The caller can then await a
select message, <c>{'$socket', Socket, select, Info}</c> (where
- <c>Info</c> is the <c>select_ref()</c> from the <c>SelectInfo</c>),
+ <c>Info</c> is the
+ <seealso marker="socket#type-select_ref"><c>ref</c></seealso>
+ field from the <c>SelectInfo</c>),
when data has arrived (a subsequent call to recvmsg will then return
the data). </p>
</desc>
@@ -704,8 +716,8 @@
</func>
<func>
- <name name="send" arity="3" clause_i="2" anchor="send_async" since="OTP @OTP-15731@"/>
- <name name="send" arity="4" clause_i="1" since="OTP @OTP-15731@"/>
+ <name name="send" arity="3" clause_i="2" anchor="send_async" since="OTP 22.1"/>
+ <name name="send" arity="4" clause_i="1" since="OTP 22.1"/>
<fsummary>Send a message on a socket.</fsummary>
<desc>
<p>Send a message on a connected socket.</p>
@@ -714,7 +726,9 @@
the function will return with the <c>SelectInfo</c>. The caller
can then await a select message,
<c>{'$socket', Socket, select, Info}</c>
- (where <c>Info</c> is the <c>select_ref()</c> from the
+ (where <c>Info</c> is the
+ <seealso marker="socket#type-select_ref"><c>ref</c></seealso>
+ field from the
<c>SelectInfo</c>), when there is room for more data (a subsequent
call to send will then send the data). </p>
<p>Note that if not all the data was sent, the function will return
@@ -750,8 +764,8 @@
</func>
<func>
- <name name="sendmsg" arity="3" clause_i="2" anchor="sendmsg_async" since="OTP @OTP-15731@"/>
- <name name="sendmsg" arity="4" clause_i="1" since="OTP @OTP-15731@"/>
+ <name name="sendmsg" arity="3" clause_i="2" anchor="sendmsg_async" since="OTP 22.1"/>
+ <name name="sendmsg" arity="4" clause_i="1" since="OTP 22.1"/>
<fsummary>Send a message on a socket.</fsummary>
<desc>
<p>Send a message on a socket. The destination, if needed
@@ -772,7 +786,9 @@
the function will return with the <c>SelectInfo</c>. The caller
can then await a select message,
<c>{'$socket', Socket, select, Info}</c>
- (where <c>Info</c> is the <c>select_ref()</c> from the
+ (where <c>Info</c> is the
+ <seealso marker="socket#type-select_ref"><c>ref</c></seealso>
+ field from the
<c>SelectInfo</c>), when there is room for more data (a subsequent
call to sendmsg will then send the data). </p>
</desc>
@@ -790,8 +806,8 @@
</func>
<func>
- <name name="sendto" arity="4" clause_i="2" anchor="sendto_async" since="OTP @OTP-15731@"/>
- <name name="sendto" arity="5" clause_i="1" since="OTP @OTP-15731@"/>
+ <name name="sendto" arity="4" clause_i="2" anchor="sendto_async" since="OTP 22.1"/>
+ <name name="sendto" arity="5" clause_i="1" since="OTP 22.1"/>
<fsummary>Send a message on a socket.</fsummary>
<desc>
<p>Send a message on a socket, to the specified destination.</p>
@@ -800,7 +816,9 @@
the function will return with the <c>SelectInfo</c>. The caller
can then await a select message,
<c>{'$socket', Socket, select, Info}</c>
- (where <c>Info</c> is the <c>select_ref()</c> from the
+ (where <c>Info</c> is the
+ <seealso marker="socket#type-select_ref"><c>ref</c></seealso>
+ field from the
<c>SelectInfo</c>), when there is room for more data (a subsequent
call to sendto will then send the data). </p>
</desc>
@@ -935,4 +953,3 @@ server(Addr, Port) ->
</code>
</section>
</erlref>
-
diff --git a/erts/doc/src/socket_usage.xml b/erts/doc/src/socket_usage.xml
index 7e65bcbf70..c3cda9a615 100644
--- a/erts/doc/src/socket_usage.xml
+++ b/erts/doc/src/socket_usage.xml
@@ -56,23 +56,20 @@
function with Timeout set to <c>nowait</c> (i.e.
<c>recv(Sock, 0, nowait)</c>)
when there is actually nothing to read, it will return with
- <c>{ok, </c>
- <seealso marker="socket#type-select_info"><c>SelectInfo</c></seealso><c>}</c>.
+ <c>{select, </c>
+ <seealso marker="socket#type-select_info"><c>SelectInfo</c></seealso><c>}</c>
+ (<c>SelectInfo</c> contains the
+ <seealso marker="socket#type-select_ref">SelectRef</seealso>).
When data eventually arrives a 'select message'
will be sent to the caller:</p>
<taglist>
<!-- NOTE THAT THE EMPTY TAG IS INTENTIONAL -->
<tag></tag>
- <item><c>{'$socket', socket(), select, select_ref()}</c></item>
+ <item><c>{'$socket', socket(), select, SelectRef}</c></item>
</taglist>
<p>The caller can then make another
call to the recv function and now expect data.</p>
<p>The user must also be prepared to receive an abort message: </p>
- <!--
- <list type="ordered">
- <item><c>{'$socket', Sock, abort, Info}</c></item>
- </list>
- -->
<taglist>
<!-- NOTE THAT THE EMPTY TAG IS INTENTIONAL -->
<tag></tag>
@@ -81,7 +78,7 @@
<p>If the operation is aborted
for whatever reason (e.g. if the socket is closed "by someone else").
The <c>Info</c> part contains the abort reason (in this case that
- the socket has been closed <c>Info = {select_ref(), closed}</c>). </p>
+ the socket has been closed <c>Info = {SelectRef, closed}</c>). </p>
<p>Note that all other users are <em>locked out</em> until the
'current user' has called the function (recv in this case). So either
immediately call the function or
@@ -197,7 +194,7 @@
<cell><em>Other Requirements and comments</em></cell>
</row>
<row>
- <cell>acceptcon</cell>
+ <cell>acceptconn</cell>
<cell>boolean()</cell>
<cell>no</cell>
<cell>yes</cell>
@@ -208,7 +205,9 @@
<cell>string()</cell>
<cell>yes</cell>
<cell>yes</cell>
- <cell>none</cell>
+ <cell>Before Linux 3.8, this socket option could be set, but not get.
+ Only works for some socket types (e.g. <c>inet</c>).
+ If empty value is set, the binding is removed.</cell>
</row>
<row>
<cell>broadcast</cell>
@@ -222,7 +221,7 @@
<cell>integer()</cell>
<cell>yes</cell>
<cell>yes</cell>
- <cell>requires admin capability</cell>
+ <cell>may require admin capability</cell>
</row>
<row>
<cell>domain</cell>
diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in
index a8b1728b90..70718575e9 100644
--- a/erts/emulator/Makefile.in
+++ b/erts/emulator/Makefile.in
@@ -599,7 +599,6 @@ endif
$(TTF_DIR)/erl_bif_table.c \
$(TTF_DIR)/erl_bif_table.h \
-$(TTF_DIR)/erl_bif_wrap.c \
$(TTF_DIR)/erl_bif_list.h \
$(TTF_DIR)/erl_atom_table.c \
$(TTF_DIR)/erl_atom_table.h \
@@ -741,8 +740,8 @@ $(OBJDIR)/PROFILE: $(BINDIR)/$(PROFILE_EXECUTABLE)
$(V_at)echo " PROFILE ${PROFILE_EXECUTABLE}"
$(V_at)rm -f $(OBJDIR)/erl*.profraw
$(V_at)set -e; LLVM_PROFILE_FILE="$(OBJDIR)/erlc-%m.profraw" \
- ERL_FLAGS="-emu_type prof${TYPEMARKER} +S 1" $(ERLC) -DPGO \
- -o $(OBJDIR) test/estone_SUITE.erl > $(OBJDIR)/PROFILE_LOG
+ ERL_FLAGS="-emu_type prof${TYPEMARKER} +S 1" $(ERLC) -no-server \
+ -DPGO -o $(OBJDIR) test/estone_SUITE.erl > $(OBJDIR)/PROFILE_LOG
$(V_at)set -e; LLVM_PROFILE_FILE="$(OBJDIR)/erl-%m.profraw" \
ERL_FLAGS="-emu_type prof${TYPEMARKER} +S 1" $(ERL) -pa $(OBJDIR) \
-noshell -s estone_SUITE pgo -s init stop >> $(OBJDIR)/PROFILE_LOG
@@ -885,7 +884,6 @@ RUN_OBJS += \
$(OBJDIR)/erl_bif_persistent.o \
$(OBJDIR)/erl_bif_atomics.o $(OBJDIR)/erl_bif_counters.o \
$(OBJDIR)/erl_bif_trace.o $(OBJDIR)/erl_bif_unique.o \
- $(OBJDIR)/erl_bif_wrap.o $(OBJDIR)/erl_nfunc_sched.o \
$(OBJDIR)/erl_guard_bifs.o $(OBJDIR)/erl_dirty_bif_wrap.o \
$(OBJDIR)/erl_trace.o $(OBJDIR)/copy.o \
$(OBJDIR)/utils.o $(OBJDIR)/bif.o \
@@ -920,7 +918,8 @@ RUN_OBJS += \
$(OBJDIR)/erl_ptab.o $(OBJDIR)/erl_map.o \
$(OBJDIR)/erl_msacc.o $(OBJDIR)/erl_lock_flags.o \
$(OBJDIR)/erl_io_queue.o $(OBJDIR)/erl_db_catree.o \
- $(ESOCK_RUN_OBJS) $(OBJDIR)/erl_flxctr.o
+ $(ESOCK_RUN_OBJS) $(OBJDIR)/erl_flxctr.o \
+ $(OBJDIR)/erl_nfunc_sched.o
LTTNG_OBJS = $(OBJDIR)/erlang_lttng.o
diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names
index 412d689246..4c67c2029f 100644
--- a/erts/emulator/beam/atom.names
+++ b/erts/emulator/beam/atom.names
@@ -248,6 +248,7 @@ atom erts_debug
atom erts_dflags
atom erts_internal
atom ets
+atom ets_info_binary
atom ETS_TRANSFER='ETS-TRANSFER'
atom exact_reductions
atom exception_from
@@ -403,6 +404,7 @@ atom min_bin_vheap_size
atom minor
atom minor_version
atom Minus='-'
+atom MinusMinus='--'
atom module
atom module_info
atom monitored_by
@@ -493,6 +495,7 @@ atom packet
atom packet_size
atom parallelism
atom Plus='+'
+atom PlusPlus='++'
atom pause
atom pending
atom pending_driver
diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c
index bb1b2e5b27..dcbff99f54 100644
--- a/erts/emulator/beam/beam_bif_load.c
+++ b/erts/emulator/beam/beam_bif_load.c
@@ -835,21 +835,25 @@ BIF_RETTYPE finish_after_on_load_2(BIF_ALIST_2)
*/
num_exps = export_list_size(code_ix);
for (i = 0; i < num_exps; i++) {
- Export *ep = export_list(i,code_ix);
- if (ep == NULL || ep->info.mfa.module != BIF_ARG_1) {
- continue;
- }
- if (ep->beam[1] != 0) {
- ep->addressv[code_ix] = (void *) ep->beam[1];
- ep->beam[1] = 0;
- } else {
- if (ep->addressv[code_ix] == ep->beam &&
- BeamIsOpCode(ep->beam[0], op_apply_bif)) {
- continue;
- }
- ep->addressv[code_ix] = ep->beam;
- ep->beam[0] = BeamOpCodeAddr(op_call_error_handler);
- }
+ Export *ep = export_list(i, code_ix);
+
+ if (ep == NULL || ep->info.mfa.module != BIF_ARG_1) {
+ continue;
+ }
+
+ DBG_CHECK_EXPORT(ep, code_ix);
+
+ if (ep->trampoline.not_loaded.deferred != 0) {
+ ep->addressv[code_ix] = (void*)ep->trampoline.not_loaded.deferred;
+ ep->trampoline.not_loaded.deferred = 0;
+ } else {
+ if (ep->bif_table_index != -1) {
+ continue;
+ }
+
+ ep->addressv[code_ix] = ep->trampoline.raw;
+ ep->trampoline.op = BeamOpCodeAddr(op_call_error_handler);
+ }
}
modp->curr.code_hdr->on_load_function_ptr = NULL;
@@ -872,10 +876,11 @@ BIF_RETTYPE finish_after_on_load_2(BIF_ALIST_2)
if (ep == NULL || ep->info.mfa.module != BIF_ARG_1) {
continue;
}
- if (BeamIsOpCode(ep->beam[0], op_apply_bif)) {
+ if (ep->bif_table_index != -1) {
continue;
}
- ep->beam[1] = 0;
+
+ ep->trampoline.not_loaded.deferred = 0;
}
}
erts_release_code_write_permission();
@@ -1125,16 +1130,15 @@ check_process_code(Process* rp, Module* modp, int *redsp, int fcalls)
mod_size = modp->old.code_length;
/*
- * Check if current instruction or continuation pointer points into module.
+ * Check if the instruction pointer points into module.
*/
- if (ErtsInArea(rp->i, mod_start, mod_size)
- || ErtsInArea(rp->cp, mod_start, mod_size)) {
+ if (ErtsInArea(rp->i, mod_start, mod_size)) {
return am_true;
}
-
+
*redsp += 1;
- if (erts_check_nif_export_in_area(rp, mod_start, mod_size))
+ if (erts_check_nfunc_in_area(rp, mod_start, mod_size))
return am_true;
*redsp += (STACK_START(rp) - rp->stop) / 32;
@@ -1799,6 +1803,78 @@ erts_queue_release_literals(Process* c_p, ErtsLiteralArea* literals)
}
}
+struct debug_la_oh {
+ void (*func)(ErlOffHeap *, void *);
+ void *arg;
+};
+
+static void debug_later_cleanup_literal_area_off_heap(void *vfap,
+ ErtsThrPrgrVal val,
+ void *vlrlap)
+{
+ struct debug_la_oh *fap = vfap;
+ ErtsLaterReleasLiteralArea *lrlap = vlrlap;
+ ErtsLiteralArea *lap = lrlap->la;
+ if (!erts_debug_have_accessed_literal_area(lap)) {
+ ErlOffHeap oh;
+ ERTS_INIT_OFF_HEAP(&oh);
+ oh.first = lap->off_heap;
+ (*fap->func)(&oh, fap->arg);
+ erts_debug_save_accessed_literal_area(lap);
+ }
+}
+
+static void debug_later_complete_literal_area_switch_off_heap(void *vfap,
+ ErtsThrPrgrVal val,
+ void *vlap)
+{
+ struct debug_la_oh *fap = vfap;
+ ErtsLiteralArea *lap = vlap;
+ if (lap && !erts_debug_have_accessed_literal_area(lap)) {
+ ErlOffHeap oh;
+ ERTS_INIT_OFF_HEAP(&oh);
+ oh.first = lap->off_heap;
+ (*fap->func)(&oh, fap->arg);
+ erts_debug_save_accessed_literal_area(lap);
+ }
+}
+
+
+void
+erts_debug_foreach_release_literal_area_off_heap(void (*func)(ErlOffHeap *, void *), void *arg)
+{
+ ErtsLiteralArea *lap;
+ ErlOffHeap oh;
+ ErtsLiteralAreaRef *ref;
+ struct debug_la_oh fa;
+ erts_mtx_lock(&release_literal_areas.mtx);
+ for (ref = release_literal_areas.first; ref; ref = ref->next) {
+ lap = ref->literal_area;
+ if (!erts_debug_have_accessed_literal_area(lap)) {
+ ERTS_INIT_OFF_HEAP(&oh);
+ oh.first = lap->off_heap;
+ (*func)(&oh, arg);
+ erts_debug_save_accessed_literal_area(lap);
+ }
+ }
+ erts_mtx_unlock(&release_literal_areas.mtx);
+ lap = ERTS_COPY_LITERAL_AREA();
+ if (lap && !erts_debug_have_accessed_literal_area(lap)) {
+ ERTS_INIT_OFF_HEAP(&oh);
+ oh.first = lap->off_heap;
+ (*func)(&oh, arg);
+ erts_debug_save_accessed_literal_area(lap);
+ }
+ fa.func = func;
+ fa.arg = arg;
+ erts_debug_later_op_foreach(later_release_literal_area,
+ debug_later_cleanup_literal_area_off_heap,
+ (void *) &fa);
+ erts_debug_later_op_foreach(complete_literal_area_switch,
+ debug_later_complete_literal_area_switch_off_heap,
+ (void *) &fa);
+}
+
/*
* Move code from current to old and null all export entries for the module
*/
@@ -1813,25 +1889,28 @@ delete_code(Module* modp)
for (i = 0; i < num_exps; i++) {
Export *ep = export_list(i, code_ix);
if (ep != NULL && (ep->info.mfa.module == module)) {
- if (ep->addressv[code_ix] == ep->beam) {
- if (BeamIsOpCode(ep->beam[0], op_apply_bif)) {
- continue;
- }
- else if (BeamIsOpCode(ep->beam[0], op_i_generic_breakpoint)) {
+ if (ep->addressv[code_ix] == ep->trampoline.raw) {
+ if (BeamIsOpCode(ep->trampoline.op, op_i_generic_breakpoint)) {
ERTS_LC_ASSERT(erts_thr_progress_is_blocking());
ASSERT(modp->curr.num_traced_exports > 0);
DBG_TRACE_MFA_P(&ep->info.mfa,
"export trace cleared, code_ix=%d", code_ix);
- erts_clear_export_break(modp, &ep->info);
+ erts_clear_export_break(modp, ep);
}
else {
- ASSERT(BeamIsOpCode(ep->beam[0], op_call_error_handler) ||
+ ASSERT(BeamIsOpCode(ep->trampoline.op, op_call_error_handler) ||
!erts_initialized);
}
}
- ep->addressv[code_ix] = ep->beam;
- ep->beam[0] = BeamOpCodeAddr(op_call_error_handler);
- ep->beam[1] = 0;
+
+ if (ep->bif_table_index != -1 && ep->is_bif_traced) {
+ /* Code unloading kills both global and local call tracing. */
+ ep->is_bif_traced = 0;
+ }
+
+ ep->addressv[code_ix] = ep->trampoline.raw;
+ ep->trampoline.op = BeamOpCodeAddr(op_call_error_handler);
+ ep->trampoline.not_loaded.deferred = 0;
DBG_TRACE_MFA_P(&ep->info.mfa,
"export invalidation, code_ix=%d", code_ix);
}
diff --git a/erts/emulator/beam/beam_bp.c b/erts/emulator/beam/beam_bp.c
index 0832b3f374..1bb20f6ae3 100644
--- a/erts/emulator/beam/beam_bp.c
+++ b/erts/emulator/beam/beam_bp.c
@@ -207,9 +207,6 @@ erts_bp_match_functions(BpFunctions* f, ErtsCodeMFA *mfa, int specified)
if (erts_is_function_native(ci)) {
continue;
}
- if (is_nil(ci->mfa.module)) { /* Ignore BIF stub */
- continue;
- }
switch (specified) {
case 3:
if (ci->mfa.arity != mfa->arity)
@@ -244,8 +241,10 @@ erts_bp_match_export(BpFunctions* f, ErtsCodeMFA *mfa, int specified)
f->matching = (BpFunction *) Alloc(num_exps*sizeof(BpFunction));
ne = 0;
for (i = 0; i < num_exps; i++) {
- Export* ep = export_list(i, code_ix);
- BeamInstr* pc;
+ BeamInstr *func;
+ Export* ep;
+
+ ep = export_list(i, code_ix);
switch (specified) {
case 3:
@@ -263,19 +262,20 @@ erts_bp_match_export(BpFunctions* f, ErtsCodeMFA *mfa, int specified)
ASSERT(0);
}
- pc = ep->beam;
- if (ep->addressv[code_ix] == pc) {
- if (BeamIsOpCode(*pc, op_apply_bif) ||
- BeamIsOpCode(*pc, op_call_error_handler)) {
- continue;
- }
- ASSERT(BeamIsOpCode(*pc, op_i_generic_breakpoint));
- } else if (erts_is_function_native(erts_code_to_codeinfo(ep->addressv[code_ix]))) {
- continue;
- }
+ func = ep->addressv[code_ix];
+
+ if (func == ep->trampoline.raw) {
+ if (BeamIsOpCode(*func, op_call_error_handler)) {
+ continue;
+ }
+ ASSERT(BeamIsOpCode(*func, op_i_generic_breakpoint));
+ } else if (erts_is_function_native(erts_code_to_codeinfo(func))) {
+ continue;
+ }
f->matching[ne].ci = &ep->info;
f->matching[ne].mod = erts_get_module(ep->info.mfa.module, code_ix);
+
ne++;
}
@@ -305,18 +305,6 @@ erts_consolidate_bp_data(BpFunctions* f, int local)
}
}
-void
-erts_consolidate_bif_bp_data(void)
-{
- int i;
-
- ERTS_LC_ASSERT(erts_has_code_write_permission());
- for (i = 0; i < BIF_SIZE; i++) {
- Export *ep = bif_export[i];
- consolidate_bp_data(0, &ep->info, 0);
- }
-}
-
static void
consolidate_bp_data(Module* modp, ErtsCodeInfo *ci, int local)
{
@@ -495,7 +483,7 @@ erts_set_mtrace_break(BpFunctions* f, Binary *match_spec, ErtsTracer tracer)
}
void
-erts_set_call_trace_bif(ErtsCodeInfo *ci, Binary *match_spec, int local)
+erts_set_export_trace(ErtsCodeInfo *ci, Binary *match_spec, int local)
{
Uint flags = local ? ERTS_BPF_LOCAL_TRACE : ERTS_BPF_GLOBAL_TRACE;
@@ -503,25 +491,6 @@ erts_set_call_trace_bif(ErtsCodeInfo *ci, Binary *match_spec, int local)
}
void
-erts_set_mtrace_bif(ErtsCodeInfo *ci, Binary *match_spec, ErtsTracer tracer)
-{
- set_function_break(ci, match_spec, ERTS_BPF_META_TRACE, 0, tracer);
-}
-
-void
-erts_set_time_trace_bif(ErtsCodeInfo *ci, enum erts_break_op count_op)
-{
- set_function_break(ci, NULL,
- ERTS_BPF_TIME_TRACE|ERTS_BPF_TIME_TRACE_ACTIVE,
- count_op, erts_tracer_nil);
-}
-
-void
-erts_clear_time_trace_bif(ErtsCodeInfo *ci) {
- clear_function_break(ci, ERTS_BPF_TIME_TRACE|ERTS_BPF_TIME_TRACE_ACTIVE);
-}
-
-void
erts_set_debug_break(BpFunctions* f) {
set_break(f, NULL, ERTS_BPF_DEBUG, 0, erts_tracer_nil);
}
@@ -547,7 +516,7 @@ erts_clear_trace_break(BpFunctions* f)
}
void
-erts_clear_call_trace_bif(ErtsCodeInfo *ci, int local)
+erts_clear_export_trace(ErtsCodeInfo *ci, int local)
{
GenericBp* g = ci->u.gen_bp;
@@ -566,12 +535,6 @@ erts_clear_mtrace_break(BpFunctions* f)
}
void
-erts_clear_mtrace_bif(ErtsCodeInfo *ci)
-{
- clear_function_break(ci, ERTS_BPF_META_TRACE);
-}
-
-void
erts_clear_debug_break(BpFunctions* f)
{
ERTS_LC_ASSERT(erts_thr_progress_is_blocking());
@@ -630,58 +593,56 @@ erts_clear_module_break(Module *modp) {
}
void
-erts_clear_export_break(Module* modp, ErtsCodeInfo *ci)
+erts_clear_export_break(Module* modp, Export *ep)
{
+ ErtsCodeInfo *ci;
+
ERTS_LC_ASSERT(erts_thr_progress_is_blocking());
+ ci = &ep->info;
+
+ ASSERT(erts_codeinfo_to_code(ci) == ep->trampoline.raw);
+
+ ASSERT(BeamIsOpCode(ep->trampoline.op, op_i_generic_breakpoint));
+ ep->trampoline.op = 0;
+
clear_function_break(ci, ERTS_BPF_ALL);
erts_commit_staged_bp();
- *erts_codeinfo_to_code(ci) = (BeamInstr) 0;
+
consolidate_bp_data(modp, ci, 0);
ASSERT(ci->u.gen_bp == NULL);
}
/*
- * If c_p->cp is a trace return instruction, we set cp
- * to be the place where we again start to execute code.
+ * If the topmost continuation pointer on the stack is a trace return
+ * instruction, we modify it to be the place where we again start to
+ * execute code.
*
- * cp is used by match spec {caller} to get the calling
- * function, and if we don't do this fixup it will be
- * 'undefined'. This has the odd side effect of {caller}
- * not really being which function is the caller, but
- * rather which function we are about to return to.
+ * This continuation pointer is used by match spec {caller} to get the
+ * calling function, and if we don't do this fixup it will be
+ * 'undefined'. This has the odd side effect of {caller} not really
+ * being the function which is the caller, but rather the function
+ * which we are about to return to.
*/
static void fixup_cp_before_trace(Process *c_p, int *return_to_trace)
{
- Eterm *cpp, *E = c_p->stop;
- BeamInstr w = *c_p->cp;
- if (BeamIsOpCode(w, op_return_trace)) {
- cpp = &E[2];
- } else if (BeamIsOpCode(w, op_i_return_to_trace)) {
- *return_to_trace = 1;
- cpp = &E[0];
- } else if (BeamIsOpCode(w, op_i_return_time_trace)) {
- cpp = &E[0];
- } else {
- cpp = NULL;
- }
- if (cpp) {
- for (;;) {
- BeamInstr w = *cp_val(*cpp);
- if (BeamIsOpCode(w, op_return_trace)) {
- cpp += 3;
- } else if (BeamIsOpCode(w, op_i_return_to_trace)) {
- *return_to_trace = 1;
- cpp += 1;
- } else if (BeamIsOpCode(w, op_i_return_time_trace)) {
- cpp += 2;
- } else {
- break;
- }
+ Eterm *cpp = c_p->stop;
+
+ for (;;) {
+ BeamInstr w = *cp_val(*cpp);
+ if (BeamIsOpCode(w, op_return_trace)) {
+ cpp += 3;
+ } else if (BeamIsOpCode(w, op_i_return_to_trace)) {
+ *return_to_trace = 1;
+ cpp += 1;
+ } else if (BeamIsOpCode(w, op_i_return_time_trace)) {
+ cpp += 2;
+ } else {
+ break;
}
- c_p->cp = (BeamInstr *) cp_val(*cpp);
- ASSERT(is_CP(*cpp));
}
+ c_p->stop[0] = (Eterm) cp_val(*cpp);
+ ASSERT(is_CP(*cpp));
}
BeamInstr
@@ -743,12 +704,13 @@ erts_generic_breakpoint(Process* c_p, ErtsCodeInfo *info, Eterm* reg)
if (bp_flags & ERTS_BPF_TIME_TRACE_ACTIVE) {
Eterm w;
+ Eterm* E;
erts_trace_time_call(c_p, info, bp->time);
- w = (BeamInstr) *c_p->cp;
+ E = c_p->stop;
+ w = (BeamInstr) E[0];
if (! (BeamIsOpCode(w, op_i_return_time_trace) ||
BeamIsOpCode(w, op_return_trace) ||
BeamIsOpCode(w, op_i_return_to_trace)) ) {
- Eterm* E = c_p->stop;
ASSERT(c_p->htop <= E && E <= c_p->hend);
if (E - 2 < c_p->htop) {
(void) erts_garbage_collect(c_p, 2, reg, info->mfa.arity);
@@ -759,9 +721,8 @@ erts_generic_breakpoint(Process* c_p, ErtsCodeInfo *info, Eterm* reg)
ASSERT(c_p->htop <= E && E <= c_p->hend);
E -= 2;
- E[0] = make_cp(erts_codeinfo_to_code(info));
- E[1] = make_cp(c_p->cp); /* original return address */
- c_p->cp = beam_return_time_trace;
+ E[1] = make_cp(erts_codeinfo_to_code(info));
+ E[0] = (Eterm) beam_return_time_trace;
c_p->stop = E;
}
}
@@ -773,237 +734,24 @@ erts_generic_breakpoint(Process* c_p, ErtsCodeInfo *info, Eterm* reg)
}
}
-/*
- * Entry point called by the trace wrap functions in erl_bif_wrap.c
- *
- * The trace wrap functions are themselves called through the export
- * entries instead of the original BIF functions.
- */
-Eterm
-erts_bif_trace(int bif_index, Process* p, Eterm* args, BeamInstr* I)
-{
- Eterm result;
- Eterm (*func)(Process*, Eterm*, BeamInstr*);
- Export* ep = bif_export[bif_index];
- Uint32 flags = 0, flags_meta = 0;
- ErtsTracer meta_tracer = erts_tracer_nil;
- int applying = (I == ep->beam); /* Yup, the apply code for a bif
- * is actually in the
- * export entry */
- BeamInstr *cp = p->cp;
- GenericBp* g;
- GenericBpData* bp = NULL;
- Uint bp_flags = 0;
- int return_to_trace = 0;
-
- ERTS_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p);
-
- g = ep->info.u.gen_bp;
- if (g) {
- bp = &g->data[erts_active_bp_ix()];
- bp_flags = bp->flags;
- }
-
- /*
- * Make continuation pointer OK, it is not during direct BIF calls,
- * but it is correct during apply of bif.
- */
- if (!applying) {
- p->cp = I;
- } else {
- fixup_cp_before_trace(p, &return_to_trace);
- }
- if (bp_flags & (ERTS_BPF_LOCAL_TRACE|ERTS_BPF_GLOBAL_TRACE) &&
- IS_TRACED_FL(p, F_TRACE_CALLS)) {
- int local = !!(bp_flags & ERTS_BPF_LOCAL_TRACE);
- flags = erts_call_trace(p, &ep->info, bp->local_ms, args,
- local, &ERTS_TRACER(p));
- }
- if (bp_flags & ERTS_BPF_META_TRACE) {
- ErtsTracer old_tracer;
-
- meta_tracer = erts_atomic_read_nob(&bp->meta_tracer->tracer);
- old_tracer = meta_tracer;
- flags_meta = erts_call_trace(p, &ep->info, bp->meta_ms, args,
- 0, &meta_tracer);
-
- if (!ERTS_TRACER_COMPARE(old_tracer, meta_tracer)) {
- ErtsTracer new_tracer = erts_tracer_nil;
- erts_tracer_update(&new_tracer, meta_tracer);
- if (old_tracer == erts_atomic_cmpxchg_acqb(
- &bp->meta_tracer->tracer,
- (erts_aint_t)new_tracer,
- (erts_aint_t)old_tracer)) {
- ERTS_TRACER_CLEAR(&old_tracer);
- } else {
- ERTS_TRACER_CLEAR(&new_tracer);
- }
- }
- }
- if (bp_flags & ERTS_BPF_TIME_TRACE_ACTIVE &&
- IS_TRACED_FL(p, F_TRACE_CALLS)) {
- erts_trace_time_call(p, &ep->info, bp->time);
- }
-
- /* Restore original continuation pointer (if changed). */
- p->cp = cp;
-
- func = bif_table[bif_index].f;
-
- result = func(p, args, I);
-
- if (erts_nif_export_check_save_trace(p, result,
- applying, ep,
- cp, flags,
- flags_meta, I,
- meta_tracer)) {
- /*
- * erts_bif_trace_epilogue() will be called
- * later when appropriate via the NIF export
- * scheduling functionality...
- */
- return result;
- }
-
- return erts_bif_trace_epilogue(p, result, applying, ep, cp,
- flags, flags_meta, I,
- meta_tracer);
-}
-
-Eterm
-erts_bif_trace_epilogue(Process *p, Eterm result, int applying,
- Export* ep, BeamInstr *cp, Uint32 flags,
- Uint32 flags_meta, BeamInstr* I,
- ErtsTracer meta_tracer)
-{
- if (applying && (flags & MATCH_SET_RETURN_TO_TRACE)) {
- BeamInstr i_return_trace = beam_return_trace[0];
- BeamInstr i_return_to_trace = beam_return_to_trace[0];
- BeamInstr i_return_time_trace = beam_return_time_trace[0];
- Eterm *cpp;
- /* Maybe advance cp to skip trace stack frames */
- for (cpp = p->stop; ; cp = cp_val(*cpp++)) {
- if (*cp == i_return_trace) {
- /* Skip stack frame variables */
- while (is_not_CP(*cpp)) cpp++;
- cpp += 2; /* Skip return_trace parameters */
- } else if (*cp == i_return_time_trace) {
- /* Skip stack frame variables */
- while (is_not_CP(*cpp)) cpp++;
- cpp += 1; /* Skip return_time_trace parameters */
- } else if (*cp == i_return_to_trace) {
- /* A return_to trace message is going to be generated
- * by normal means, so we do not have to.
- */
- cp = NULL;
- break;
- } else break;
- }
- }
-
- /* Try to get these in the order
- * they usually appear in normal code... */
- if (is_non_value(result)) {
- Uint reason = p->freason;
- if (reason != TRAP) {
- Eterm class;
- Eterm value = p->fvalue;
- /* Expand error value like in handle_error() */
- if (reason & EXF_ARGLIST) {
- Eterm *tp;
- ASSERT(is_tuple(value));
- tp = tuple_val(value);
- value = tp[1];
- }
- if ((reason & EXF_THROWN) && (p->catches <= 0)) {
- Eterm *hp = HAlloc(p, 3);
- value = TUPLE2(hp, am_nocatch, value);
- reason = EXC_ERROR;
- }
- /* Note: expand_error_value() could theoretically
- * allocate on the heap, but not for any error
- * returned by a BIF, and it would do no harm,
- * just be annoying.
- */
- value = expand_error_value(p, reason, value);
- class = exception_tag[GET_EXC_CLASS(reason)];
-
- if (flags_meta & MATCH_SET_EXCEPTION_TRACE) {
- erts_trace_exception(p, &ep->info.mfa, class, value,
- &meta_tracer);
- }
- if (flags & MATCH_SET_EXCEPTION_TRACE) {
- erts_trace_exception(p, &ep->info.mfa, class, value,
- &ERTS_TRACER(p));
- }
- if ((flags & MATCH_SET_RETURN_TO_TRACE) && p->catches > 0) {
- /* can only happen if(local)*/
- Eterm *ptr = p->stop;
- ASSERT(is_CP(*ptr));
- ASSERT(ptr <= STACK_START(p));
- /* Search the nearest stack frame for a catch */
- while (++ptr < STACK_START(p)) {
- if (is_CP(*ptr)) break;
- if (is_catch(*ptr)) {
- if (applying) {
- /* Apply of BIF, cp is in calling function */
- if (cp) erts_trace_return_to(p, cp);
- } else {
- /* Direct bif call, I points into
- * calling function */
- erts_trace_return_to(p, I);
- }
- }
- }
- }
- if ((flags_meta|flags) & MATCH_SET_EXCEPTION_TRACE) {
- erts_proc_lock(p, ERTS_PROC_LOCKS_ALL_MINOR);
- ERTS_TRACE_FLAGS(p) |= F_EXCEPTION_TRACE;
- erts_proc_unlock(p, ERTS_PROC_LOCKS_ALL_MINOR);
- }
- }
- } else {
- if (flags_meta & MATCH_SET_RX_TRACE) {
- erts_trace_return(p, &ep->info.mfa, result, &meta_tracer);
- }
- /* MATCH_SET_RETURN_TO_TRACE cannot occur if(meta) */
- if (flags & MATCH_SET_RX_TRACE) {
- erts_trace_return(p, &ep->info.mfa, result, &ERTS_TRACER(p));
- }
- if (flags & MATCH_SET_RETURN_TO_TRACE &&
- IS_TRACED_FL(p, F_TRACE_RETURN_TO)) {
- /* can only happen if(local)*/
- if (applying) {
- /* Apply of BIF, cp is in calling function */
- if (cp) erts_trace_return_to(p, cp);
- } else {
- /* Direct bif call, I points into calling function */
- erts_trace_return_to(p, I);
- }
- }
- }
- ERTS_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p);
- return result;
-}
-
static ErtsTracer
do_call_trace(Process* c_p, ErtsCodeInfo* info, Eterm* reg,
int local, Binary* ms, ErtsTracer tracer)
{
int return_to_trace = 0;
- BeamInstr *cp_save = c_p->cp;
Uint32 flags;
Uint need = 0;
+ Eterm cp_save;
Eterm* E = c_p->stop;
- fixup_cp_before_trace(c_p, &return_to_trace);
+ cp_save = E[0];
+ fixup_cp_before_trace(c_p, &return_to_trace);
ERTS_UNREQ_PROC_MAIN_LOCK(c_p);
flags = erts_call_trace(c_p, info, ms, reg, local, &tracer);
ERTS_REQ_PROC_MAIN_LOCK(c_p);
- /* restore cp after potential fixup */
- c_p->cp = cp_save;
+ E[0] = cp_save;
ASSERT(!ERTS_PROC_IS_EXITING(c_p));
if ((flags & MATCH_SET_RETURN_TO_TRACE) && !return_to_trace) {
@@ -1023,28 +771,23 @@ do_call_trace(Process* c_p, ErtsCodeInfo* info, Eterm* reg,
if (flags & MATCH_SET_RETURN_TO_TRACE && !return_to_trace) {
E -= 1;
ASSERT(c_p->htop <= E && E <= c_p->hend);
- E[0] = make_cp(c_p->cp);
- c_p->cp = beam_return_to_trace;
+ E[0] = (Eterm) beam_return_to_trace;
+ c_p->stop = E;
}
- if (flags & MATCH_SET_RX_TRACE)
- {
+ if (flags & MATCH_SET_RX_TRACE) {
E -= 3;
c_p->stop = E;
ASSERT(c_p->htop <= E && E <= c_p->hend);
ASSERT(is_CP((Eterm) (UWord) (&info->mfa.module)));
ASSERT(IS_TRACER_VALID(tracer));
- E[2] = make_cp(c_p->cp);
- E[1] = copy_object(tracer, c_p);
- E[0] = make_cp(&info->mfa.module);
- /* We ARE at the beginning of an instruction,
- the funcinfo is above i. */
- c_p->cp = (flags & MATCH_SET_EXCEPTION_TRACE) ?
- beam_exception_trace : beam_return_trace;
+ E[2] = copy_object(tracer, c_p);
+ E[1] = make_cp(&info->mfa.module);
+ E[0] = (Eterm) ((flags & MATCH_SET_EXCEPTION_TRACE) ?
+ beam_exception_trace : beam_return_trace);
erts_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
ERTS_TRACE_FLAGS(c_p) |= F_EXCEPTION_TRACE;
erts_proc_unlock(c_p, ERTS_PROC_LOCKS_ALL_MINOR);
- } else
- c_p->stop = E;
+ }
return tracer;
}
diff --git a/erts/emulator/beam/beam_bp.h b/erts/emulator/beam/beam_bp.h
index a64765822b..54e84e7e4f 100644
--- a/erts/emulator/beam/beam_bp.h
+++ b/erts/emulator/beam/beam_bp.h
@@ -119,20 +119,16 @@ void erts_bp_free_matched_functions(BpFunctions* f);
void erts_install_breakpoints(BpFunctions* f);
void erts_uninstall_breakpoints(BpFunctions* f);
void erts_consolidate_bp_data(BpFunctions* f, int local);
-void erts_consolidate_bif_bp_data(void);
void erts_set_trace_break(BpFunctions *f, Binary *match_spec);
void erts_clear_trace_break(BpFunctions *f);
-void erts_set_call_trace_bif(ErtsCodeInfo *ci, Binary *match_spec, int local);
-void erts_clear_call_trace_bif(ErtsCodeInfo *ci, int local);
+void erts_set_export_trace(ErtsCodeInfo *ci, Binary *match_spec, int local);
+void erts_clear_export_trace(ErtsCodeInfo *ci, int local);
void erts_set_mtrace_break(BpFunctions *f, Binary *match_spec,
ErtsTracer tracer);
void erts_clear_mtrace_break(BpFunctions *f);
-void erts_set_mtrace_bif(ErtsCodeInfo *ci, Binary *match_spec,
- ErtsTracer tracer);
-void erts_clear_mtrace_bif(ErtsCodeInfo *ci);
void erts_set_debug_break(BpFunctions *f);
void erts_clear_debug_break(BpFunctions *f);
@@ -142,7 +138,7 @@ void erts_clear_count_break(BpFunctions *f);
void erts_clear_all_breaks(BpFunctions* f);
int erts_clear_module_break(Module *modp);
-void erts_clear_export_break(Module *modp, ErtsCodeInfo* ci);
+void erts_clear_export_break(Module *modp, Export *ep);
BeamInstr erts_generic_breakpoint(Process* c_p, ErtsCodeInfo *ci, Eterm* reg);
BeamInstr erts_trace_break(Process *p, ErtsCodeInfo *ci, Eterm *args,
@@ -151,8 +147,6 @@ BeamInstr erts_trace_break(Process *p, ErtsCodeInfo *ci, Eterm *args,
int erts_is_trace_break(ErtsCodeInfo *ci, Binary **match_spec_ret, int local);
int erts_is_mtrace_break(ErtsCodeInfo *ci, Binary **match_spec_ret,
ErtsTracer *tracer_ret);
-int erts_is_mtrace_bif(ErtsCodeInfo *ci, Binary **match_spec_ret,
- ErtsTracer *tracer_ret);
int erts_is_native_break(ErtsCodeInfo *ci);
int erts_is_count_break(ErtsCodeInfo *ci, Uint *count_ret);
int erts_is_time_break(Process *p, ErtsCodeInfo *ci, Eterm *call_time);
@@ -163,10 +157,6 @@ void erts_schedule_time_break(Process *p, Uint out);
void erts_set_time_break(BpFunctions *f, enum erts_break_op);
void erts_clear_time_break(BpFunctions *f);
-int erts_is_time_trace_bif(Process *p, ErtsCodeInfo *ci, Eterm *call_time);
-void erts_set_time_trace_bif(ErtsCodeInfo *ci, enum erts_break_op);
-void erts_clear_time_trace_bif(ErtsCodeInfo *ci);
-
ErtsCodeInfo *erts_find_local_func(ErtsCodeMFA *mfa);
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
diff --git a/erts/emulator/beam/beam_debug.c b/erts/emulator/beam/beam_debug.c
index 4d52435139..6a9a6b7dc9 100644
--- a/erts/emulator/beam/beam_debug.c
+++ b/erts/emulator/beam/beam_debug.c
@@ -332,7 +332,7 @@ erts_debug_disassemble_1(BIF_ALIST_1)
"unknown " HEXF "\n", instr);
code_ptr++;
}
- if (i == op_call_nif) {
+ if (i == op_call_nif_WWW) {
/*
* The rest of the code will not be executed. Don't disassemble any
* more code in this function.
diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c
index 07c16e3415..e8a0c666e3 100644
--- a/erts/emulator/beam/beam_emu.c
+++ b/erts/emulator/beam/beam_emu.c
@@ -111,10 +111,9 @@ do { \
#define CHECK_ALIGNED(Dst) ASSERT((((Uint)&Dst) & (sizeof(Uint)-1)) == 0)
-#define GET_BIF_MODULE(p) (p->info.mfa.module)
-#define GET_BIF_FUNCTION(p) (p->info.mfa.function)
-#define GET_BIF_ARITY(p) (p->info.mfa.arity)
-#define GET_BIF_ADDRESS(p) ((BifFunction) (p->beam[1]))
+#define GET_EXPORT_MODULE(p) ((p)->info.mfa.module)
+#define GET_EXPORT_FUNCTION(p) ((p)->info.mfa.function)
+#define GET_EXPORT_ARITY(p) ((p)->info.mfa.arity)
#define TermWords(t) (((t) / (sizeof(BeamInstr)/sizeof(Eterm))) + !!((t) % (sizeof(BeamInstr)/sizeof(Eterm))))
@@ -141,10 +140,6 @@ do { \
BeamCodeAddr(IP) < (BeamInstr)LabelAddr(end_emulator_loop))
#endif /* NO_JUMP_TABLE */
-#define SET_CP(p, ip) \
- ASSERT(VALID_INSTR(*(ip))); \
- (p)->cp = (ip)
-
#define SET_I(ip) \
ASSERT(VALID_INSTR(* (Eterm *)(ip))); \
I = (ip)
@@ -254,72 +249,6 @@ void** beam_ops;
#define Q(N) (N*sizeof(Eterm *))
#define l(N) (freg[N].fd)
-/*
- * Check that we haven't used the reductions and jump to function pointed to by
- * the I register. If we are out of reductions, do a context switch.
- */
-
-#define DispatchMacro() \
- do { \
- BeamInstr dis_next; \
- dis_next = *I; \
- CHECK_ARGS(I); \
- if (FCALLS > 0 || FCALLS > neg_o_reds) { \
- FCALLS--; \
- Goto(dis_next); \
- } else { \
- goto context_switch; \
- } \
- } while (0) \
-
-#define DispatchMacroFun() \
- do { \
- BeamInstr dis_next; \
- dis_next = *I; \
- CHECK_ARGS(I); \
- if (FCALLS > 0 || FCALLS > neg_o_reds) { \
- FCALLS--; \
- Goto(dis_next); \
- } else { \
- goto context_switch_fun; \
- } \
- } while (0)
-
-#define DispatchMacrox() \
- do { \
- if (FCALLS > 0) { \
- BeamInstr dis_next; \
- SET_I(((Export *) Arg(0))->addressv[erts_active_code_ix()]); \
- dis_next = *I; \
- FCALLS--; \
- CHECK_ARGS(I); \
- Goto(dis_next); \
- } else if (ERTS_PROC_GET_SAVED_CALLS_BUF(c_p) \
- && FCALLS > neg_o_reds) { \
- goto save_calls1; \
- } else { \
- SET_I(((Export *) Arg(0))->addressv[erts_active_code_ix()]); \
- CHECK_ARGS(I); \
- goto context_switch; \
- } \
- } while (0)
-
-#ifdef DEBUG
-/*
- * To simplify breakpoint setting, put the code in one place only and jump to it.
- */
-# define Dispatch() goto do_dispatch
-# define Dispatchx() goto do_dispatchx
-# define Dispatchfun() goto do_dispatchfun
-#else
-/*
- * Inline for speed.
- */
-# define Dispatch() DispatchMacro()
-# define Dispatchx() DispatchMacrox()
-# define Dispatchfun() DispatchMacroFun()
-#endif
-
#define Arg(N) I[(N)+1]
#define GetSource(raw, dst) \
@@ -352,19 +281,6 @@ do { \
} \
} while(0)
-#define DispatchReturn \
-do { \
- if (FCALLS > 0 || FCALLS > neg_o_reds) { \
- FCALLS--; \
- Goto(*I); \
- } \
- else { \
- c_p->current = NULL; \
- c_p->arity = 1; \
- goto context_switch3; \
- } \
-} while (0)
-
#ifdef DEBUG
/* Better static type testing by the C compiler */
# define BEAM_IS_TUPLE(Src) is_tuple(Src)
@@ -524,7 +440,7 @@ init_emulator(void)
#define DTRACE_RETURN_FROM_PC(p) \
do { \
ErtsCodeMFA* cmfa; \
- if (DTRACE_ENABLED(function_return) && (cmfa = find_function_from_pc((p)->cp))) { \
+ if (DTRACE_ENABLED(function_return) && (cmfa = find_function_from_pc(cp_val((p)->stop[0])))) { \
DTRACE_RETURN((p), cmfa); \
} \
} while(0)
@@ -772,27 +688,9 @@ void process_main(Eterm * x_reg_array, FloatDef* f_reg_array)
#endif
#include "beam_hot.h"
-
-#ifdef DEBUG
/*
- * Set a breakpoint here to get control just after a call instruction.
- * I points to the first instruction in the called function.
- *
- * In gdb, use 'call dis(I-5, 1)' to show the name of the function.
- */
- do_dispatch:
- DispatchMacro();
-
- do_dispatchx:
- DispatchMacrox();
-
- do_dispatchfun:
- DispatchMacroFun();
-
-#endif
-
- /*
- * Jumped to from the Dispatch() macro when the reductions are used up.
+ * The labels are jumped to from the $DISPATCH() macros when the reductions
+ * are used up.
*
* Since the I register points just beyond the FuncBegin instruction, we
* can get the module, function, and arity for the function being
@@ -986,18 +884,42 @@ void process_main(Eterm * x_reg_array, FloatDef* f_reg_array)
}
#endif
return; /* Never executed */
+}
- save_calls1:
- {
- BeamInstr dis_next;
+/*
+ * Enter all BIFs into the export table.
+ *
+ * Note that they will all call the error_handler until their modules have been
+ * loaded, which may prevent the system from booting if BIFs from non-preloaded
+ * modules are apply/3'd while loading code. Ordinary BIF calls will work fine
+ * however since they won't go through export entries.
+ */
+static void install_bifs(void) {
+ int i;
+
+ for (i = 0; i < BIF_SIZE; i++) {
+ BifEntry *entry;
+ Export *ep;
+ int j;
- save_calls(c_p, (Export *) Arg(0));
+ entry = &bif_table[i];
- SET_I(((Export *) Arg(0))->addressv[erts_active_code_ix()]);
+ ep = erts_export_put(entry->module, entry->name, entry->arity);
- dis_next = *I;
- FCALLS--;
- Goto(dis_next);
+ ep->info.op = BeamOpCodeAddr(op_i_func_info_IaaI);
+ ep->info.mfa.module = entry->module;
+ ep->info.mfa.function = entry->name;
+ ep->info.mfa.arity = entry->arity;
+ ep->bif_table_index = i;
+
+ memset(&ep->trampoline, 0, sizeof(ep->trampoline));
+ ep->trampoline.op = BeamOpCodeAddr(op_call_error_handler);
+
+ for (j = 0; j < ERTS_NUM_CODE_IX; j++) {
+ ep->addressv[j] = ep->trampoline.raw;
+ }
+
+ bif_export[i] = ep;
}
}
@@ -1008,43 +930,30 @@ void process_main(Eterm * x_reg_array, FloatDef* f_reg_array)
static void
init_emulator_finish(void)
{
- int i;
- Export* ep;
-
#if defined(ARCH_64) && defined(CODE_MODEL_SMALL)
- for (i = 0; i < NUMBER_OF_OPCODES; i++) {
- BeamInstr instr = BeamOpCodeAddr(i);
- if (instr >= (1ull << 32)) {
- erts_exit(ERTS_ERROR_EXIT,
- "This run-time was supposed be compiled with all code below 2Gb,\n"
- "but the instruction '%s' is located at %016lx.\n",
- opc[i].name, instr);
- }
- }
+ int i;
+
+ for (i = 0; i < NUMBER_OF_OPCODES; i++) {
+ BeamInstr instr = BeamOpCodeAddr(i);
+ if (instr >= (1ull << 32)) {
+ erts_exit(ERTS_ERROR_EXIT,
+ "This run-time was supposed be compiled with all code below 2Gb,\n"
+ "but the instruction '%s' is located at %016lx.\n",
+ opc[i].name, instr);
+ }
+ }
#endif
- beam_apply[0] = BeamOpCodeAddr(op_i_apply);
- beam_apply[1] = BeamOpCodeAddr(op_normal_exit);
- beam_exit[0] = BeamOpCodeAddr(op_error_action_code);
- beam_continue_exit[0] = BeamOpCodeAddr(op_continue_exit);
- beam_return_to_trace[0] = BeamOpCodeAddr(op_i_return_to_trace);
- beam_return_trace[0] = BeamOpCodeAddr(op_return_trace);
- beam_exception_trace[0] = BeamOpCodeAddr(op_return_trace); /* UGLY */
- beam_return_time_trace[0] = BeamOpCodeAddr(op_i_return_time_trace);
+ beam_apply[0] = BeamOpCodeAddr(op_i_apply);
+ beam_apply[1] = BeamOpCodeAddr(op_normal_exit);
+ beam_exit[0] = BeamOpCodeAddr(op_error_action_code);
+ beam_continue_exit[0] = BeamOpCodeAddr(op_continue_exit);
+ beam_return_to_trace[0] = BeamOpCodeAddr(op_i_return_to_trace);
+ beam_return_trace[0] = BeamOpCodeAddr(op_return_trace);
+ beam_exception_trace[0] = BeamOpCodeAddr(op_return_trace); /* UGLY */
+ beam_return_time_trace[0] = BeamOpCodeAddr(op_i_return_time_trace);
- /*
- * Enter all BIFs into the export table.
- */
- for (i = 0; i < BIF_SIZE; i++) {
- ep = erts_export_put(bif_table[i].module,
- bif_table[i].name,
- bif_table[i].arity);
- bif_export[i] = ep;
- ep->beam[0] = BeamOpCodeAddr(op_apply_bif);
- ep->beam[1] = (BeamInstr) bif_table[i].f;
- /* XXX: set func info for bifs */
- ep->info.op = BeamOpCodeAddr(op_i_func_info_IaaI);
- }
+ install_bifs();
}
/*
@@ -1257,7 +1166,7 @@ void erts_dirty_process_main(ErtsSchedulerData *esdp)
* I[2]: Pointer to erl_module_nif
* I[3]: Function pointer to dirty NIF
*
- * This layout is determined by the NifExport struct
+ * This layout is determined by the ErtsNativeFunc struct
*/
ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_NIF);
@@ -1271,11 +1180,11 @@ void erts_dirty_process_main(ErtsSchedulerData *esdp)
ERTS_UNREQ_PROC_MAIN_LOCK(c_p);
ASSERT(!ERTS_PROC_IS_EXITING(c_p));
- if (BeamIsOpCode(*I, op_apply_bif)) {
+ if (BeamIsOpCode(*I, op_call_bif_W)) {
exiting = erts_call_dirty_bif(esdp, c_p, I, reg);
}
else {
- ASSERT(BeamIsOpCode(*I, op_call_nif));
+ ASSERT(BeamIsOpCode(*I, op_call_nif_WWW));
exiting = erts_call_dirty_nif(esdp, c_p, I, reg);
}
@@ -1344,6 +1253,33 @@ Eterm error_atom[NUMBER_EXIT_CODES] = {
am_badkey, /* 19 */
};
+/* Returns the return address at E[0] in printable form, skipping tracing in
+ * the same manner as gather_stacktrace.
+ *
+ * This is needed to generate correct stacktraces when throwing errors from
+ * instructions that return like an ordinary function, such as call_nif. */
+BeamInstr *erts_printable_return_address(Process* p, Eterm *E) {
+ Eterm *ptr = E;
+
+ ASSERT(is_CP(*ptr));
+
+ while (ptr < STACK_START(p)) {
+ BeamInstr *cp = cp_val(*ptr);
+
+ if (cp == beam_exception_trace || cp == beam_return_trace) {
+ ptr += 3;
+ } else if (cp == beam_return_time_trace) {
+ ptr += 2;
+ } else if (cp == beam_return_to_trace) {
+ ptr += 1;
+ } else {
+ return cp;
+ }
+ }
+
+ ERTS_ASSERT(!"No continuation pointer on stack");
+}
+
/*
* To fully understand the error handling, one must keep in mind that
* when an exception is thrown, the search for a handler can jump back
@@ -1373,14 +1309,14 @@ handle_error(Process* c_p, BeamInstr* pc, Eterm* reg, ErtsCodeMFA *bif_mfa)
ASSERT(c_p->freason != TRAP); /* Should have been handled earlier. */
- if (c_p->freason & EXF_RESTORE_NIF)
- erts_nif_export_restore_error(c_p, &pc, reg, &bif_mfa);
+ if (c_p->freason & EXF_RESTORE_NFUNC)
+ erts_nfunc_restore_error(c_p, &pc, reg, &bif_mfa);
#ifdef DEBUG
if (bif_mfa) {
- /* Verify that bif_mfa does not point into our nif export */
- NifExport *nep = ERTS_PROC_GET_NIF_TRAP_EXPORT(c_p);
- ASSERT(!nep || !ErtsInArea(bif_mfa, (char *)nep, sizeof(NifExport)));
+ /* Verify that bif_mfa does not point into our native function wrapper */
+ ErtsNativeFunc *nep = ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(c_p);
+ ASSERT(!nep || !ErtsInArea(bif_mfa, (char *)nep, sizeof(ErtsNativeFunc)));
}
#endif
@@ -1443,7 +1379,7 @@ handle_error(Process* c_p, BeamInstr* pc, Eterm* reg, ErtsCodeMFA *bif_mfa)
reg[2] = Value;
reg[3] = c_p->ftrace;
if ((new_pc = next_catch(c_p, reg))) {
- c_p->cp = 0; /* To avoid keeping stale references. */
+ c_p->stop[0] = NIL; /* To avoid keeping stale references. */
ERTS_RECV_MARK_CLEAR(c_p); /* No longer safe to use this position */
return new_pc;
}
@@ -1481,35 +1417,6 @@ next_catch(Process* c_p, Eterm *reg) {
return NULL;
}
- /*
- * Better safe than sorry here. In debug builds, produce a core
- * dump if the top of the stack doesn't point to a continuation
- * pointer. In other builds, ignore a non-CP at the top of stack.
- */
- ASSERT(is_CP(*ptr));
- if ((is_not_CP(*ptr) || (*cp_val(*ptr) != i_return_trace &&
- *cp_val(*ptr) != i_return_to_trace &&
- *cp_val(*ptr) != i_return_time_trace ))
- && c_p->cp) {
- /* Can not follow cp here - code may be unloaded */
- BeamInstr *cpp = c_p->cp;
- if (cpp == beam_exception_trace) {
- ErtsCodeMFA *mfa = (ErtsCodeMFA*)cp_val(ptr[0]);
- erts_trace_exception(c_p, mfa,
- reg[1], reg[2],
- ERTS_TRACER_FROM_ETERM(ptr+1));
- /* Skip return_trace parameters */
- ptr += 2;
- } else if (cpp == beam_return_trace) {
- /* Skip return_trace parameters */
- ptr += 2;
- } else if (cpp == beam_return_time_trace) {
- /* Skip return_trace parameters */
- ptr += 1;
- } else if (cpp == beam_return_to_trace) {
- have_return_to_trace = !0; /* Record next cp */
- }
- }
while (ptr < STACK_START(c_p)) {
if (is_catch(*ptr)) {
if (active_catches) goto found_catch;
@@ -1664,6 +1571,54 @@ expand_error_value(Process* c_p, Uint freason, Eterm Value) {
return Value;
}
+
+static void
+gather_stacktrace(Process* p, struct StackTrace* s, int depth)
+{
+ BeamInstr *prev;
+ Eterm *ptr;
+
+ if (depth == 0) {
+ return;
+ }
+
+ prev = s->depth ? s->trace[s->depth - 1] : s->pc;
+ ptr = p->stop;
+
+ /*
+ * Traverse the stack backwards and add all unique continuation
+ * pointers to the buffer, up to the maximum stack trace size.
+ *
+ * Skip trace stack frames.
+ */
+
+ ASSERT(ptr >= STACK_TOP(p) && ptr <= STACK_START(p));
+
+ while (ptr < STACK_START(p) && depth > 0) {
+ if (is_CP(*ptr)) {
+ BeamInstr *cp = cp_val(*ptr);
+
+ if (cp == beam_exception_trace || cp == beam_return_trace) {
+ ptr += 3;
+ } else if (cp == beam_return_time_trace) {
+ ptr += 2;
+ } else if (cp == beam_return_to_trace) {
+ ptr += 1;
+ } else {
+ if (cp != prev) {
+ /* Record non-duplicates only */
+ prev = cp;
+ s->trace[s->depth++] = cp - 1;
+ depth--;
+ }
+ ptr++;
+ }
+ } else {
+ ptr++;
+ }
+ }
+}
+
/*
* Quick-saving the stack trace in an internal form on the heap. Note
* that c_p->ftrace will point to a cons cell which holds the given args
@@ -1750,11 +1705,6 @@ save_stacktrace(Process* c_p, BeamInstr* pc, Eterm* reg,
s->trace[s->depth++] = pc;
depth--;
}
- /* Save second stack entry if CP is valid and different from pc */
- if (depth > 0 && c_p->cp != 0 && c_p->cp != pc) {
- s->trace[s->depth++] = c_p->cp - 1;
- depth--;
- }
s->pc = NULL;
args = make_arglist(c_p, reg, bif_mfa->arity); /* Overwrite CAR(c_p->ftrace) */
} else {
@@ -1762,9 +1712,9 @@ save_stacktrace(Process* c_p, BeamInstr* pc, Eterm* reg,
non_bif_stacktrace:
s->current = c_p->current;
- /*
+ /*
* For a function_clause error, the arguments are in the beam
- * registers, c_p->cp is valid, and c_p->current is set.
+ * registers and c_p->current is set.
*/
if ( (GET_EXC_INDEX(s->freason)) ==
(GET_EXC_INDEX(EXC_FUNCTION_CLAUSE)) ) {
@@ -1772,18 +1722,8 @@ save_stacktrace(Process* c_p, BeamInstr* pc, Eterm* reg,
ASSERT(s->current);
a = s->current->arity;
args = make_arglist(c_p, reg, a); /* Overwrite CAR(c_p->ftrace) */
- /* Save first stack entry */
- ASSERT(c_p->cp);
- if (depth > 0) {
- s->trace[s->depth++] = c_p->cp - 1;
- depth--;
- }
s->pc = NULL; /* Ignore pc */
} else {
- if (depth > 0 && c_p->cp != 0 && c_p->cp != pc) {
- s->trace[s->depth++] = c_p->cp - 1;
- depth--;
- }
s->pc = pc;
}
}
@@ -1796,80 +1736,13 @@ save_stacktrace(Process* c_p, BeamInstr* pc, Eterm* reg,
}
/* Save the actual stack trace */
- erts_save_stacktrace(c_p, s, depth);
+ gather_stacktrace(c_p, s, depth);
}
void
erts_save_stacktrace(Process* p, struct StackTrace* s, int depth)
{
- if (depth > 0) {
- Eterm *ptr;
- BeamInstr *prev = s->depth ? s->trace[s->depth-1] : NULL;
- BeamInstr i_return_trace = beam_return_trace[0];
- BeamInstr i_return_to_trace = beam_return_to_trace[0];
-
- /*
- * Traverse the stack backwards and add all unique continuation
- * pointers to the buffer, up to the maximum stack trace size.
- *
- * Skip trace stack frames.
- */
- ptr = p->stop;
- if (ptr < STACK_START(p) &&
- (is_not_CP(*ptr)|| (*cp_val(*ptr) != i_return_trace &&
- *cp_val(*ptr) != i_return_to_trace)) &&
- p->cp) {
- /* Cannot follow cp here - code may be unloaded */
- BeamInstr *cpp = p->cp;
- int trace_cp;
- if (cpp == beam_exception_trace || cpp == beam_return_trace) {
- /* Skip return_trace parameters */
- ptr += 2;
- trace_cp = 1;
- } else if (cpp == beam_return_to_trace) {
- /* Skip return_to_trace parameters */
- ptr += 1;
- trace_cp = 1;
- }
- else {
- trace_cp = 0;
- }
- if (trace_cp && s->pc == cpp) {
- /*
- * If process 'cp' points to a return/exception trace
- * instruction and 'cp' has been saved as 'pc' in
- * stacktrace, we need to update 'pc' in stacktrace
- * with the actual 'cp' located on the top of the
- * stack; otherwise, we will lose the top stackframe
- * when building the stack trace.
- */
- ASSERT(is_CP(p->stop[0]));
- s->pc = cp_val(p->stop[0]);
- }
- }
- while (ptr < STACK_START(p) && depth > 0) {
- if (is_CP(*ptr)) {
- if (*cp_val(*ptr) == i_return_trace) {
- /* Skip stack frame variables */
- do ++ptr; while (is_not_CP(*ptr));
- /* Skip return_trace parameters */
- ptr += 2;
- } else if (*cp_val(*ptr) == i_return_to_trace) {
- /* Skip stack frame variables */
- do ++ptr; while (is_not_CP(*ptr));
- } else {
- BeamInstr *cp = cp_val(*ptr);
- if (cp != prev) {
- /* Record non-duplicates only */
- prev = cp;
- s->trace[s->depth++] = cp - 1;
- depth--;
- }
- ptr++;
- }
- } else ptr++;
- }
- }
+ gather_stacktrace(p, s, depth);
}
/*
@@ -2128,95 +2001,66 @@ apply_bif_error_adjustment(Process *p, Export *ep,
Eterm *reg, Uint arity,
BeamInstr *I, Uint stack_offset)
{
+ int apply_only;
+ Uint need;
+
+ need = stack_offset /* bytes */ / sizeof(Eterm);
+ apply_only = stack_offset == 0;
+
/*
* I is only set when the apply is a tail call, i.e.,
* from the instructions i_apply_only, i_apply_last_P,
* and apply_last_IP.
*/
- if (I
- && BeamIsOpCode(ep->beam[0], op_apply_bif)
- && (ep == bif_export[BIF_error_1]
- || ep == bif_export[BIF_error_2]
- || ep == bif_export[BIF_exit_1]
- || ep == bif_export[BIF_throw_1])) {
- /*
- * We are about to tail apply one of the BIFs
- * erlang:error/1, erlang:error/2, erlang:exit/1,
- * or erlang:throw/1. Error handling of these BIFs is
- * special!
- *
- * We need 'p->cp' to point into the calling
- * function when handling the error after the BIF has
- * been applied. This in order to get the topmost
- * stackframe correct. Without the following adjustment,
- * 'p->cp' will point into the function that called
- * current function when handling the error. We add a
- * dummy stackframe in order to achieve this.
- *
- * Note that these BIFs unconditionally will cause
- * an exception to be raised. That is, our modifications
- * of 'p->cp' as well as the stack will be corrected by
- * the error handling code.
- *
- * If we find an exception/return-to trace continuation
- * pointer as the topmost continuation pointer, we do not
- * need to do anything since the information already will
- * be available for generation of the stacktrace.
- */
- int apply_only = stack_offset == 0;
- BeamInstr *cpp;
+ if (!(I && (ep == bif_export[BIF_error_1] ||
+ ep == bif_export[BIF_error_2] ||
+ ep == bif_export[BIF_exit_1] ||
+ ep == bif_export[BIF_throw_1]))) {
+ return;
+ }
- if (apply_only) {
- ASSERT(p->cp != NULL);
- cpp = p->cp;
- }
- else {
- ASSERT(is_CP(p->stop[0]));
- cpp = cp_val(p->stop[0]);
- }
+ /*
+ * We are about to tail apply one of the BIFs erlang:error/1,
+ * erlang:error/2, erlang:exit/1, or erlang:throw/1. Error handling of
+ * these BIFs is special!
+ *
+ * We need the topmost continuation pointer to point into the calling
+ * function when handling the error after the BIF has been applied. This in
+ * order to get the topmost stackframe correct.
+ *
+ * Note that these BIFs will unconditionally cause an exception to be
+ * raised. That is, our modifications of the stack will be corrected by the
+ * error handling code.
+ */
+ if (need == 0) {
+ need = 1; /* i_apply_only */
+ }
- if (cpp != beam_exception_trace
- && cpp != beam_return_trace
- && cpp != beam_return_to_trace) {
- Uint need = stack_offset /* bytes */ / sizeof(Eterm);
- if (need == 0)
- need = 1; /* i_apply_only */
- if (p->stop - p->htop < need)
- erts_garbage_collect(p, (int) need, reg, arity+1);
- p->stop -= need;
-
- if (apply_only) {
- /*
- * Called from the i_apply_only instruction.
- *
- * 'p->cp' contains continuation pointer pointing
- * into the function that called current function.
- * We push that continuation pointer onto the stack,
- * and set 'p->cp' to point into current function.
- */
+ if (p->stop - p->htop < need) {
+ erts_garbage_collect(p, (int) need, reg, arity+1);
+ }
- p->stop[0] = make_cp(p->cp);
- p->cp = I;
- }
- else {
- /*
- * Called from an i_apply_last_p, or apply_last_IP,
- * instruction.
- *
- * Calling instruction will after we return read
- * a continuation pointer from the stack and write
- * it to 'p->cp', and then remove the topmost
- * stackframe of size 'stack_offset'.
- *
- * We have sized the dummy-stackframe so that it
- * will be removed by the instruction we currently
- * are executing, and leave the stackframe that
- * normally would have been removed intact.
- *
- */
- p->stop[0] = make_cp(I);
- }
- }
+ if (apply_only) {
+ /*
+ * Called from the i_apply_only instruction.
+ *
+ * Push the continuation pointer for the current function to the stack.
+ */
+ p->stop -= need;
+ p->stop[0] = make_cp(I);
+ } else {
+ /*
+ * Called from an i_apply_last_* instruction.
+ *
+ * The calling instruction will deallocate a stack frame of size
+ * 'stack_offset'.
+ *
+ * Push the continuation pointer for the current function to the stack,
+ * and then add a dummy stackframe for the i_apply_last* instruction
+ * to discard.
+ */
+ p->stop[0] = make_cp(I);
+ p->stop -= need;
}
}
@@ -2437,10 +2281,10 @@ erts_hibernate(Process* c_p, Eterm* reg)
c_p->arg_reg[0] = module;
c_p->arg_reg[1] = function;
c_p->arg_reg[2] = args;
- c_p->stop = STACK_START(c_p);
+ c_p->stop = c_p->hend - 1; /* Keep first continuation pointer */
+ ASSERT(c_p->stop[0] == make_cp(beam_apply+1));
c_p->catches = 0;
c_p->i = beam_apply;
- c_p->cp = (BeamInstr *) beam_apply+1;
/*
* If there are no waiting messages, garbage collect and
@@ -3268,10 +3112,10 @@ erts_is_builtin(Eterm Mod, Eterm Name, int arity)
e.info.mfa.arity = arity;
if ((ep = export_get(&e)) == NULL) {
- return 0;
+ return 0;
}
- return ep->addressv[erts_active_code_ix()] == ep->beam &&
- BeamIsOpCode(ep->beam[0], op_apply_bif);
+
+ return ep->bif_table_index != -1;
}
diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c
index 3d5683f19f..8fe046095f 100644
--- a/erts/emulator/beam/beam_load.c
+++ b/erts/emulator/beam/beam_load.c
@@ -141,7 +141,7 @@ typedef struct {
* eventually patch with a pointer into
* the export entry.
*/
- BifFunction bf; /* Pointer to BIF function if BIF;
+ Export *bif; /* Pointer to export entry if BIF;
* NULL otherwise.
*/
} ImportEntry;
@@ -845,17 +845,23 @@ erts_finish_loading(Binary* magic, Process* c_p,
if (ep == NULL || ep->info.mfa.module != module) {
continue;
}
- if (ep->addressv[code_ix] == ep->beam) {
- if (BeamIsOpCode(ep->beam[0], op_apply_bif)) {
- continue;
- } else if (BeamIsOpCode(ep->beam[0], op_i_generic_breakpoint)) {
+
+ DBG_CHECK_EXPORT(ep, code_ix);
+
+ if (ep->addressv[code_ix] == ep->trampoline.raw) {
+ if (BeamIsOpCode(ep->trampoline.op, op_i_generic_breakpoint)) {
ERTS_LC_ASSERT(erts_thr_progress_is_blocking());
ASSERT(mod_tab_p->curr.num_traced_exports > 0);
- erts_clear_export_break(mod_tab_p, &ep->info);
- ep->addressv[code_ix] = (BeamInstr *) ep->beam[1];
- ep->beam[1] = 0;
+
+ erts_clear_export_break(mod_tab_p, ep);
+
+ ep->addressv[code_ix] =
+ (BeamInstr*)ep->trampoline.breakpoint.address;
+ ep->trampoline.breakpoint.address = 0;
+
+ ASSERT(ep->addressv[code_ix] != ep->trampoline.raw);
}
- ASSERT(ep->beam[1] == 0);
+ ASSERT(ep->trampoline.breakpoint.address == 0);
}
}
ASSERT(mod_tab_p->curr.num_breakpoints == 0);
@@ -1471,15 +1477,14 @@ load_import_table(LoaderState* stp)
}
stp->import[i].arity = arity;
stp->import[i].patches = 0;
- stp->import[i].bf = NULL;
+ stp->import[i].bif = NULL;
/*
- * If the export entry refers to a BIF, get the pointer to
- * the BIF function.
+ * If the export entry refers to a BIF, save a pointer to the BIF entry.
*/
if ((e = erts_active_export_entry(mod, func, arity)) != NULL) {
- if (BeamIsOpCode(e->beam[0], op_apply_bif)) {
- stp->import[i].bf = (BifFunction) e->beam[1];
+ if (e->bif_table_index != -1) {
+ stp->import[i].bif = e;
if (func == am_load_nif && mod == am_erlang && arity == 2) {
stp->may_load_nif = 1;
}
@@ -1530,33 +1535,6 @@ read_export_table(LoaderState* stp)
LoadError2(stp, "export table entry %u: label %u not resolved", i, n);
}
stp->export[i].address = address = stp->codev + value;
-
- /*
- * Find out if there is a BIF with the same name.
- */
-
- if (!is_bif(stp->module, func, arity)) {
- continue;
- }
-
- /*
- * This is a stub for a BIF.
- *
- * It should not be exported, and the information in its
- * func_info instruction should be invalidated so that it
- * can be filtered out by module_info(functions) and by
- * any other functions that walk through all local functions.
- */
-
- if (stp->labels[n].num_patches > 0) {
- LoadError3(stp, "there are local calls to the stub for "
- "the BIF %T:%T/%d",
- stp->module, func, arity);
- }
- stp->export[i].address = NULL;
- address[-1] = 0;
- address[-2] = NIL;
- address[-3] = NIL;
}
return 1;
@@ -1564,25 +1542,16 @@ read_export_table(LoaderState* stp)
return 0;
}
-
static int
is_bif(Eterm mod, Eterm func, unsigned arity)
{
- Export* e = erts_active_export_entry(mod, func, arity);
- if (e == NULL) {
- return 0;
- }
- if (! BeamIsOpCode(e->beam[0], op_apply_bif)) {
- return 0;
- }
- if (mod == am_erlang && func == am_apply && arity == 3) {
- /*
- * erlang:apply/3 is a special case -- it is implemented
- * as an instruction and it is OK to redefine it.
- */
- return 0;
+ Export *e = erts_active_export_entry(mod, func, arity);
+
+ if (e != NULL) {
+ return e->bif_table_index != -1;
}
- return 1;
+
+ return 0;
}
static int
@@ -2534,10 +2503,14 @@ load_code(LoaderState* stp)
if (i >= stp->num_imports) {
LoadError1(stp, "invalid import table index %d", i);
}
- if (stp->import[i].bf == NULL) {
+ if (stp->import[i].bif == NULL) {
LoadError1(stp, "not a BIF: import table index %d", i);
}
- code[ci++] = (BeamInstr) stp->import[i].bf;
+ {
+ int bif_index = stp->import[i].bif->bif_table_index;
+ BifEntry *bif_entry = &bif_table[bif_index];
+ code[ci++] = (BeamInstr) bif_entry->f;
+ }
break;
case 'P': /* Byte offset into tuple or stack */
case 'Q': /* Like 'P', but packable */
@@ -2845,18 +2818,43 @@ load_code(LoaderState* stp)
switch (stp->specific_op) {
case op_i_func_info_IaaI:
{
+ int padding_required;
Sint offset;
+
if (function_number >= stp->num_functions) {
LoadError1(stp, "too many functions in module (header said %u)",
stp->num_functions);
}
- if (stp->may_load_nif) {
+ /* Native function calls may be larger than their stubs, so
+ * we'll need to make sure any potentially-native function stub
+ * is padded with enough room.
+ *
+ * Note that the padding is applied for the previous function,
+ * not the current one, so we check whether the old F/A is
+ * a BIF. */
+ padding_required = last_func_start && (stp->may_load_nif ||
+ is_bif(stp->module, stp->function, stp->arity));
+
+ /*
+ * Save context for error messages.
+ */
+ stp->function = code[ci-2];
+ stp->arity = code[ci-1];
+
+ /*
+ * Save current offset of into the line instruction array.
+ */
+ if (stp->func_line) {
+ stp->func_line[function_number] = stp->current_li;
+ }
+
+ if (padding_required) {
const int finfo_ix = ci - FUNC_INFO_SZ;
- if (finfo_ix - last_func_start < BEAM_NIF_MIN_FUNC_SZ && last_func_start) {
+ if (finfo_ix - last_func_start < BEAM_NATIVE_MIN_FUNC_SZ) {
/* Must make room for call_nif op */
- int pad = BEAM_NIF_MIN_FUNC_SZ - (finfo_ix - last_func_start);
- ASSERT(pad > 0 && pad < BEAM_NIF_MIN_FUNC_SZ);
+ int pad = BEAM_NATIVE_MIN_FUNC_SZ - (finfo_ix - last_func_start);
+ ASSERT(pad > 0 && pad < BEAM_NATIVE_MIN_FUNC_SZ);
CodeNeed(pad);
sys_memmove(&code[finfo_ix+pad], &code[finfo_ix],
FUNC_INFO_SZ*sizeof(BeamInstr));
@@ -2867,20 +2865,6 @@ load_code(LoaderState* stp)
}
last_func_start = ci;
- /*
- * Save current offset of into the line instruction array.
- */
-
- if (stp->func_line) {
- stp->func_line[function_number] = stp->current_li;
- }
-
- /*
- * Save context for error messages.
- */
- stp->function = code[ci-2];
- stp->arity = code[ci-1];
-
/* When this assert is triggered, it is normally a sign that
the size of the ops.tab i_func_info instruction is not
the same as FUNC_INFO_SZ */
@@ -2910,7 +2894,6 @@ load_code(LoaderState* stp)
case op_i_bs_match_string_yfWW:
new_string_patch(stp, ci-1);
break;
-
case op_catch_yf:
/* code[ci-3] &&lb_catch_yf
* code[ci-2] y-register offset in E
@@ -3184,6 +3167,26 @@ is_killed_by_make_fun(LoaderState* stp, GenOpArg Reg, GenOpArg idx)
}
}
+/* Test whether Bif is "heavy" and should always go through its export entry */
+static int
+is_heavy_bif(LoaderState* stp, GenOpArg Bif)
+{
+ Export *bif_export;
+
+ if (Bif.type != TAG_u || Bif.val >= stp->num_imports) {
+ return 0;
+ }
+
+ bif_export = stp->import[Bif.val].bif;
+
+ if (bif_export) {
+ int bif_index = bif_export->bif_table_index;
+ return bif_table[bif_index].kind == BIF_KIND_HEAVY;
+ }
+
+ return 0;
+}
+
/*
* Generate an instruction for element/2.
*/
@@ -5211,27 +5214,52 @@ final_touch(LoaderState* stp, struct erl_module_instance* inst_p)
*/
for (i = 0; i < stp->num_exps; i++) {
- Export* ep;
- BeamInstr* address = stp->export[i].address;
+ Export* ep;
+ BeamInstr* address = stp->export[i].address;
- if (address == NULL) {
- /* Skip stub for a BIF */
- continue;
- }
- ep = erts_export_put(stp->module, stp->export[i].function,
- stp->export[i].arity);
- if (on_load) {
- /*
- * on_load: Don't make any of the exported functions
- * callable yet. Keep any function in the current
- * code callable.
- */
- ep->beam[1] = (BeamInstr) address;
- }
- else
+ ep = erts_export_put(stp->module,
+ stp->export[i].function,
+ stp->export[i].arity);
+
+ /* Fill in BIF stubs with a proper call to said BIF. */
+ if (ep->bif_table_index != -1) {
+ erts_write_bif_wrapper(ep, address);
+ }
+
+ if (on_load) {
+ /*
+ * on_load: Don't make any of the exported functions
+ * callable yet. Keep any function in the current
+ * code callable.
+ */
+ ep->trampoline.not_loaded.deferred = (BeamInstr) address;
+ } else {
ep->addressv[erts_staging_code_ix()] = address;
+ }
}
+#ifdef DEBUG
+ /* Ensure that we've loaded stubs for all BIFs in this module. */
+ for (i = 0; i < BIF_SIZE; i++) {
+ BifEntry *entry = &bif_table[i];
+
+ if (stp->module == entry->module) {
+ Export *ep = erts_export_put(entry->module,
+ entry->name,
+ entry->arity);
+ BeamInstr *addr = ep->addressv[erts_staging_code_ix()];
+
+ if (!ErtsInArea(addr, stp->codev, stp->ci * sizeof(BeamInstr))) {
+ erts_exit(ERTS_ABORT_EXIT,
+ "Module %T doesn't export BIF %T/%i\n",
+ entry->module,
+ entry->name,
+ entry->arity);
+ }
+ }
+ }
+#endif
+
/*
* Import functions and patch all callers.
*/
@@ -5403,15 +5431,14 @@ transform_engine(LoaderState* st)
i = instr->a[ap].val;
ASSERT(i < st->num_imports);
- if (i >= st->num_imports || st->import[i].bf == NULL)
+ if (i >= st->num_imports || st->import[i].bif == NULL)
goto restart;
if (bif_number != -1 &&
- bif_export[bif_number]->beam[1] != (BeamInstr) st->import[i].bf) {
+ bif_export[bif_number] != st->import[i].bif) {
goto restart;
}
}
break;
-
#endif
#if defined(TOP_is_not_bif)
case TOP_is_not_bif:
@@ -5441,7 +5468,7 @@ transform_engine(LoaderState* st)
* they are special.
*/
if (i < st->num_imports) {
- if (st->import[i].bf != NULL ||
+ if (st->import[i].bif != NULL ||
(st->import[i].module == am_erlang &&
st->import[i].function == am_apply &&
(st->import[i].arity == 2 || st->import[i].arity == 3))) {
@@ -6286,12 +6313,12 @@ exported_from_module(Process* p, /* Process whose heap to use. */
if (ep->info.mfa.module == mod) {
Eterm tuple;
-
- if (ep->addressv[code_ix] == ep->beam &&
- BeamIsOpCode(ep->beam[0], op_call_error_handler)) {
- /* There is a call to the function, but it does not exist. */
- continue;
- }
+
+ if (ep->addressv[code_ix] == ep->trampoline.raw &&
+ BeamIsOpCode(ep->trampoline.op, op_call_error_handler)) {
+ /* There is a call to the function, but it does not exist. */
+ continue;
+ }
if (hp == hend) {
int need = 10 * 5;
diff --git a/erts/emulator/beam/beam_load.h b/erts/emulator/beam/beam_load.h
index 156c3c45e2..e7127c5b08 100644
--- a/erts/emulator/beam/beam_load.h
+++ b/erts/emulator/beam/beam_load.h
@@ -106,7 +106,7 @@ typedef struct beam_code_header {
}BeamCodeHeader;
-# define BEAM_NIF_MIN_FUNC_SZ 4
+# define BEAM_NATIVE_MIN_FUNC_SZ 4
void erts_release_literal_area(struct ErtsLiteralArea_* literal_area);
int erts_is_module_native(BeamCodeHeader* code);
diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c
index 9f67f46b31..d3fd99932a 100644
--- a/erts/emulator/beam/bif.c
+++ b/erts/emulator/beam/bif.c
@@ -4971,15 +4971,28 @@ void erts_init_trap_export(Export* ep, Eterm m, Eterm f, Uint a,
Eterm (*bif)(BIF_ALIST))
{
int i;
+
sys_memset((void *) ep, 0, sizeof(Export));
+
for (i=0; i<ERTS_NUM_CODE_IX; i++) {
- ep->addressv[i] = ep->beam;
+ ep->addressv[i] = ep->trampoline.raw;
}
+
ep->info.mfa.module = m;
ep->info.mfa.function = f;
ep->info.mfa.arity = a;
- ep->beam[0] = BeamOpCodeAddr(op_apply_bif);
- ep->beam[1] = (BeamInstr) bif;
+ ep->trampoline.op = BeamOpCodeAddr(op_call_bif_W);
+ ep->trampoline.raw[1] = (BeamInstr)bif;
+}
+
+/*
+ * Writes a BIF call wrapper to the given address.
+ */
+void erts_write_bif_wrapper(Export *export, BeamInstr *address) {
+ BifEntry *entry = &bif_table[export->bif_table_index];
+
+ address[0] = BeamOpCodeAddr(op_call_bif_W);
+ address[1] = (BeamInstr)entry->f;
}
void erts_init_bif(void)
@@ -5031,7 +5044,7 @@ void erts_init_bif(void)
}
/*
- * Scheduling of BIFs via NifExport...
+ * Scheduling of BIFs via ErtsNativeFunc...
*/
#define ERTS_WANT_NFUNC_SCHED_INTERNALS__
#include "erl_nfunc_sched.h"
@@ -5046,8 +5059,8 @@ schedule(Process *c_p, Process *dirty_shadow_proc,
int argc, Eterm *argv)
{
ERTS_LC_ASSERT(ERTS_PROC_LOCK_MAIN & erts_proc_lc_my_proc_locks(c_p));
- (void) erts_nif_export_schedule(c_p, dirty_shadow_proc,
- mfa, pc, BeamOpCodeAddr(op_apply_bif),
+ (void) erts_nfunc_schedule(c_p, dirty_shadow_proc,
+ mfa, pc, BeamOpCodeAddr(op_call_bif_W),
dfunc, ifunc,
module, function,
argc, argv);
@@ -5056,23 +5069,23 @@ schedule(Process *c_p, Process *dirty_shadow_proc,
static BIF_RETTYPE dirty_bif_result(BIF_ALIST_1)
{
- NifExport *nep = (NifExport *) ERTS_PROC_GET_NIF_TRAP_EXPORT(BIF_P);
- erts_nif_export_restore(BIF_P, nep, BIF_ARG_1);
+ ErtsNativeFunc *nep = (ErtsNativeFunc *) ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(BIF_P);
+ erts_nfunc_restore(BIF_P, nep, BIF_ARG_1);
BIF_RET(BIF_ARG_1);
}
static BIF_RETTYPE dirty_bif_trap(BIF_ALIST)
{
- NifExport *nep = (NifExport *) ERTS_PROC_GET_NIF_TRAP_EXPORT(BIF_P);
+ ErtsNativeFunc *nep = (ErtsNativeFunc *) ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(BIF_P);
/*
* Arity and argument registers already set
* correct by call to dirty_bif_trap()...
*/
- ASSERT(BIF_P->arity == nep->exp.info.mfa.arity);
+ ASSERT(BIF_P->arity == nep->trampoline.info.mfa.arity);
- erts_nif_export_restore(BIF_P, nep, THE_NON_VALUE);
+ erts_nfunc_restore(BIF_P, nep, THE_NON_VALUE);
BIF_P->i = (BeamInstr *) nep->func;
BIF_P->freason = TRAP;
@@ -5087,8 +5100,8 @@ static BIF_RETTYPE dirty_bif_exception(BIF_ALIST_2)
freason = signed_val(BIF_ARG_1);
- /* Restore orig info for error and clear nif export in handle_error() */
- freason |= EXF_RESTORE_NIF;
+ /* Restore orig info for error and clear nif wrapper in handle_error() */
+ freason |= EXF_RESTORE_NFUNC;
BIF_P->fvalue = BIF_ARG_2;
@@ -5126,6 +5139,7 @@ erts_schedule_bif(Process *proc,
if (!ERTS_PROC_IS_EXITING(c_p)) {
Export *exp;
BifFunction dbif, ibif;
+ BeamInstr call_instr;
BeamInstr *pc;
/*
@@ -5160,29 +5174,41 @@ erts_schedule_bif(Process *proc,
if (i == NULL) {
ERTS_INTERNAL_ERROR("Missing instruction pointer");
}
+
+ if (BeamIsOpCode(*i, op_i_generic_breakpoint)) {
+ ErtsCodeInfo *ci;
+ GenericBp *bp;
+
+ ci = erts_code_to_codeinfo(i);
+ bp = ci->u.gen_bp;
+
+ call_instr = bp->orig_instr;
+ } else {
+ call_instr = *i;
+ }
+
#ifdef HIPE
- else if (proc->flags & F_HIPE_MODE) {
+ if (proc->flags & F_HIPE_MODE) {
/* Pointer to bif export in i */
exp = (Export *) i;
- pc = c_p->cp;
+ pc = cp_val(c_p->stop[0]);
mfa = &exp->info.mfa;
- }
+ } else /* !! This is part of the if clause below !! */
#endif
- else if (BeamIsOpCode(*i, op_call_bif_e)) {
- /* Pointer to bif export in i+1 */
- exp = (Export *) i[1];
+ if (BeamIsOpCode(call_instr, op_call_light_bif_be)) {
+ /* Pointer to bif export in i+2 */
+ exp = (Export *) i[2];
pc = i;
mfa = &exp->info.mfa;
}
- else if (BeamIsOpCode(*i, op_call_bif_only_e)) {
- /* Pointer to bif export in i+1 */
- exp = (Export *) i[1];
+ else if (BeamIsOpCode(call_instr, op_call_light_bif_only_be)) {
+ /* Pointer to bif export in i+2 */
+ exp = (Export *) i[2];
pc = i;
mfa = &exp->info.mfa;
}
- else if (BeamIsOpCode(*i, op_apply_bif)) {
- /* Pointer to bif in i+1, and mfa in i-3 */
- pc = c_p->cp;
+ else if (BeamIsOpCode(call_instr, op_call_bif_W)) {
+ pc = cp_val(c_p->stop[0]);
mfa = erts_code_to_codemfa(i);
}
else {
@@ -5210,7 +5236,7 @@ erts_schedule_bif(Process *proc,
static BIF_RETTYPE
call_bif(Process *c_p, Eterm *reg, BeamInstr *I)
{
- NifExport *nep = ERTS_I_BEAM_OP_TO_NIF_EXPORT(I);
+ ErtsNativeFunc *nep = ERTS_I_BEAM_OP_TO_NFUNC(I);
ErtsBifFunc bif = (ErtsBifFunc) nep->func;
BIF_RETTYPE ret;
@@ -5223,12 +5249,12 @@ call_bif(Process *c_p, Eterm *reg, BeamInstr *I)
ret = (*bif)(c_p, reg, I);
if (is_value(ret))
- erts_nif_export_restore(c_p, nep, ret);
+ erts_nfunc_restore(c_p, nep, ret);
else if (c_p->freason != TRAP)
- c_p->freason |= EXF_RESTORE_NIF; /* restore in handle_error() */
+ c_p->freason |= EXF_RESTORE_NFUNC; /* restore in handle_error() */
else if (nep->func == ERTS_SCHED_BIF_TRAP_MARKER) {
/* BIF did an ordinary trap... */
- erts_nif_export_restore(c_p, nep, ret);
+ erts_nfunc_restore(c_p, nep, ret);
}
/* else:
* BIF rescheduled itself using erts_schedule_bif().
@@ -5245,7 +5271,7 @@ erts_call_dirty_bif(ErtsSchedulerData *esdp, Process *c_p, BeamInstr *I, Eterm *
int exiting;
Process *dirty_shadow_proc;
ErtsBifFunc bf;
- NifExport *nep;
+ ErtsNativeFunc *nep;
#ifdef DEBUG
Eterm *c_p_htop;
erts_aint32_t state;
@@ -5258,8 +5284,8 @@ erts_call_dirty_bif(ErtsSchedulerData *esdp, Process *c_p, BeamInstr *I, Eterm *
#endif
- nep = ERTS_I_BEAM_OP_TO_NIF_EXPORT(I);
- ASSERT(nep == ERTS_PROC_GET_NIF_TRAP_EXPORT(c_p));
+ nep = ERTS_I_BEAM_OP_TO_NFUNC(I);
+ ASSERT(nep == ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(c_p));
nep->func = ERTS_SCHED_BIF_TRAP_MARKER;
@@ -5273,7 +5299,6 @@ erts_call_dirty_bif(ErtsSchedulerData *esdp, Process *c_p, BeamInstr *I, Eterm *
dirty_shadow_proc->freason = c_p->freason;
dirty_shadow_proc->fvalue = c_p->fvalue;
dirty_shadow_proc->ftrace = c_p->ftrace;
- dirty_shadow_proc->cp = c_p->cp;
dirty_shadow_proc->i = c_p->i;
#ifdef DEBUG
@@ -5320,7 +5345,6 @@ erts_call_dirty_bif(ErtsSchedulerData *esdp, Process *c_p, BeamInstr *I, Eterm *
c_p->freason = dirty_shadow_proc->freason;
c_p->fvalue = dirty_shadow_proc->fvalue;
c_p->ftrace = dirty_shadow_proc->ftrace;
- c_p->cp = dirty_shadow_proc->cp;
c_p->i = dirty_shadow_proc->i;
c_p->arity = dirty_shadow_proc->arity;
}
diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index 602db106b1..19dabc0514 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -25,13 +25,14 @@
#
# <bif-decl> ::= "bif" <bif> <C-name>* |
# "ubif" <bif> <C-name>* |
-# "gcbif" <bif> <C-name>*
+# "hbif" <bif> <C-name>*
# <bif> ::= <module> ":" <name> "/" <arity>
#
-# ubif: Use for operators and guard BIFs that never build anything
-# on the heap (such as tuple_size/1) and operators.
+# ubif: Use for operators and guard BIFs.
#
-# gcbif: Use for guard BIFs that may build on the heap (such as abs/1).
+# hbif: Use for BIFs that perform garbage collection or need up-to-date
+# information on where they were called from. These must be called
+# through the export entry.
#
# bif: Use for all other BIFs.
#
@@ -60,7 +61,7 @@ bif erlang:display_string/1
bif erlang:display_nl/0
ubif erlang:element/2
bif erlang:erase/0
-bif erlang:erase/1
+hbif erlang:erase/1
bif erlang:exit/1
bif erlang:exit/2
bif erlang:exit_signal/2
@@ -70,7 +71,7 @@ ubif erlang:float/1
bif erlang:float_to_list/1
bif erlang:float_to_list/2
bif erlang:fun_info/2
-bif erts_internal:garbage_collect/1
+hbif erts_internal:garbage_collect/1
bif erlang:get/0
bif erlang:get/1
bif erlang:get_keys/1
@@ -127,10 +128,10 @@ bif erlang:ports/0
bif erlang:pre_loaded/0
bif erlang:process_flag/2
bif erts_internal:process_flag/3
-bif erlang:process_info/1
-bif erlang:process_info/2
+hbif erlang:process_info/1
+hbif erlang:process_info/2
bif erlang:processes/0
-bif erlang:put/2
+hbif erlang:put/2
bif erlang:register/2
bif erlang:registered/0
ubif erlang:round/1
@@ -174,7 +175,7 @@ bif erts_internal:port_connect/2
bif erts_internal:request_system_task/3
bif erts_internal:request_system_task/4
-bif erts_internal:check_process_code/1
+hbif erts_internal:check_process_code/1
bif erts_internal:map_to_tuple_keys/1
bif erts_internal:term_type/1
@@ -195,6 +196,8 @@ bif erts_internal:dirty_process_handle_signals/1
bif erts_internal:create_dist_channel/4
+bif erts_internal:ets_super_user/1
+
# inet_db support
bif erlang:port_set_data/2
bif erlang:port_get_data/1
@@ -464,7 +467,7 @@ bif code:is_module_native/1
# New Bifs in R9C.
#
-bif erlang:hibernate/3
+hbif erlang:hibernate/3
bif error_logger:warning_map/0
#
@@ -742,3 +745,11 @@ bif erts_internal:spawn_system_process/3
bif erlang:integer_to_list/2
bif erlang:integer_to_binary/2
bif persistent_term:get/2
+
+#
+# New in 22.1
+#
+
+bif erts_internal:ets_lookup_binary_info/2
+bif erts_internal:ets_raw_first/1
+bif erts_internal:ets_raw_next/2
diff --git a/erts/emulator/beam/bif_instrs.tab b/erts/emulator/beam/bif_instrs.tab
index 8e0caa38a3..de5305bde4 100644
--- a/erts/emulator/beam/bif_instrs.tab
+++ b/erts/emulator/beam/bif_instrs.tab
@@ -212,26 +212,32 @@ i_length.execute(Fail, Live, Dst) {
// Call a BIF, store the result in x(0) and transfer control to the
// next instruction.
//
-call_bif(Exp) {
+call_light_bif(Bif, Exp) {
+ Export *export;
ErtsBifFunc bf;
+
Eterm result;
ErlHeapFragment *live_hf_end;
- Export *export = (Export*) $Exp;
+
+ bf = (ErtsBifFunc) $Bif;
+ export = (Export*) $Exp;
if (!((FCALLS - 1) > 0 || (FCALLS-1) > neg_o_reds)) {
/*
* If we have run out of reductions, do a context
* switch before calling the BIF.
*/
- c_p->arity = GET_BIF_ARITY(export);
+ c_p->arity = GET_EXPORT_ARITY(export);
c_p->current = &export->info.mfa;
goto context_switch3;
}
- ERTS_MSACC_SET_BIF_STATE_CACHED_X(GET_BIF_MODULE(export),
- GET_BIF_ADDRESS(export));
+ if (ERTS_UNLIKELY(export->is_bif_traced)) {
+ $SAVE_CONTINUATION_POINTER($NEXT_INSTRUCTION);
+ $DISPATCH_EXPORT(export);
+ }
- bf = GET_BIF_ADDRESS(export);
+ ERTS_MSACC_SET_BIF_STATE_CACHED_X(GET_EXPORT_MODULE(export), bf);
PRE_BIF_SWAPOUT(c_p);
ERTS_DBG_CHK_REDS(c_p, FCALLS);
@@ -243,21 +249,26 @@ call_bif(Exp) {
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
live_hf_end = c_p->mbuf;
ERTS_CHK_MBUF_SZ(c_p);
+
result = (*bf)(c_p, reg, I);
+
+ /* Only heavy BIFs may GC. */
+ ASSERT(E == c_p->stop);
+
ERTS_CHK_MBUF_SZ(c_p);
ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(result));
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
ERTS_HOLE_CHECK(c_p);
ERTS_REQ_PROC_MAIN_LOCK(c_p);
if (ERTS_IS_GC_DESIRED(c_p)) {
- Uint arity = GET_BIF_ARITY(export);
+ Uint arity = GET_EXPORT_ARITY(export);
result = erts_gc_after_bif_call_lhf(c_p, live_hf_end, result,
reg, arity);
E = c_p->stop;
}
- PROCESS_MAIN_CHK_LOCKS(c_p);
HTOP = HEAP_TOP(c_p);
FCALLS = c_p->fcalls;
+ PROCESS_MAIN_CHK_LOCKS(c_p);
ERTS_DBG_CHK_REDS(c_p, FCALLS);
/*
@@ -280,10 +291,9 @@ call_bif(Exp) {
* erlang code or by nif_bif.epilogue() when the BIF
* is done).
*/
- SET_CP(c_p, $NEXT_INSTRUCTION);
+ $SAVE_CONTINUATION_POINTER($NEXT_INSTRUCTION);
SET_I(c_p->i);
- SWAPIN;
- Dispatch();
+ $DISPATCH();
}
/*
@@ -297,29 +307,38 @@ call_bif(Exp) {
//
// Call a BIF tail-recursively, storing the result in x(0) and doing
-// a return to the continuation poiner (c_p->cp).
+// a return to the continuation poiner.
//
-
-call_bif_only(Exp) {
+call_light_bif_only(Bif, Exp) {
+ ErlHeapFragment *live_hf_end;
ErtsBifFunc bf;
+ Export *export;
Eterm result;
- ErlHeapFragment *live_hf_end;
- Export *export = (Export*) $Exp;
+
+ bf = (ErtsBifFunc) $Bif;
+ export = (Export*) $Exp;
if (!((FCALLS - 1) > 0 || (FCALLS-1) > neg_o_reds)) {
/*
* If we have run out of reductions, do a context
* switch before calling the BIF.
*/
- c_p->arity = GET_BIF_ARITY(export);
+ c_p->arity = GET_EXPORT_ARITY(export);
c_p->current = &export->info.mfa;
goto context_switch3;
}
- ERTS_MSACC_SET_BIF_STATE_CACHED_X(GET_BIF_MODULE(export),
- GET_BIF_ADDRESS(export));
+ if (ERTS_UNLIKELY(export->is_bif_traced)) {
+ /* Set up a dummy stack frame so we can perform a normal call. Loader
+ * transformations ensure that the next instruction after this is
+ * 'deallocate_return 0'. */
+ $AH(0, 0, GET_EXPORT_ARITY(export));
+
+ $SAVE_CONTINUATION_POINTER($NEXT_INSTRUCTION);
+ $DISPATCH_EXPORT(export);
+ }
- bf = GET_BIF_ADDRESS(export);
+ ERTS_MSACC_SET_BIF_STATE_CACHED_X(GET_EXPORT_MODULE(export), bf);
PRE_BIF_SWAPOUT(c_p);
ERTS_DBG_CHK_REDS(c_p, FCALLS);
@@ -331,21 +350,26 @@ call_bif_only(Exp) {
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
live_hf_end = c_p->mbuf;
ERTS_CHK_MBUF_SZ(c_p);
+
result = (*bf)(c_p, reg, I);
+
+ /* Only heavy BIFs may GC. */
+ ASSERT(E == c_p->stop);
+
ERTS_CHK_MBUF_SZ(c_p);
ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(result));
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
ERTS_HOLE_CHECK(c_p);
ERTS_REQ_PROC_MAIN_LOCK(c_p);
if (ERTS_IS_GC_DESIRED(c_p)) {
- Uint arity = GET_BIF_ARITY(export);
+ Uint arity = GET_EXPORT_ARITY(export);
result = erts_gc_after_bif_call_lhf(c_p, live_hf_end, result,
reg, arity);
E = c_p->stop;
}
- PROCESS_MAIN_CHK_LOCKS(c_p);
HTOP = HEAP_TOP(c_p);
FCALLS = c_p->fcalls;
+ PROCESS_MAIN_CHK_LOCKS(c_p);
ERTS_DBG_CHK_REDS(c_p, FCALLS);
/*
@@ -367,11 +391,10 @@ call_bif_only(Exp) {
} else if (c_p->freason == TRAP) {
/*
* Dispatch to a trap. When the trap is done, a jump
- * to the continuation pointer (c_p->cp) will be done.
+ * to the continuation pointer on the stack will be done.
*/
SET_I(c_p->i);
- SWAPIN;
- Dispatch();
+ $DISPATCH();
}
/*
@@ -413,17 +436,17 @@ send() {
r(0) = result;
CHECK_TERM(r(0));
} else if (c_p->freason == TRAP) {
- SET_CP(c_p, $NEXT_INSTRUCTION);
+ $SAVE_CONTINUATION_POINTER($NEXT_INSTRUCTION);
SET_I(c_p->i);
SWAPIN;
- Dispatch();
+ $DISPATCH();
} else {
goto find_func_info;
}
}
call_nif := nif_bif.call_nif.epilogue;
-apply_bif := nif_bif.apply_bif.epilogue;
+call_bif := nif_bif.call_bif.epilogue;
nif_bif.head() {
Eterm nif_bif_result;
@@ -433,7 +456,7 @@ nif_bif.head() {
ErtsCodeMFA *codemfa;
}
-nif_bif.call_nif() {
+nif_bif.call_nif(Func, NifMod, DirtyFunc) {
/*
* call_nif is always first instruction in function:
*
@@ -443,11 +466,14 @@ nif_bif.call_nif() {
* I[0]: &&call_nif
* I[1]: Function pointer to NIF function
* I[2]: Pointer to erl_module_nif
- * I[3]: Function pointer to dirty NIF
+ * I[3]: Function pointer to dirty NIF. This is not used in this
+ * instruction, but dirty schedulers look at it.
*
- * This layout is determined by the NifExport struct
+ * This layout is determined by the ErtsNativeFunc struct
*/
+ (void)$DirtyFunc;
+
ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_NIF);
codemfa = erts_code_to_codemfa(I);
@@ -465,12 +491,12 @@ nif_bif.call_nif() {
ASSERT(!ERTS_PROC_IS_EXITING(c_p));
{
typedef Eterm NifF(struct enif_environment_t*, int argc, Eterm argv[]);
- NifF* fp = vbf = (NifF*) I[1];
+ NifF* fp = vbf = (NifF*) $Func;
struct enif_environment_t env;
ASSERT(c_p->scheduler_data);
live_hf_end = c_p->mbuf;
ERTS_CHK_MBUF_SZ(c_p);
- erts_pre_nif(&env, c_p, (struct erl_module_nif*)I[2], NULL);
+ erts_pre_nif(&env, c_p, (struct erl_module_nif*)$NifMod, NULL);
ASSERT((c_p->scheduler_data)->current_nif == NULL);
(c_p->scheduler_data)->current_nif = &env;
@@ -495,15 +521,15 @@ nif_bif.call_nif() {
DTRACE_NIF_RETURN(c_p, codemfa);
}
-nif_bif.apply_bif() {
+nif_bif.call_bif(Func) {
/*
- * At this point, I points to the code[0] in the export entry for
- * the BIF:
+ * At this point, I points to the code[0] in the native function wrapper
+ * for the BIF:
*
* code[-3]: Module
* code[-2]: Function
* code[-1]: Arity
- * code[0]: &&apply_bif
+ * code[0]: &&call_bif
* code[1]: Function pointer to BIF function
*/
@@ -515,21 +541,19 @@ nif_bif.apply_bif() {
codemfa = erts_code_to_codemfa(I);
- ERTS_MSACC_SET_BIF_STATE_CACHED_X(codemfa->module, (BifFunction)Arg(0));
-
+ ERTS_MSACC_SET_BIF_STATE_CACHED_X(codemfa->module, (BifFunction)$Func);
/* In case we apply process_info/1,2 or load_nif/1 */
c_p->current = codemfa;
$SET_CP_I_ABS(I); /* In case we apply check_process_code/2. */
c_p->arity = 0; /* To allow garbage collection on ourselves
- * (check_process_code/2).
- */
+ * (check_process_code/2, put/2, etc). */
DTRACE_BIF_ENTRY(c_p, codemfa);
SWAPOUT;
ERTS_DBG_CHK_REDS(c_p, FCALLS - 1);
c_p->fcalls = FCALLS - 1;
- vbf = (BifFunction) Arg(0);
+ vbf = (BifFunction)$Func;
PROCESS_MAIN_CHK_LOCKS(c_p);
bif_nif_arity = codemfa->arity;
ASSERT(bif_nif_arity <= 4);
@@ -557,6 +581,7 @@ nif_bif.apply_bif() {
}
nif_bif.epilogue() {
+ //| -no_next
ERTS_REQ_PROC_MAIN_LOCK(c_p);
ERTS_HOLE_CHECK(c_p);
if (ERTS_IS_GC_DESIRED(c_p)) {
@@ -570,8 +595,7 @@ nif_bif.epilogue() {
if (ERTS_LIKELY(is_value(nif_bif_result))) {
r(0) = nif_bif_result;
CHECK_TERM(r(0));
- SET_I(c_p->cp);
- c_p->cp = 0;
+ $RETURN();
Goto(*I);
} else if (c_p->freason == TRAP) {
SET_I(c_p->i);
@@ -579,8 +603,42 @@ nif_bif.epilogue() {
c_p->flags &= ~F_HIBERNATE_SCHED;
goto do_schedule;
}
- Dispatch();
+ $DISPATCH();
+ }
+ {
+ BeamInstr *cp = erts_printable_return_address(c_p, E);
+ ASSERT(VALID_INSTR(*cp));
+ I = handle_error(c_p, cp, reg, c_p->current);
}
- I = handle_error(c_p, c_p->cp, reg, c_p->current);
goto post_error_handling;
}
+
+i_load_nif() {
+ //| -no_next
+ if (erts_try_seize_code_write_permission(c_p)) {
+ Eterm result;
+
+ PRE_BIF_SWAPOUT(c_p);
+ result = erts_load_nif(c_p, I, r(0), r(1));
+ erts_release_code_write_permission();
+ ERTS_REQ_PROC_MAIN_LOCK(c_p);
+ SWAPIN;
+
+ if (ERTS_LIKELY(is_value(result))) {
+ r(0) = result;
+ $NEXT0();
+ } else {
+ static ErtsCodeMFA mfa = {am_erlang, am_load_nif, 2};
+ c_p->freason = BADARG;
+ I = handle_error(c_p, I, reg, &mfa);
+ goto post_error_handling;
+ }
+ } else {
+ /* Yield and try again */
+ $SET_CP_I_ABS(I);
+ SWAPOUT;
+ c_p->current = NULL;
+ c_p->arity = 2;
+ goto do_schedule;
+ }
+}
diff --git a/erts/emulator/beam/binary.c b/erts/emulator/beam/binary.c
index a18228b84a..4ddf59092a 100644
--- a/erts/emulator/beam/binary.c
+++ b/erts/emulator/beam/binary.c
@@ -1153,51 +1153,47 @@ BIF_RETTYPE list_to_bitstring_1(BIF_ALIST_1)
BIF_RETTYPE split_binary_2(BIF_ALIST_2)
{
- Uint pos;
- ErlSubBin* sb1;
- ErlSubBin* sb2;
- size_t orig_size;
- Eterm orig;
- Uint offset;
- Uint bit_offset;
- Uint bit_size;
- Eterm* hp;
+ size_t orig_size, left_size, right_size;
+ Uint byte_offset, bit_offset, bit_size;
+ Uint split_at;
+
+ Eterm *hp, *hp_end;
+ Eterm left, right;
+ Eterm real_bin;
+ Eterm result;
+ byte *bptr;
if (is_not_binary(BIF_ARG_1)) {
- goto error;
- }
- if (!term_to_Uint(BIF_ARG_2, &pos)) {
- goto error;
- }
- if ((orig_size = binary_size(BIF_ARG_1)) < pos) {
- goto error;
+ BIF_ERROR(BIF_P, BADARG);
+ } else if (!term_to_Uint(BIF_ARG_2, &split_at)) {
+ BIF_ERROR(BIF_P, BADARG);
+ } else if ((orig_size = binary_size(BIF_ARG_1)) < split_at) {
+ BIF_ERROR(BIF_P, BADARG);
}
- hp = HAlloc(BIF_P, 2*ERL_SUB_BIN_SIZE+3);
- ERTS_GET_REAL_BIN(BIF_ARG_1, orig, offset, bit_offset, bit_size);
- sb1 = (ErlSubBin *) hp;
- sb1->thing_word = HEADER_SUB_BIN;
- sb1->size = pos;
- sb1->offs = offset;
- sb1->orig = orig;
- sb1->bitoffs = bit_offset;
- sb1->bitsize = 0;
- sb1->is_writable = 0;
- hp += ERL_SUB_BIN_SIZE;
-
- sb2 = (ErlSubBin *) hp;
- sb2->thing_word = HEADER_SUB_BIN;
- sb2->size = orig_size - pos;
- sb2->offs = offset + pos;
- sb2->orig = orig;
- sb2->bitoffs = bit_offset;
- sb2->bitsize = bit_size; /* The extra bits go into the second binary. */
- sb2->is_writable = 0;
- hp += ERL_SUB_BIN_SIZE;
-
- return TUPLE2(hp, make_binary(sb1), make_binary(sb2));
-
- error:
- BIF_ERROR(BIF_P, BADARG);
+
+ left_size = split_at;
+ right_size = orig_size - split_at;
+
+ ERTS_GET_REAL_BIN(BIF_ARG_1, real_bin, byte_offset, bit_offset, bit_size);
+ bptr = binary_bytes(real_bin);
+
+ hp = HAlloc(BIF_P, EXTRACT_SUB_BIN_HEAP_NEED * 2 + 3);
+ hp_end = hp + (EXTRACT_SUB_BIN_HEAP_NEED * 2 + 3);
+
+ left = erts_extract_sub_binary(&hp, real_bin, bptr,
+ byte_offset * 8 + bit_offset,
+ left_size * 8);
+
+ right = erts_extract_sub_binary(&hp, real_bin, bptr,
+ (byte_offset + split_at) * 8 + bit_offset,
+ right_size * 8 + bit_size);
+
+ result = TUPLE2(hp, left, right);
+ hp += 3;
+
+ HRelease(BIF_P, hp_end, hp);
+
+ return result;
}
diff --git a/erts/emulator/beam/bs_instrs.tab b/erts/emulator/beam/bs_instrs.tab
index bd1ad91e45..5f25bc2ad3 100644
--- a/erts/emulator/beam/bs_instrs.tab
+++ b/erts/emulator/beam/bs_instrs.tab
@@ -149,7 +149,7 @@ i_bs_get_binary_all2.execute(Fail, Live, Unit, Dst) {
ErlBinMatchBuffer *_mb;
Eterm _result;
- $GC_TEST_PRESERVE(ERL_SUB_BIN_SIZE, $Live, context);
+ $GC_TEST_PRESERVE(EXTRACT_SUB_BIN_HEAP_NEED, $Live, context);
_mb = ms_matchbuffer(context);
if (((_mb->size - _mb->offset) % $Unit) == 0) {
LIGHT_SWAPOUT;
@@ -179,7 +179,7 @@ i_bs_get_binary2.execute(Fail, Live, Sz, Flags, Dst) {
Eterm _result;
Uint _size;
$BS_GET_FIELD_SIZE($Sz, (($Flags) >> 3), $FAIL($Fail), _size);
- $GC_TEST_PRESERVE(ERL_SUB_BIN_SIZE, $Live, context);
+ $GC_TEST_PRESERVE(EXTRACT_SUB_BIN_HEAP_NEED, $Live, context);
_mb = ms_matchbuffer(context);
LIGHT_SWAPOUT;
_result = erts_bs_get_binary_2(c_p, _size, $Flags, _mb);
@@ -206,8 +206,7 @@ i_bs_get_binary_imm2.fetch(Ctx) {
i_bs_get_binary_imm2.execute(Fail, Live, Sz, Flags, Dst) {
ErlBinMatchBuffer *_mb;
Eterm _result;
- $GC_TEST_PRESERVE(heap_bin_size(ERL_ONHEAP_BIN_LIMIT),
- $Live, context);
+ $GC_TEST_PRESERVE(EXTRACT_SUB_BIN_HEAP_NEED, $Live, context);
_mb = ms_matchbuffer(context);
LIGHT_SWAPOUT;
_result = erts_bs_get_binary_2(c_p, $Sz, $Flags, _mb);
@@ -876,9 +875,7 @@ bs_start_match.execute(Fail, Live, Slots, Dst) {
result = erts_bs_start_match_2(c_p, context, slots);
HTOP = HEAP_TOP(c_p);
HEAP_SPACE_VERIFIED(0);
- if (is_non_value(result)) {
- $FAIL($Fail);
- }
+
$REFRESH_GEN_DEST();
$Dst = result;
} else {
@@ -1296,10 +1293,6 @@ i_bs_start_match3_gp.execute(Live, Fail, Dst, Pos) {
HTOP = HEAP_TOP(c_p);
HEAP_SPACE_VERIFIED(0);
- if (ms == NULL) {
- $FAIL($Fail);
- }
-
$REFRESH_GEN_DEST();
$Dst = make_matchstate(ms);
position = ms->mb.offset;
@@ -1348,10 +1341,6 @@ i_bs_start_match3.execute(Live, Fail, Dst) {
HTOP = HEAP_TOP(c_p);
HEAP_SPACE_VERIFIED(0);
- if (ms == NULL) {
- $FAIL($Fail);
- }
-
$REFRESH_GEN_DEST();
$Dst = make_matchstate(ms);
} else {
@@ -1523,10 +1512,6 @@ i_bs_start_match3.execute(Live, Fail, Dst) {
HTOP = HEAP_TOP(c_p);
HEAP_SPACE_VERIFIED(0);
- if (is_non_value(result)) {
- $FAIL($Fail);
- }
-
$REFRESH_GEN_DEST();
$Dst = result;
} else {
diff --git a/erts/emulator/beam/copy.c b/erts/emulator/beam/copy.c
index db74b06cc5..9bbcba6f3b 100644
--- a/erts/emulator/beam/copy.c
+++ b/erts/emulator/beam/copy.c
@@ -604,7 +604,12 @@ cleanup:
/*
* Copy a structure to a heap.
*/
-Eterm copy_struct_x(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap, Uint *bsz, erts_literal_area_t *litopt)
+Eterm copy_struct_x(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap,
+ Uint *bsz, erts_literal_area_t *litopt
+#ifdef ERTS_COPY_REGISTER_LOCATION
+ , char *file, int line
+#endif
+ )
{
char* hstart;
Uint hsize;
@@ -854,7 +859,11 @@ Eterm copy_struct_x(Eterm obj, Uint sz, Eterm** hpp, ErlOffHeap* off_heap, Uint
case EXTERNAL_REF_SUBTAG:
{
ExternalThing *etp = (ExternalThing *) objp;
+#if defined(ERTS_COPY_REGISTER_LOCATION) && defined(ERL_NODE_BOOKKEEP)
+ erts_ref_node_entry__(etp->node, 2, make_boxed(htop), file, line);
+#else
erts_ref_node_entry(etp->node, 2, make_boxed(htop));
+#endif
}
L_off_heap_node_container_common:
{
@@ -1326,8 +1335,13 @@ Uint copy_shared_calculate(Eterm obj, erts_shcopy_t *info)
* Copy object "obj" preserving sharing.
* Second half: copy and restore the object.
*/
-Uint copy_shared_perform(Eterm obj, Uint size, erts_shcopy_t *info,
- Eterm** hpp, ErlOffHeap* off_heap) {
+Uint copy_shared_perform_x(Eterm obj, Uint size, erts_shcopy_t *info,
+ Eterm** hpp, ErlOffHeap* off_heap
+#ifdef ERTS_COPY_REGISTER_LOCATION
+ , char *file, int line
+#endif
+ )
+{
Uint e;
unsigned sz;
Eterm* ptr;
@@ -1393,7 +1407,11 @@ Uint copy_shared_perform(Eterm obj, Uint size, erts_shcopy_t *info,
*resp = obj;
} else {
Uint bsz = 0;
- *resp = copy_struct_x(obj, hbot - hp, &hp, off_heap, &bsz, NULL); /* copy literal */
+ *resp = copy_struct_x(obj, hbot - hp, &hp, off_heap, &bsz, NULL
+#ifdef ERTS_COPY_REGISTER_LOCATION
+ , file, line
+#endif
+ ); /* copy literal */
hbot -= bsz;
}
goto cleanup_next;
@@ -1461,7 +1479,11 @@ Uint copy_shared_perform(Eterm obj, Uint size, erts_shcopy_t *info,
*resp = obj;
} else {
Uint bsz = 0;
- *resp = copy_struct_x(obj, hbot - hp, &hp, off_heap, &bsz, NULL); /* copy literal */
+ *resp = copy_struct_x(obj, hbot - hp, &hp, off_heap, &bsz, NULL
+#ifdef ERTS_COPY_REGISTER_LOCATION
+ , file, line
+#endif
+ ); /* copy literal */
hbot -= bsz;
}
goto cleanup_next;
@@ -1660,7 +1682,12 @@ Uint copy_shared_perform(Eterm obj, Uint size, erts_shcopy_t *info,
case EXTERNAL_REF_SUBTAG:
{
ExternalThing *etp = (ExternalThing *) ptr;
+
+#if defined(ERTS_COPY_REGISTER_LOCATION) && defined(ERL_NODE_BOOKKEEP)
+ erts_ref_node_entry__(etp->node, 2, make_boxed(hp), file, line);
+#else
erts_ref_node_entry(etp->node, 2, make_boxed(hp));
+#endif
}
off_heap_node_container_common:
{
@@ -1823,8 +1850,12 @@ all_clean:
*
* NOTE: Assumes that term is a tuple (ptr is an untagged tuple ptr).
*/
-Eterm copy_shallow(Eterm* ERTS_RESTRICT ptr, Uint sz, Eterm** hpp,
- ErlOffHeap* off_heap)
+Eterm
+copy_shallow_x(Eterm* ERTS_RESTRICT ptr, Uint sz, Eterm** hpp, ErlOffHeap* off_heap
+#ifdef ERTS_COPY_REGISTER_LOCATION
+ , char *file, int line
+#endif
+ )
{
Eterm* tp = ptr;
Eterm* hp = *hpp;
@@ -1866,7 +1897,11 @@ Eterm copy_shallow(Eterm* ERTS_RESTRICT ptr, Uint sz, Eterm** hpp,
case EXTERNAL_REF_SUBTAG:
{
ExternalThing* etp = (ExternalThing *) (tp-1);
+#if defined(ERTS_COPY_REGISTER_LOCATION) && defined(ERL_NODE_BOOKKEEP)
+ erts_ref_node_entry__(etp->node, 2, make_boxed(hp-1), file, line);
+#else
erts_ref_node_entry(etp->node, 2, make_boxed(hp-1));
+#endif
}
off_heap_common:
{
diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c
index 83ef10cbec..dafe805a6f 100644
--- a/erts/emulator/beam/dist.c
+++ b/erts/emulator/beam/dist.c
@@ -1266,18 +1266,6 @@ erts_dsig_send_group_leader(ErtsDSigSendContext *ctx, Eterm leader, Eterm remote
return dsig_send_ctl(ctx, ctl);
}
-struct dist_sequences {
- ErlHeapFragment hfrag;
- struct dist_sequences *parent;
- struct dist_sequences *left;
- struct dist_sequences *right;
- char is_red;
-
- Uint64 seq_id;
- int cnt;
- Sint ctl_len;
-};
-
#define ERTS_RBT_PREFIX dist_seq
#define ERTS_RBT_T DistSeqNode
#define ERTS_RBT_KEY_T Uint
@@ -1312,25 +1300,25 @@ struct dist_sequences {
#include "erl_rbtree.h"
-struct erts_dist_seq_tree_foreach_iter_arg {
- int (*func)(ErtsDistExternal *, void *, Sint);
+struct erts_debug_dist_seq_tree_foreach_iter_arg {
+ int (*func)(DistSeqNode *, void *, Sint);
void *arg;
};
static int
-erts_dist_seq_tree_foreach_iter(DistSeqNode *seq, void *arg, Sint reds)
+erts_debug_dist_seq_tree_foreach_iter(DistSeqNode *seq, void *arg, Sint reds)
{
- struct erts_dist_seq_tree_foreach_iter_arg *state = arg;
- return state->func(erts_get_dist_ext(&seq->hfrag), state->arg, reds);
+ struct erts_debug_dist_seq_tree_foreach_iter_arg *state = arg;
+ return state->func(seq, state->arg, reds);
}
void
-erts_dist_seq_tree_foreach(DistEntry *dep, int (*func)(ErtsDistExternal *, void *, Sint), void *arg)
+erts_debug_dist_seq_tree_foreach(DistEntry *dep, int (*func)(DistSeqNode *, void *, Sint), void *arg)
{
- struct erts_dist_seq_tree_foreach_iter_arg state;
+ struct erts_debug_dist_seq_tree_foreach_iter_arg state;
state.func = func;
state.arg = arg;
- dist_seq_rbt_foreach(dep->sequences, erts_dist_seq_tree_foreach_iter, &state);
+ dist_seq_rbt_foreach(dep->sequences, erts_debug_dist_seq_tree_foreach_iter, &state);
}
static int dist_seq_cleanup(DistSeqNode *seq, void *unused, Sint reds)
diff --git a/erts/emulator/beam/dist.h b/erts/emulator/beam/dist.h
index f953a2ab8c..37ec88cc55 100644
--- a/erts/emulator/beam/dist.h
+++ b/erts/emulator/beam/dist.h
@@ -274,6 +274,18 @@ typedef struct erts_dsig_send_context {
typedef struct dist_sequences DistSeqNode;
+struct dist_sequences {
+ ErlHeapFragment hfrag;
+ struct dist_sequences *parent;
+ struct dist_sequences *left;
+ struct dist_sequences *right;
+ char is_red;
+
+ Uint64 seq_id;
+ int cnt;
+ Sint ctl_len;
+};
+
/*
* erts_dsig_send_* return values.
*/
@@ -306,9 +318,9 @@ extern Uint erts_dist_cache_size(void);
extern Sint erts_abort_connection_rwunlock(DistEntry *dep);
-extern void erts_dist_seq_tree_foreach(
+extern void erts_debug_dist_seq_tree_foreach(
DistEntry *dep,
- int (*func)(ErtsDistExternal *, void*, Sint), void *args);
+ int (*func)(DistSeqNode *, void*, Sint), void *args);
extern int erts_dsig_prepare(ErtsDSigSendContext *,
DistEntry*,
diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c
index b9f0334172..1bbc7d7f1e 100644
--- a/erts/emulator/beam/erl_alloc.c
+++ b/erts/emulator/beam/erl_alloc.c
@@ -653,8 +653,6 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop)
= erts_timer_type_size(ERTS_ALC_T_HL_PTIMER);
fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_BIF_TIMER)]
= erts_timer_type_size(ERTS_ALC_T_BIF_TIMER);
- fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_NIF_EXP_TRACE)]
- = sizeof(NifExportTrace);
fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_MREF_NSCHED_ENT)]
= sizeof(ErtsNSchedMagicRefTableEntry);
fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_MINDIRECTION)]
@@ -2392,10 +2390,6 @@ erts_memory(fmtfn_t *print_to_p, void *print_to_arg, void *proc, Eterm earg)
&size.processes_used,
fi,
ERTS_ALC_T_BIF_TIMER);
- add_fix_values(&size.processes,
- &size.processes_used,
- fi,
- ERTS_ALC_T_NIF_EXP_TRACE);
}
if (want.atom || want.atom_used) {
diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types
index 58d586453c..3e643a6223 100644
--- a/erts/emulator/beam/erl_alloc.types
+++ b/erts/emulator/beam/erl_alloc.types
@@ -149,6 +149,7 @@ type MODULE_TABLE LONG_LIVED CODE module_tab
type TAINT LONG_LIVED CODE taint_list
type MODULE_REFS STANDARD CODE module_refs
type NC_TMP TEMPORARY SYSTEM nc_tmp
+type NC_STD STANDARD SYSTEM nc_std
type TMP TEMPORARY SYSTEM tmp
type UNDEF SYSTEM SYSTEM undefined
type DCACHE STANDARD SYSTEM dcache
@@ -330,8 +331,7 @@ type DB_HEIR_DATA STANDARD ETS db_heir_data
type DB_MS_PSDO_PROC LONG_LIVED ETS db_match_pseudo_proc
type SCHDLR_DATA LONG_LIVED SYSTEM scheduler_data
-type NIF_TRAP_EXPORT STANDARD PROCESSES nif_trap_export_entry
-type NIF_EXP_TRACE FIXED_SIZE PROCESSES nif_export_trace
+type NFUNC_TRAP_WRAPPER STANDARD PROCESSES nfunc_trap_wrapper
type EXPORT LONG_LIVED CODE export_entry
type MONITOR FIXED_SIZE PROCESSES monitor
type MONITOR_SUSPEND STANDARD PROCESSES monitor_suspend
diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c
index 25ac3bc5af..bfc2f5992c 100644
--- a/erts/emulator/beam/erl_alloc_util.c
+++ b/erts/emulator/beam/erl_alloc_util.c
@@ -7112,12 +7112,21 @@ static int blockscan_cpool_yielding(blockscan_t *state)
return 0;
}
-static int blockscan_yield_helper(blockscan_t *state,
- int (*yielding_op)(blockscan_t*))
+/* */
+
+static int blockscan_finish(blockscan_t *state)
{
- /* Note that we don't check whether to abort here; only yielding_op knows
- * whether the carrier is still in the list/pool. */
+ if (ERTS_PROC_IS_EXITING(state->process)) {
+ state->abort(state->user_data);
+ return 0;
+ }
+ state->current_op = blockscan_finish;
+
+ return state->finish(state->user_data);
+}
+
+static void blockscan_lock_helper(blockscan_t *state) {
if ((state->allocator)->thread_safe) {
/* Locked scans have to be as short as possible. */
state->reductions = 1;
@@ -7126,34 +7135,18 @@ static int blockscan_yield_helper(blockscan_t *state,
} else {
state->reductions = BLOCKSCAN_REDUCTIONS;
}
+}
- if (yielding_op(state)) {
- state->next_op = state->current_op;
- }
-
+static void blockscan_unlock_helper(blockscan_t *state) {
if ((state->allocator)->thread_safe) {
erts_mtx_unlock(&(state->allocator)->mutex);
}
-
- return 1;
-}
-
-/* */
-
-static int blockscan_finish(blockscan_t *state)
-{
- if (ERTS_PROC_IS_EXITING(state->process)) {
- state->abort(state->user_data);
- return 0;
- }
-
- state->current_op = blockscan_finish;
-
- return state->finish(state->user_data);
}
static int blockscan_sweep_sbcs(blockscan_t *state)
{
+ blockscan_lock_helper(state);
+
if (state->current_op != blockscan_sweep_sbcs) {
SET_CARRIER_HDR(&state->dummy_carrier, 0, SCH_SBC, state->allocator);
state->current_clist = &(state->allocator)->sbc_list;
@@ -7163,11 +7156,19 @@ static int blockscan_sweep_sbcs(blockscan_t *state)
state->current_op = blockscan_sweep_sbcs;
state->next_op = blockscan_finish;
- return blockscan_yield_helper(state, blockscan_clist_yielding);
+ if (blockscan_clist_yielding(state)) {
+ state->next_op = state->current_op;
+ }
+
+ blockscan_unlock_helper(state);
+
+ return 1;
}
static int blockscan_sweep_mbcs(blockscan_t *state)
{
+ blockscan_lock_helper(state);
+
if (state->current_op != blockscan_sweep_mbcs) {
SET_CARRIER_HDR(&state->dummy_carrier, 0, SCH_MBC, state->allocator);
state->current_clist = &(state->allocator)->mbc_list;
@@ -7177,11 +7178,19 @@ static int blockscan_sweep_mbcs(blockscan_t *state)
state->current_op = blockscan_sweep_mbcs;
state->next_op = blockscan_sweep_sbcs;
- return blockscan_yield_helper(state, blockscan_clist_yielding);
+ if (blockscan_clist_yielding(state)) {
+ state->next_op = state->current_op;
+ }
+
+ blockscan_unlock_helper(state);
+
+ return 1;
}
static int blockscan_sweep_cpool(blockscan_t *state)
{
+ blockscan_lock_helper(state);
+
if (state->current_op != blockscan_sweep_cpool) {
SET_CARRIER_HDR(&state->dummy_carrier, 0, SCH_MBC, state->allocator);
state->cpool_cursor = (state->allocator)->cpool.sentinel;
@@ -7190,7 +7199,13 @@ static int blockscan_sweep_cpool(blockscan_t *state)
state->current_op = blockscan_sweep_cpool;
state->next_op = blockscan_sweep_mbcs;
- return blockscan_yield_helper(state, blockscan_cpool_yielding);
+ if (blockscan_cpool_yielding(state)) {
+ state->next_op = state->current_op;
+ }
+
+ blockscan_unlock_helper(state);
+
+ return 1;
}
static int blockscan_get_specific_allocator(int allocator_num,
diff --git a/erts/emulator/beam/erl_bif_binary.c b/erts/emulator/beam/erl_bif_binary.c
index 4d6d31cd76..b8e56390c1 100644
--- a/erts/emulator/beam/erl_bif_binary.c
+++ b/erts/emulator/beam/erl_bif_binary.c
@@ -1750,9 +1750,8 @@ static Eterm do_split_single_result(Process *p, Eterm subject, BinaryFindContext
Uint offset;
Uint bit_offset;
Uint bit_size;
- ErlSubBin *sb1;
- ErlSubBin *sb2;
- Eterm *hp;
+ Uint hp_need;
+ Eterm *hp, *hp_end;
Eterm ret;
pos = ff->pos;
@@ -1765,57 +1764,58 @@ static Eterm do_split_single_result(Process *p, Eterm subject, BinaryFindContext
if (pos == 0) {
ret = NIL;
} else {
- hp = HAlloc(p, (ERL_SUB_BIN_SIZE + 2));
+ Eterm extracted;
+
+ hp_need = EXTRACT_SUB_BIN_HEAP_NEED + 2;
+
+ hp = HAlloc(p, hp_need);
+ hp_end = hp + hp_need;
+
ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size);
- sb1 = (ErlSubBin *) hp;
- sb1->thing_word = HEADER_SUB_BIN;
- sb1->size = pos;
- sb1->offs = offset;
- sb1->orig = orig;
- sb1->bitoffs = bit_offset;
- sb1->bitsize = bit_size;
- sb1->is_writable = 0;
- hp += ERL_SUB_BIN_SIZE;
-
- ret = CONS(hp, make_binary(sb1), NIL);
+ extracted = erts_extract_sub_binary(&hp, orig, binary_bytes(orig),
+ offset * 8 + bit_offset,
+ pos * 8 + bit_size);
+
+ ret = CONS(hp, extracted, NIL);
hp += 2;
+
+ HRelease(p, hp_end, hp);
+
+ return ret;
}
} else {
- if ((ctx->flags & BF_FLAG_SPLIT_TRIM_ALL) && (pos == 0)) {
- hp = HAlloc(p, 1 * (ERL_SUB_BIN_SIZE + 2));
- ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size);
- sb1 = NULL;
- } else {
- hp = HAlloc(p, 2 * (ERL_SUB_BIN_SIZE + 2));
- ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size);
- sb1 = (ErlSubBin *) hp;
- sb1->thing_word = HEADER_SUB_BIN;
- sb1->size = pos;
- sb1->offs = offset;
- sb1->orig = orig;
- sb1->bitoffs = bit_offset;
- sb1->bitsize = 0;
- sb1->is_writable = 0;
- hp += ERL_SUB_BIN_SIZE;
- }
-
- sb2 = (ErlSubBin *) hp;
- sb2->thing_word = HEADER_SUB_BIN;
- sb2->size = orig_size - pos - len;
- sb2->offs = offset + pos + len;
- sb2->orig = orig;
- sb2->bitoffs = bit_offset;
- sb2->bitsize = bit_size;
- sb2->is_writable = 0;
- hp += ERL_SUB_BIN_SIZE;
-
- ret = CONS(hp, make_binary(sb2), NIL);
- hp += 2;
- if (sb1 != NULL) {
- ret = CONS(hp, make_binary(sb1), ret);
- hp += 2;
- }
+ Eterm first, rest;
+
+ hp_need = (EXTRACT_SUB_BIN_HEAP_NEED + 2) * 2;
+
+ hp = HAlloc(p, hp_need);
+ hp_end = hp + hp_need;
+
+ ERTS_GET_REAL_BIN(subject, orig, offset, bit_offset, bit_size);
+
+ if ((ctx->flags & BF_FLAG_SPLIT_TRIM_ALL) && (pos == 0)) {
+ first = NIL;
+ } else {
+ first = erts_extract_sub_binary(&hp, orig, binary_bytes(orig),
+ offset * 8 + bit_offset,
+ pos * 8);
+ }
+
+ rest = erts_extract_sub_binary(&hp, orig, binary_bytes(orig),
+ (offset + pos + len) * 8 + bit_offset,
+ (orig_size - pos - len) * 8 + bit_size);
+
+ ret = CONS(hp, rest, NIL);
+ hp += 2;
+
+ if (first != NIL) {
+ ret = CONS(hp, first, ret);
+ hp += 2;
+ }
+
+ HRelease(p, hp_end, hp);
}
+
return ret;
}
@@ -1829,7 +1829,9 @@ static Eterm do_split_global_result(Process *p, Eterm subject, BinaryFindContext
Uint offset;
Uint bit_offset;
Uint bit_size;
- ErlSubBin *sb;
+ Uint extracted_offset;
+ Uint extracted_size;
+ Eterm extracted;
Uint do_trim;
Sint i;
register Uint reds = ctx->reds;
@@ -1852,7 +1854,8 @@ static Eterm do_split_global_result(Process *p, Eterm subject, BinaryFindContext
*ctxp = ctx;
fa = &(ctx->u.fa);
}
- erts_factory_proc_prealloc_init(&(fa->factory), p, (fa->size + 1) * (ERL_SUB_BIN_SIZE + 2));
+ erts_factory_proc_prealloc_init(&(fa->factory), p, (fa->size + 1) *
+ (EXTRACT_SUB_BIN_HEAP_NEED + 2));
ctx->state = BFResult;
}
@@ -1871,39 +1874,39 @@ static Eterm do_split_global_result(Process *p, Eterm subject, BinaryFindContext
}
return THE_NON_VALUE;
}
- sb = (ErlSubBin *)(fa->factory.hp);
- sb->size = fa->end_pos - (fad[i].pos + fad[i].len);
- if (!(sb->size == 0 && do_trim)) {
- sb->thing_word = HEADER_SUB_BIN;
- sb->offs = offset + fad[i].pos + fad[i].len;
- sb->orig = orig;
- sb->bitoffs = bit_offset;
- sb->bitsize = 0;
- sb->is_writable = 0;
- fa->factory.hp += ERL_SUB_BIN_SIZE;
- fa->term = CONS(fa->factory.hp, make_binary(sb), fa->term);
- fa->factory.hp += 2;
- do_trim &= ~BF_FLAG_SPLIT_TRIM;
- }
- fa->end_pos = fad[i].pos;
+
+ extracted_offset = (offset + fad[i].pos + fad[i].len) * 8 + bit_offset;
+ extracted_size = (fa->end_pos - (fad[i].pos + fad[i].len)) * 8;
+
+ if (!(extracted_size == 0 && do_trim)) {
+ extracted = erts_extract_sub_binary(&fa->factory.hp, orig,
+ binary_bytes(orig),
+ extracted_offset,
+ extracted_size);
+ fa->term = CONS(fa->factory.hp, extracted, fa->term);
+ fa->factory.hp += 2;
+
+ do_trim &= ~BF_FLAG_SPLIT_TRIM;
+ }
+
+ fa->end_pos = fad[i].pos;
}
fa->head = i;
ctx->reds = reds;
- sb = (ErlSubBin *)(fa->factory.hp);
- sb->size = fad[0].pos;
- if (!(sb->size == 0 && do_trim)) {
- sb->thing_word = HEADER_SUB_BIN;
- sb->offs = offset;
- sb->orig = orig;
- sb->bitoffs = bit_offset;
- sb->bitsize = 0;
- sb->is_writable = 0;
- fa->factory.hp += ERL_SUB_BIN_SIZE;
- fa->term = CONS(fa->factory.hp, make_binary(sb), fa->term);
- fa->factory.hp += 2;
+ extracted_offset = offset * 8 + bit_offset;
+ extracted_size = fad[0].pos * 8;
+
+ if (!(extracted_size == 0 && do_trim)) {
+ extracted = erts_extract_sub_binary(&fa->factory.hp, orig,
+ binary_bytes(orig),
+ extracted_offset,
+ extracted_size);
+ fa->term = CONS(fa->factory.hp, extracted, fa->term);
+ fa->factory.hp += 2;
}
+
erts_factory_close(&(fa->factory));
return fa->term;
@@ -1937,8 +1940,8 @@ BIF_RETTYPE erts_binary_part(Process *p, Eterm binary, Eterm epos, Eterm elen)
Uint offset;
Uint bit_offset;
Uint bit_size;
- Eterm* hp;
- ErlSubBin* sb;
+ Eterm *hp, *hp_end;
+ Eterm result;
if (is_not_binary(binary)) {
goto badarg;
@@ -1970,19 +1973,18 @@ BIF_RETTYPE erts_binary_part(Process *p, Eterm binary, Eterm epos, Eterm elen)
goto badarg;
}
- hp = HeapFragOnlyAlloc(p, ERL_SUB_BIN_SIZE);
+ hp = HeapFragOnlyAlloc(p, EXTRACT_SUB_BIN_HEAP_NEED);
+ hp_end = hp + EXTRACT_SUB_BIN_HEAP_NEED;
ERTS_GET_REAL_BIN(binary, orig, offset, bit_offset, bit_size);
- sb = (ErlSubBin *) hp;
- sb->thing_word = HEADER_SUB_BIN;
- sb->size = len;
- sb->offs = offset + pos;
- sb->orig = orig;
- sb->bitoffs = bit_offset;
- sb->bitsize = 0;
- sb->is_writable = 0;
-
- BIF_RET(make_binary(sb));
+
+ result = erts_extract_sub_binary(&hp, orig, binary_bytes(orig),
+ (offset + pos) * 8 + bit_offset,
+ len * 8);
+
+ HRelease(p, hp_end, hp);
+
+ BIF_RET(result);
badarg:
BIF_ERROR(p, BADARG);
diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c
index aa55bdab6a..57fc5ec131 100644
--- a/erts/emulator/beam/erl_bif_info.c
+++ b/erts/emulator/beam/erl_bif_info.c
@@ -158,26 +158,28 @@ static Eterm os_version_tuple;
static Eterm
current_function(Process* p, ErtsHeapFactory *hfact, Process* rp,
int full_info, Uint reserve_size, int flags);
-static Eterm current_stacktrace(ErtsHeapFactory *hfact, Process* rp,
- Uint reserve_size);
-
static Eterm
-bld_bin_list(Uint **hpp, Uint *szp, ErlOffHeap* oh)
+current_stacktrace(Process* p, ErtsHeapFactory *hfact, Process* rp,
+ Uint reserve_size, int flags);
+
+Eterm
+erts_bld_bin_list(Uint **hpp, Uint *szp, ErlOffHeap* oh, Eterm tail)
{
- struct erl_off_heap_header* ohh;
- Eterm res = NIL;
+ union erl_off_heap_ptr u;
+ Eterm res = tail;
Eterm tuple;
+ struct erts_tmp_aligned_offheap tmp;
- for (ohh = oh->first; ohh; ohh = ohh->next) {
- if (ohh->thing_word == HEADER_PROC_BIN) {
- ProcBin* pb = (ProcBin*) ohh;
- Eterm val = erts_bld_uword(hpp, szp, (UWord) pb->val);
- Eterm orig_size = erts_bld_uint(hpp, szp, pb->val->orig_size);
+ for (u.hdr = oh->first; u.hdr; u.hdr = u.hdr->next) {
+ erts_align_offheap(&u, &tmp);
+ if (u.hdr->thing_word == HEADER_PROC_BIN) {
+ Eterm val = erts_bld_uword(hpp, szp, (UWord) u.pb->val);
+ Eterm orig_size = erts_bld_uint(hpp, szp, u.pb->val->orig_size);
if (szp)
*szp += 4+2;
if (hpp) {
- Uint refc = (Uint) erts_refc_read(&pb->val->intern.refc, 1);
+ Uint refc = (Uint) erts_refc_read(&u.pb->val->intern.refc, 1);
tuple = TUPLE3(*hpp, val, orig_size, make_small(refc));
res = CONS(*hpp + 4, tuple, res);
*hpp += 4+2;
@@ -1383,7 +1385,7 @@ process_info_aux(Process *c_p,
break;
case ERTS_PI_IX_CURRENT_STACKTRACE:
- res = current_stacktrace(hfact, rp, reserve_size);
+ res = current_stacktrace(c_p, hfact, rp, reserve_size, flags);
break;
case ERTS_PI_IX_INITIAL_CALL:
@@ -1864,11 +1866,25 @@ process_info_aux(Process *c_p,
break;
case ERTS_PI_IX_BINARY: {
- Uint sz = 0;
- (void) bld_bin_list(NULL, &sz, &MSO(rp));
+ ErlHeapFragment *hfrag;
+ Uint sz;
+
+ res = NIL;
+ sz = 0;
+
+ (void)erts_bld_bin_list(NULL, &sz, &MSO(rp), NIL);
+ for (hfrag = rp->mbuf; hfrag != NULL; hfrag = hfrag->next) {
+ (void)erts_bld_bin_list(NULL, &sz, &hfrag->off_heap, NIL);
+ }
+
hp = erts_produce_heap(hfact, sz, reserve_size);
- res = bld_bin_list(&hp, NULL, &MSO(rp));
- break;
+
+ res = erts_bld_bin_list(&hp, NULL, &MSO(rp), NIL);
+ for (hfrag = rp->mbuf; hfrag != NULL; hfrag = hfrag->next) {
+ res = erts_bld_bin_list(&hp, NULL, &hfrag->off_heap, res);
+ }
+
+ break;
}
case ERTS_PI_IX_SEQUENTIAL_TRACE_TOKEN: {
@@ -2007,19 +2023,23 @@ current_function(Process *c_p, ErtsHeapFactory *hfact, Process* rp,
}
if (c_p == rp && !(flags & ERTS_PI_FLAG_REQUEST_FOR_OTHER)) {
- FunctionInfo fi2;
+ BeamInstr* return_address;
+ FunctionInfo caller_fi;
- /*
- * The current function is erlang:process_info/{1,2},
- * which is not the answer that the application want.
- * We will use the function pointed into by rp->cp
- * instead if it can be looked up.
- */
- erts_lookup_function_info(&fi2, rp->cp, full_info);
- if (fi2.mfa) {
- fi = fi2;
- rp->current = fi2.mfa;
- }
+ /*
+ * The current function is erlang:process_info/{1,2}, and we've
+ * historically returned the *calling* function in that case. We
+ * therefore use the continuation pointer stored at the top of the
+ * stack instead, which is safe since process_info is a "heavy" BIF
+ * that is only called through its export entry.
+ */
+ return_address = erts_printable_return_address(rp, STACK_TOP(rp));
+
+ erts_lookup_function_info(&caller_fi, return_address, full_info);
+ if (caller_fi.mfa) {
+ fi = caller_fi;
+ rp->current = caller_fi.mfa;
+ }
}
/*
@@ -2040,8 +2060,8 @@ current_function(Process *c_p, ErtsHeapFactory *hfact, Process* rp,
}
static Eterm
-current_stacktrace(ErtsHeapFactory *hfact, Process* rp,
- Uint reserve_size)
+current_stacktrace(Process *p, ErtsHeapFactory *hfact, Process* rp,
+ Uint reserve_size, int flags)
{
Uint sz;
struct StackTrace* s;
@@ -2058,13 +2078,14 @@ current_stacktrace(ErtsHeapFactory *hfact, Process* rp,
sz = offsetof(struct StackTrace, trace) + sizeof(BeamInstr *)*depth;
s = (struct StackTrace *) erts_alloc(ERTS_ALC_T_TMP, sz);
s->depth = 0;
- if (depth > 0 && rp->i) {
- s->trace[s->depth++] = rp->i;
- depth--;
- }
- if (depth > 0 && rp->cp != 0) {
- s->trace[s->depth++] = rp->cp - 1;
- depth--;
+ s->pc = NULL;
+
+ /* We skip current pc when requesting our own stack trace since it will
+ * inevitably point to process_info/1,2 */
+ if ((p != rp || (flags & ERTS_PI_FLAG_REQUEST_FOR_OTHER)) &&
+ depth > 0 && rp->i) {
+ s->trace[s->depth++] = rp->i;
+ depth--;
}
erts_save_stacktrace(rp, s, depth);
diff --git a/erts/emulator/beam/erl_bif_lists.c b/erts/emulator/beam/erl_bif_lists.c
index fa2edfef1e..9d485abc35 100644
--- a/erts/emulator/beam/erl_bif_lists.c
+++ b/erts/emulator/beam/erl_bif_lists.c
@@ -32,8 +32,46 @@
#include "bif.h"
#include "erl_binary.h"
+static Export plusplus_trap_export;
+static Export append_trap_export;
-static Eterm keyfind(int Bif, Process* p, Eterm Key, Eterm Pos, Eterm List);
+static Export minusminus_trap_export;
+static Export subtract_trap_export;
+
+static Export member_trap_export;
+
+static Export reverse_trap_export;
+
+static Export keymember_trap_export;
+static Export keysearch_trap_export;
+static Export keyfind_trap_export;
+
+void erts_init_bif_lists(void) {
+ erts_init_trap_export(&plusplus_trap_export, am_erlang, am_PlusPlus, 2,
+ ebif_plusplus_2);
+ erts_init_trap_export(&append_trap_export, am_erlang, am_append, 2,
+ append_2);
+
+ erts_init_trap_export(&minusminus_trap_export, am_erlang, am_MinusMinus, 2,
+ ebif_minusminus_2);
+ erts_init_trap_export(&subtract_trap_export, am_lists, am_subtract, 2,
+ subtract_2);
+
+ erts_init_trap_export(&reverse_trap_export, am_lists, am_reverse, 2,
+ lists_reverse_2);
+
+ erts_init_trap_export(&member_trap_export, am_lists, am_member, 2,
+ lists_member_2);
+
+ erts_init_trap_export(&keymember_trap_export, am_lists, am_keymember, 3,
+ lists_keymember_3);
+ erts_init_trap_export(&keysearch_trap_export, am_lists, am_keysearch, 3,
+ lists_keysearch_3);
+ erts_init_trap_export(&keyfind_trap_export, am_lists, am_keyfind, 3,
+ lists_keyfind_3);
+}
+
+static Eterm keyfind(Export* Bif, Process* p, Eterm Key, Eterm Pos, Eterm List);
/* erlang:'++'/2
*
@@ -308,12 +346,12 @@ static Eterm append(Export *bif_entry, BIF_ALIST_2) {
Eterm
ebif_plusplus_2(BIF_ALIST_2)
{
- return append(bif_export[BIF_ebif_plusplus_2], BIF_CALL_ARGS);
+ return append(&plusplus_trap_export, BIF_CALL_ARGS);
}
BIF_RETTYPE append_2(BIF_ALIST_2)
{
- return append(bif_export[BIF_append_2], BIF_CALL_ARGS);
+ return append(&append_trap_export, BIF_CALL_ARGS);
}
/* erlang:'--'/2
@@ -1039,11 +1077,11 @@ static Eterm subtract(Export *bif_entry, BIF_ALIST_2) {
}
BIF_RETTYPE ebif_minusminus_2(BIF_ALIST_2) {
- return subtract(bif_export[BIF_ebif_minusminus_2], BIF_CALL_ARGS);
+ return subtract(&minusminus_trap_export, BIF_CALL_ARGS);
}
BIF_RETTYPE subtract_2(BIF_ALIST_2) {
- return subtract(bif_export[BIF_subtract_2], BIF_CALL_ARGS);
+ return subtract(&subtract_trap_export, BIF_CALL_ARGS);
}
@@ -1068,7 +1106,7 @@ BIF_RETTYPE lists_member_2(BIF_ALIST_2)
while (is_list(list)) {
if (--max_iter < 0) {
BUMP_ALL_REDS(BIF_P);
- BIF_TRAP2(bif_export[BIF_lists_member_2], BIF_P, term, list);
+ BIF_TRAP2(&member_trap_export, BIF_P, term, list);
}
item = CAR(list_val(list));
if ((item == term) || (non_immed_key && eq(item, term))) {
@@ -1130,7 +1168,7 @@ static BIF_RETTYPE lists_reverse_alloc(Process *c_p,
}
ASSERT(is_list(tail) && cells_left == 0);
- BIF_TRAP2(bif_export[BIF_lists_reverse_2], c_p, list, tail);
+ BIF_TRAP2(&reverse_trap_export, c_p, list, tail);
}
static BIF_RETTYPE lists_reverse_onheap(Process *c_p,
@@ -1179,7 +1217,7 @@ static BIF_RETTYPE lists_reverse_onheap(Process *c_p,
}
BUMP_ALL_REDS(c_p);
- BIF_TRAP2(bif_export[BIF_lists_reverse_2], c_p, list, tail);
+ BIF_TRAP2(&reverse_trap_export, c_p, list, tail);
}
BIF_ERROR(c_p, BADARG);
@@ -1209,7 +1247,7 @@ lists_keymember_3(BIF_ALIST_3)
{
Eterm res;
- res = keyfind(BIF_lists_keymember_3, BIF_P,
+ res = keyfind(&keymember_trap_export, BIF_P,
BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
if (is_value(res) && is_tuple(res)) {
return am_true;
@@ -1223,7 +1261,7 @@ lists_keysearch_3(BIF_ALIST_3)
{
Eterm res;
- res = keyfind(BIF_lists_keysearch_3, BIF_P,
+ res = keyfind(&keysearch_trap_export, BIF_P,
BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
if (is_non_value(res) || is_not_tuple(res)) {
return res;
@@ -1236,12 +1274,12 @@ lists_keysearch_3(BIF_ALIST_3)
BIF_RETTYPE
lists_keyfind_3(BIF_ALIST_3)
{
- return keyfind(BIF_lists_keyfind_3, BIF_P,
+ return keyfind(&keyfind_trap_export, BIF_P,
BIF_ARG_1, BIF_ARG_2, BIF_ARG_3);
}
static Eterm
-keyfind(int Bif, Process* p, Eterm Key, Eterm Pos, Eterm List)
+keyfind(Export *Bif, Process* p, Eterm Key, Eterm Pos, Eterm List)
{
int max_iter = 10 * CONTEXT_REDS;
Sint pos;
@@ -1257,7 +1295,7 @@ keyfind(int Bif, Process* p, Eterm Key, Eterm Pos, Eterm List)
while (is_list(List)) {
if (--max_iter < 0) {
BUMP_ALL_REDS(p);
- BIF_TRAP3(bif_export[Bif], p, Key, Pos, List);
+ BIF_TRAP3(Bif, p, Key, Pos, List);
}
term = CAR(list_val(List));
List = CDR(list_val(List));
@@ -1282,7 +1320,7 @@ keyfind(int Bif, Process* p, Eterm Key, Eterm Pos, Eterm List)
while (is_list(List)) {
if (--max_iter < 0) {
BUMP_ALL_REDS(p);
- BIF_TRAP3(bif_export[Bif], p, Key, Pos, List);
+ BIF_TRAP3(Bif, p, Key, Pos, List);
}
term = CAR(list_val(List));
List = CDR(list_val(List));
@@ -1300,7 +1338,7 @@ keyfind(int Bif, Process* p, Eterm Key, Eterm Pos, Eterm List)
while (is_list(List)) {
if (--max_iter < 0) {
BUMP_ALL_REDS(p);
- BIF_TRAP3(bif_export[Bif], p, Key, Pos, List);
+ BIF_TRAP3(Bif, p, Key, Pos, List);
}
term = CAR(list_val(List));
List = CDR(list_val(List));
diff --git a/erts/emulator/beam/erl_bif_persistent.c b/erts/emulator/beam/erl_bif_persistent.c
index f38e0cc5cb..9dc5c66a0a 100644
--- a/erts/emulator/beam/erl_bif_persistent.c
+++ b/erts/emulator/beam/erl_bif_persistent.c
@@ -1167,6 +1167,7 @@ release_update_permission(int release_updater)
erts_resume(updater_process, ERTS_PROC_LOCK_STATUS);
}
erts_proc_unlock(updater_process, ERTS_PROC_LOCK_STATUS);
+ erts_proc_dec_refc(updater_process);
}
updater_process = NULL;
@@ -1193,6 +1194,7 @@ suspend_updater(Process* c_p)
ASSERT(updater_process == c_p);
erts_mtx_unlock(&update_table_permission_mtx);
#endif
+ erts_proc_inc_refc(c_p);
erts_suspend(c_p, ERTS_PROC_LOCK_MAIN, NULL);
}
@@ -1234,3 +1236,102 @@ next_to_delete(void)
erts_mtx_unlock(&delete_queue_mtx);
return table;
}
+
+/*
+ * test/debug functionality follow...
+ */
+
+static Uint accessed_literal_areas_size;
+static Uint accessed_no_literal_areas;
+static ErtsLiteralArea **accessed_literal_areas;
+
+int
+erts_debug_have_accessed_literal_area(ErtsLiteralArea *lap)
+{
+ Uint i;
+ for (i = 0; i < accessed_no_literal_areas; i++) {
+ if (accessed_literal_areas[i] == lap)
+ return !0;
+ }
+ return 0;
+}
+
+void
+erts_debug_save_accessed_literal_area(ErtsLiteralArea *lap)
+{
+ if (accessed_no_literal_areas == accessed_literal_areas_size) {
+ accessed_literal_areas_size += 10;
+ accessed_literal_areas = erts_realloc(ERTS_ALC_T_TMP,
+ accessed_literal_areas,
+ (sizeof(ErtsLiteralArea *)
+ *accessed_literal_areas_size));
+ }
+ accessed_literal_areas[accessed_no_literal_areas++] = lap;
+}
+
+static void debug_foreach_off_heap(HashTable *tbl, void (*func)(ErlOffHeap *, void *), void *arg)
+{
+ int i;
+
+ for (i = 0; i < tbl->allocated; i++) {
+ Eterm term = tbl->term[i];
+ if (is_tuple_arity(term, 2)) {
+ ErtsLiteralArea *lap = term_to_area(term);
+ ErlOffHeap oh;
+ if (!erts_debug_have_accessed_literal_area(lap)) {
+ ERTS_INIT_OFF_HEAP(&oh);
+ oh.first = lap->off_heap;
+ (*func)(&oh, arg);
+ erts_debug_save_accessed_literal_area(lap);
+ }
+ }
+ }
+}
+
+struct debug_la_oh {
+ void (*func)(ErlOffHeap *, void *);
+ void *arg;
+};
+
+static void debug_handle_table(void *vfap,
+ ErtsThrPrgrVal val,
+ void *vtbl)
+{
+ struct debug_la_oh *fap = vfap;
+ HashTable *tbl = vtbl;
+ debug_foreach_off_heap(tbl, fap->func, fap->arg);
+}
+
+void
+erts_debug_foreach_persistent_term_off_heap(void (*func)(ErlOffHeap *, void *), void *arg)
+{
+ HashTable *tbl;
+ struct debug_la_oh fa;
+ accessed_no_literal_areas = 0;
+ accessed_literal_areas_size = 10;
+ accessed_literal_areas = erts_alloc(ERTS_ALC_T_TMP,
+ (sizeof(ErtsLiteralArea *)
+ * accessed_literal_areas_size));
+
+ tbl = (HashTable *) erts_atomic_read_nob(&the_hash_table);
+ debug_foreach_off_heap(tbl, func, arg);
+ erts_mtx_lock(&delete_queue_mtx);
+ for (tbl = delete_queue_head; tbl; tbl = tbl->delete_next)
+ debug_foreach_off_heap(tbl, func, arg);
+ erts_mtx_unlock(&delete_queue_mtx);
+ fa.func = func;
+ fa.arg = arg;
+ erts_debug_later_op_foreach(table_updater,
+ debug_handle_table,
+ (void *) &fa);
+ erts_debug_later_op_foreach(table_deleter,
+ debug_handle_table,
+ (void *) &fa);
+ erts_debug_foreach_release_literal_area_off_heap(func, arg);
+
+ erts_free(ERTS_ALC_T_TMP, accessed_literal_areas);
+ accessed_no_literal_areas = 0;
+ accessed_literal_areas_size = 0;
+ accessed_literal_areas = NULL;
+}
+
diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c
index ed825d3dda..dd1e884705 100644
--- a/erts/emulator/beam/erl_bif_port.c
+++ b/erts/emulator/beam/erl_bif_port.c
@@ -44,6 +44,7 @@
#include "erl_bif_unique.h"
#include "dtrace-wrapper.h"
#include "erl_proc_sig_queue.h"
+#include "erl_osenv.h"
static Port *open_port(Process* p, Eterm name, Eterm settings, int *err_typep, int *err_nump);
static int merge_global_environment(erts_osenv_t *env, Eterm key_value_pairs);
diff --git a/erts/emulator/beam/erl_bif_trace.c b/erts/emulator/beam/erl_bif_trace.c
index 80ba7d1b3c..e03c97fe10 100644
--- a/erts/emulator/beam/erl_bif_trace.c
+++ b/erts/emulator/beam/erl_bif_trace.c
@@ -80,9 +80,6 @@ static Eterm trace_info_func(Process* p, Eterm pid_spec, Eterm key);
static Eterm trace_info_on_load(Process* p, Eterm key);
static Eterm trace_info_event(Process* p, Eterm event, Eterm key);
-
-static void reset_bif_trace(void);
-static void setup_bif_trace(void);
static void install_exp_breakpoints(BpFunctions* f);
static void uninstall_exp_breakpoints(BpFunctions* f);
static void clean_export_entries(BpFunctions* f);
@@ -1047,14 +1044,13 @@ static int function_is_traced(Process *p,
e.info.mfa.function = mfa[1];
e.info.mfa.arity = mfa[2];
if ((ep = export_get(&e)) != NULL) {
- pc = ep->beam;
+ pc = ep->trampoline.raw;
if (ep->addressv[erts_active_code_ix()] == pc &&
! BeamIsOpCode(*pc, op_call_error_handler)) {
int r = 0;
- ASSERT(BeamIsOpCode(*pc, op_apply_bif) ||
- BeamIsOpCode(*pc, op_i_generic_breakpoint));
+ ASSERT(BeamIsOpCode(*pc, op_i_generic_breakpoint));
if (erts_is_trace_break(&ep->info, ms, 0)) {
return FUNC_TRACE_GLOBAL_TRACE;
@@ -1426,18 +1422,21 @@ erts_set_trace_pattern(Process*p, ErtsCodeMFA *mfa, int specified,
int n;
BpFunction* fp;
- /*
- * First work on normal functions (not real BIFs).
- */
-
erts_bp_match_export(&finish_bp.e, mfa, specified);
fp = finish_bp.e.matching;
n = finish_bp.e.matched;
for (i = 0; i < n; i++) {
ErtsCodeInfo *ci = fp[i].ci;
- BeamInstr* pc = erts_codeinfo_to_code(ci);
- Export* ep = ErtsContainerStruct(ci, Export, info);
+ BeamInstr* pc;
+ Export* ep;
+
+ pc = erts_codeinfo_to_code(ci);
+ ep = ErtsContainerStruct(ci, Export, info);
+
+ if (ep->bif_table_index != -1) {
+ ep->is_bif_traced = !!on;
+ }
if (on && !flags.breakpoint) {
/* Turn on global call tracing */
@@ -1446,12 +1445,12 @@ erts_set_trace_pattern(Process*p, ErtsCodeMFA *mfa, int specified,
#ifdef DEBUG
ep->info.op = BeamOpCodeAddr(op_i_func_info_IaaI);
#endif
- ep->beam[0] = BeamOpCodeAddr(op_trace_jump_W);
- ep->beam[1] = (BeamInstr) ep->addressv[code_ix];
+ ep->trampoline.op = BeamOpCodeAddr(op_trace_jump_W);
+ ep->trampoline.trace.address = (BeamInstr) ep->addressv[code_ix];
}
- erts_set_call_trace_bif(ci, match_prog_set, 0);
+ erts_set_export_trace(ci, match_prog_set, 0);
if (ep->addressv[code_ix] != pc) {
- ep->beam[0] = BeamOpCodeAddr(op_i_generic_breakpoint);
+ ep->trampoline.op = BeamOpCodeAddr(op_i_generic_breakpoint);
}
} else if (!on && flags.breakpoint) {
/* Turn off breakpoint tracing -- nothing to do here. */
@@ -1460,91 +1459,14 @@ erts_set_trace_pattern(Process*p, ErtsCodeMFA *mfa, int specified,
* Turn off global tracing, either explicitly or implicitly
* before turning on breakpoint tracing.
*/
- erts_clear_call_trace_bif(ci, 0);
- if (BeamIsOpCode(ep->beam[0], op_i_generic_breakpoint)) {
- ep->beam[0] = BeamOpCodeAddr(op_trace_jump_W);
+ erts_clear_export_trace(ci, 0);
+ if (BeamIsOpCode(ep->trampoline.op, op_i_generic_breakpoint)) {
+ ep->trampoline.op = BeamOpCodeAddr(op_trace_jump_W);
}
}
}
/*
- ** OK, now for the bif's
- */
- for (i = 0; i < BIF_SIZE; ++i) {
- Export *ep = bif_export[i];
-
- if (!ExportIsBuiltIn(ep)) {
- continue;
- }
-
- if (bif_table[i].f == bif_table[i].traced) {
- /* Trace wrapper same as regular function - untraceable */
- continue;
- }
-
- switch (specified) {
- case 3:
- if (mfa->arity != ep->info.mfa.arity)
- continue;
- case 2:
- if (mfa->function != ep->info.mfa.function)
- continue;
- case 1:
- if (mfa->module != ep->info.mfa.module)
- continue;
- case 0:
- break;
- default:
- ASSERT(0);
- }
-
- if (! flags.breakpoint) { /* Export entry call trace */
- if (on) {
- erts_clear_call_trace_bif(&ep->info, 1);
- erts_clear_mtrace_bif(&ep->info);
- erts_set_call_trace_bif(&ep->info, match_prog_set, 0);
- } else { /* off */
- erts_clear_call_trace_bif(&ep->info, 0);
- }
- matches++;
- } else { /* Breakpoint call trace */
- int m = 0;
-
- if (on) {
- if (flags.local) {
- erts_clear_call_trace_bif(&ep->info, 0);
- erts_set_call_trace_bif(&ep->info, match_prog_set, 1);
- m = 1;
- }
- if (flags.meta) {
- erts_set_mtrace_bif(&ep->info, meta_match_prog_set,
- meta_tracer);
- m = 1;
- }
- if (flags.call_time) {
- erts_set_time_trace_bif(&ep->info, on);
- /* I don't want to remove any other tracers */
- m = 1;
- }
- } else { /* off */
- if (flags.local) {
- erts_clear_call_trace_bif(&ep->info, 1);
- m = 1;
- }
- if (flags.meta) {
- erts_clear_mtrace_bif(&ep->info);
- m = 1;
- }
- if (flags.call_time) {
- erts_clear_time_trace_bif(&ep->info);
- m = 1;
- }
- }
- matches += m;
- }
- }
-
- /*
** So, now for breakpoint tracing
*/
erts_bp_match_functions(&finish_bp.f, mfa, specified);
@@ -1670,7 +1592,6 @@ erts_finish_breakpointing(void)
install_exp_breakpoints(&finish_bp.e);
}
}
- setup_bif_trace();
return 1;
case 1:
/*
@@ -1699,7 +1620,6 @@ erts_finish_breakpointing(void)
uninstall_exp_breakpoints(&finish_bp.e);
}
}
- reset_bif_trace();
return 1;
case 3:
/*
@@ -1710,7 +1630,6 @@ erts_finish_breakpointing(void)
* updated). If any breakpoints have been totally disabled,
* deallocate the GenericBp structs for them.
*/
- erts_consolidate_bif_bp_data();
clean_export_entries(&finish_bp.e);
erts_consolidate_bp_data(&finish_bp.e, 0);
erts_consolidate_bp_data(&finish_bp.f, 1);
@@ -1736,7 +1655,7 @@ install_exp_breakpoints(BpFunctions* f)
for (i = 0; i < ne; i++) {
Export* ep = ErtsContainerStruct(fp[i].ci, Export, info);
- ep->addressv[code_ix] = ep->beam;
+ ep->addressv[code_ix] = ep->trampoline.raw;
}
}
@@ -1751,11 +1670,12 @@ uninstall_exp_breakpoints(BpFunctions* f)
for (i = 0; i < ne; i++) {
Export* ep = ErtsContainerStruct(fp[i].ci, Export, info);
- if (ep->addressv[code_ix] != ep->beam) {
- continue;
- }
- ASSERT(BeamIsOpCode(ep->beam[0], op_trace_jump_W));
- ep->addressv[code_ix] = (BeamInstr *) ep->beam[1];
+ if (ep->addressv[code_ix] != ep->trampoline.raw) {
+ continue;
+ }
+
+ ASSERT(BeamIsOpCode(ep->trampoline.op, op_trace_jump_W));
+ ep->addressv[code_ix] = (BeamInstr *) ep->trampoline.trace.address;
}
}
@@ -1770,48 +1690,14 @@ clean_export_entries(BpFunctions* f)
for (i = 0; i < ne; i++) {
Export* ep = ErtsContainerStruct(fp[i].ci, Export, info);
- if (ep->addressv[code_ix] == ep->beam) {
- continue;
- }
- if (BeamIsOpCode(ep->beam[0], op_trace_jump_W)) {
- ep->beam[0] = (BeamInstr) 0;
- ep->beam[1] = (BeamInstr) 0;
- }
- }
-}
-
-static void
-setup_bif_trace(void)
-{
- int i;
-
- for (i = 0; i < BIF_SIZE; ++i) {
- Export *ep = bif_export[i];
- GenericBp* g = ep->info.u.gen_bp;
- if (g) {
- if (ExportIsBuiltIn(ep)) {
- ASSERT(ep->beam[1]);
- ep->beam[1] = (BeamInstr) bif_table[i].traced;
- }
- }
- }
-}
+ if (ep->addressv[code_ix] == ep->trampoline.raw) {
+ continue;
+ }
-static void
-reset_bif_trace(void)
-{
- int i;
- ErtsBpIndex active = erts_active_bp_ix();
-
- for (i = 0; i < BIF_SIZE; ++i) {
- Export *ep = bif_export[i];
- GenericBp* g = ep->info.u.gen_bp;
- if (g && g->data[active].flags == 0) {
- if (ExportIsBuiltIn(ep)) {
- ASSERT(ep->beam[1]);
- ep->beam[1] = (BeamInstr) bif_table[i].f;
- }
- }
+ if (BeamIsOpCode(ep->trampoline.op, op_trace_jump_W)) {
+ ep->trampoline.op = (BeamInstr) 0;
+ ep->trampoline.trace.address = (BeamInstr) 0;
+ }
}
}
diff --git a/erts/emulator/beam/erl_binary.h b/erts/emulator/beam/erl_binary.h
index c9c047255a..f5a35f2222 100644
--- a/erts/emulator/beam/erl_binary.h
+++ b/erts/emulator/beam/erl_binary.h
@@ -311,6 +311,7 @@ ERTS_GLB_INLINE void erts_free_aligned_binary_bytes(byte* buf);
ERTS_GLB_INLINE void erts_free_aligned_binary_bytes_extra(byte* buf, ErtsAlcType_t);
ERTS_GLB_INLINE Binary *erts_bin_drv_alloc_fnf(Uint size);
ERTS_GLB_INLINE Binary *erts_bin_drv_alloc(Uint size);
+ERTS_GLB_INLINE Binary *erts_bin_nrml_alloc_fnf(Uint size);
ERTS_GLB_INLINE Binary *erts_bin_nrml_alloc(Uint size);
ERTS_GLB_INLINE Binary *erts_bin_realloc_fnf(Binary *bp, Uint size);
ERTS_GLB_INLINE Binary *erts_bin_realloc(Binary *bp, Uint size);
@@ -351,6 +352,19 @@ erts_free_aligned_binary_bytes(byte* buf)
erts_free_aligned_binary_bytes_extra(buf,ERTS_ALC_T_TMP);
}
+/* A binary's size in bits must fit into a word for matching to work. We used
+ * to allow creating larger binaries than this, but they acted really strangely
+ * in Erlang code and were pretty much only usable in drivers and NIFs.
+ *
+ * This check also ensures, indirectly, that there won't be an overflow when
+ * the size is bumped by CHICKEN_PAD and the binary struct itself. */
+#define BINARY_OVERFLOW_CHECK(BYTE_SIZE) \
+ do { \
+ if (ERTS_UNLIKELY(BYTE_SIZE > ERTS_UWORD_MAX / CHAR_BIT)) { \
+ return NULL; \
+ } \
+ } while(0)
+
/* Explicit extra bytes allocated to counter buggy drivers.
** These extra bytes where earlier (< R13B04) added by an alignment-bug
** in this code. Do we dare remove this in some major release (R14?) maybe?
@@ -364,86 +378,107 @@ erts_free_aligned_binary_bytes(byte* buf)
ERTS_GLB_INLINE Binary *
erts_bin_drv_alloc_fnf(Uint size)
{
- Uint bsize = ERTS_SIZEOF_Binary(size) + CHICKEN_PAD;
Binary *res;
+ Uint bsize;
+
+ BINARY_OVERFLOW_CHECK(size);
+ bsize = ERTS_SIZEOF_Binary(size) + CHICKEN_PAD;
- if (bsize < size) /* overflow */
- return NULL;
res = erts_alloc_fnf(ERTS_ALC_T_DRV_BINARY, bsize);
ERTS_CHK_BIN_ALIGNMENT(res);
+
if (res) {
- res->orig_size = size;
- res->intern.flags = BIN_FLAG_DRV;
+ res->orig_size = size;
+ res->intern.flags = BIN_FLAG_DRV;
erts_refc_init(&res->intern.refc, 1);
}
+
return res;
}
ERTS_GLB_INLINE Binary *
erts_bin_drv_alloc(Uint size)
{
- Uint bsize = ERTS_SIZEOF_Binary(size) + CHICKEN_PAD;
+ Binary *res = erts_bin_drv_alloc_fnf(size);
+
+ if (res) {
+ return res;
+ }
+
+ erts_alloc_enomem(ERTS_ALC_T_DRV_BINARY, size);
+}
+
+ERTS_GLB_INLINE Binary *
+erts_bin_nrml_alloc_fnf(Uint size)
+{
Binary *res;
+ Uint bsize;
- if (bsize < size) /* overflow */
- erts_alloc_enomem(ERTS_ALC_T_DRV_BINARY, size);
- res = erts_alloc(ERTS_ALC_T_DRV_BINARY, bsize);
+ BINARY_OVERFLOW_CHECK(size);
+ bsize = ERTS_SIZEOF_Binary(size) + CHICKEN_PAD;
+
+ res = erts_alloc_fnf(ERTS_ALC_T_BINARY, bsize);
ERTS_CHK_BIN_ALIGNMENT(res);
- res->orig_size = size;
- res->intern.flags = BIN_FLAG_DRV;
- erts_refc_init(&res->intern.refc, 1);
+
+ if (res) {
+ res->orig_size = size;
+ res->intern.flags = 0;
+ erts_refc_init(&res->intern.refc, 1);
+ }
+
return res;
}
ERTS_GLB_INLINE Binary *
erts_bin_nrml_alloc(Uint size)
{
- Uint bsize = ERTS_SIZEOF_Binary(size) + CHICKEN_PAD;
- Binary *res;
+ Binary *res = erts_bin_drv_alloc_fnf(size);
- if (bsize < size) /* overflow */
- erts_alloc_enomem(ERTS_ALC_T_BINARY, size);
- res = erts_alloc(ERTS_ALC_T_BINARY, bsize);
- ERTS_CHK_BIN_ALIGNMENT(res);
- res->orig_size = size;
- res->intern.flags = 0;
- erts_refc_init(&res->intern.refc, 1);
- return res;
+ if (res) {
+ return res;
+ }
+
+ erts_alloc_enomem(ERTS_ALC_T_BINARY, size);
}
ERTS_GLB_INLINE Binary *
erts_bin_realloc_fnf(Binary *bp, Uint size)
{
+ ErtsAlcType_t type;
Binary *nbp;
- Uint bsize = ERTS_SIZEOF_Binary(size) + CHICKEN_PAD;
- ErtsAlcType_t type = (bp->intern.flags & BIN_FLAG_DRV) ? ERTS_ALC_T_DRV_BINARY
- : ERTS_ALC_T_BINARY;
+ Uint bsize;
+
+ type = (bp->intern.flags & BIN_FLAG_DRV) ? ERTS_ALC_T_DRV_BINARY
+ : ERTS_ALC_T_BINARY;
ASSERT((bp->intern.flags & BIN_FLAG_MAGIC) == 0);
- if (bsize < size) /* overflow */
- return NULL;
+
+ BINARY_OVERFLOW_CHECK(size);
+ bsize = ERTS_SIZEOF_Binary(size) + CHICKEN_PAD;
+
nbp = erts_realloc_fnf(type, (void *) bp, bsize);
ERTS_CHK_BIN_ALIGNMENT(nbp);
- if (nbp)
- nbp->orig_size = size;
+
+ if (nbp) {
+ nbp->orig_size = size;
+ }
+
return nbp;
}
ERTS_GLB_INLINE Binary *
erts_bin_realloc(Binary *bp, Uint size)
{
- Binary *nbp;
- Uint bsize = ERTS_SIZEOF_Binary(size) + CHICKEN_PAD;
- ErtsAlcType_t type = (bp->intern.flags & BIN_FLAG_DRV) ? ERTS_ALC_T_DRV_BINARY
- : ERTS_ALC_T_BINARY;
- ASSERT((bp->intern.flags & BIN_FLAG_MAGIC) == 0);
- if (bsize < size) /* overflow */
- erts_realloc_enomem(type, bp, size);
- nbp = erts_realloc_fnf(type, (void *) bp, bsize);
- if (!nbp)
- erts_realloc_enomem(type, bp, bsize);
- ERTS_CHK_BIN_ALIGNMENT(nbp);
- nbp->orig_size = size;
- return nbp;
+ Binary *nbp = erts_bin_realloc_fnf(bp, size);
+
+ if (nbp) {
+ return nbp;
+ }
+
+ if (bp->intern.flags & BIN_FLAG_DRV) {
+ erts_realloc_enomem(ERTS_ALC_T_DRV_BINARY, bp, size);
+ } else {
+ erts_realloc_enomem(ERTS_ALC_T_BINARY, bp, size);
+ }
}
ERTS_GLB_INLINE void
diff --git a/erts/emulator/beam/erl_bits.c b/erts/emulator/beam/erl_bits.c
index f5807d25d7..a0edbc81a4 100644
--- a/erts/emulator/beam/erl_bits.c
+++ b/erts/emulator/beam/erl_bits.c
@@ -124,10 +124,10 @@ erts_bs_start_match_2(Process *p, Eterm Binary, Uint Max)
ProcBin* pb;
ASSERT(is_binary(Binary));
+
total_bin_size = binary_size(Binary);
- if ((total_bin_size >> (8*sizeof(Uint)-3)) != 0) {
- return THE_NON_VALUE;
- }
+ ASSERT(total_bin_size <= ERTS_UWORD_MAX / CHAR_BIT);
+
NeededSize = ERL_BIN_MATCHSTATE_SIZE(Max);
hp = HeapOnlyAlloc(p, NeededSize);
ms = (ErlBinMatchState *) hp;
@@ -157,10 +157,9 @@ ErlBinMatchState *erts_bs_start_match_3(Process *p, Eterm Binary)
ProcBin* pb;
ASSERT(is_binary(Binary));
+
total_bin_size = binary_size(Binary);
- if ((total_bin_size >> (8*sizeof(Uint)-3)) != 0) {
- return NULL;
- }
+ ASSERT(total_bin_size <= ERTS_UWORD_MAX / CHAR_BIT);
NeededSize = ERL_BIN_MATCHSTATE_SIZE(0);
hp = HeapOnlyAlloc(p, NeededSize);
@@ -459,29 +458,25 @@ erts_bs_get_integer_2(Process *p, Uint num_bits, unsigned flags, ErlBinMatchBuff
Eterm
erts_bs_get_binary_2(Process *p, Uint num_bits, unsigned flags, ErlBinMatchBuffer* mb)
{
- ErlSubBin* sb;
+ Eterm result;
CHECK_MATCH_BUFFER(mb);
- if (mb->size - mb->offset < num_bits) { /* Asked for too many bits. */
- return THE_NON_VALUE;
+ if (mb->size - mb->offset < num_bits) {
+ /* Asked for too many bits. */
+ return THE_NON_VALUE;
}
/*
* From now on, we can't fail.
*/
- sb = (ErlSubBin *) HeapOnlyAlloc(p, ERL_SUB_BIN_SIZE);
-
- sb->thing_word = HEADER_SUB_BIN;
- sb->orig = mb->orig;
- sb->size = BYTE_OFFSET(num_bits);
- sb->bitsize = BIT_OFFSET(num_bits);
- sb->offs = BYTE_OFFSET(mb->offset);
- sb->bitoffs = BIT_OFFSET(mb->offset);
- sb->is_writable = 0;
+ result = erts_extract_sub_binary(&HEAP_TOP(p),
+ mb->orig, mb->base,
+ mb->offset, num_bits);
+
mb->offset += num_bits;
-
- return make_binary(sb);
+
+ return result;
}
Eterm
@@ -545,21 +540,19 @@ erts_bs_get_float_2(Process *p, Uint num_bits, unsigned flags, ErlBinMatchBuffer
Eterm
erts_bs_get_binary_all_2(Process *p, ErlBinMatchBuffer* mb)
{
- ErlSubBin* sb;
- Uint size;
+ Uint bit_size;
+ Eterm result;
CHECK_MATCH_BUFFER(mb);
- size = mb->size-mb->offset;
- sb = (ErlSubBin *) HeapOnlyAlloc(p, ERL_SUB_BIN_SIZE);
- sb->thing_word = HEADER_SUB_BIN;
- sb->size = BYTE_OFFSET(size);
- sb->bitsize = BIT_OFFSET(size);
- sb->offs = BYTE_OFFSET(mb->offset);
- sb->bitoffs = BIT_OFFSET(mb->offset);
- sb->is_writable = 0;
- sb->orig = mb->orig;
- mb->offset = mb->size;
- return make_binary(sb);
+ bit_size = mb->size - mb->offset;
+
+ result = erts_extract_sub_binary(&HEAP_TOP(p),
+ mb->orig, mb->base,
+ mb->offset, bit_size);
+
+ mb->offset = mb->size;
+
+ return result;
}
/****************************************************************
@@ -2097,3 +2090,39 @@ erts_copy_bits(byte* src, /* Base pointer to source. */
}
}
+Eterm erts_extract_sub_binary(Eterm **hp, Eterm base_bin, byte *base_data,
+ Uint bit_offset, Uint bit_size)
+{
+ Uint byte_offset, byte_size;
+
+ ERTS_CT_ASSERT(ERL_SUB_BIN_SIZE <= ERL_ONHEAP_BIN_LIMIT);
+
+ byte_offset = BYTE_OFFSET(bit_offset);
+ byte_size = BYTE_OFFSET(bit_size);
+
+ if (BIT_OFFSET(bit_size) == 0 && byte_size <= ERL_ONHEAP_BIN_LIMIT) {
+ ErlHeapBin *hb = (ErlHeapBin*)*hp;
+ *hp += heap_bin_size(byte_size);
+
+ hb->thing_word = header_heap_bin(byte_size);
+ hb->size = byte_size;
+
+ copy_binary_to_buffer(hb->data, 0, base_data, bit_offset, bit_size);
+
+ return make_binary(hb);
+ } else {
+ ErlSubBin *sb = (ErlSubBin*)*hp;
+ *hp += ERL_SUB_BIN_SIZE;
+
+ sb->thing_word = HEADER_SUB_BIN;
+ sb->size = byte_size;
+ sb->offs = byte_offset;
+ sb->orig = base_bin;
+ sb->bitoffs = BIT_OFFSET(bit_offset);
+ sb->bitsize = BIT_OFFSET(bit_size);
+ sb->is_writable = 0;
+
+ return make_binary(sb);
+ }
+}
+
diff --git a/erts/emulator/beam/erl_bits.h b/erts/emulator/beam/erl_bits.h
index 50d353e1fa..0b2a6f3760 100644
--- a/erts/emulator/beam/erl_bits.h
+++ b/erts/emulator/beam/erl_bits.h
@@ -116,7 +116,7 @@ typedef struct erl_bin_match_struct{
do { \
if (BIT_OFFSET(DstBufOffset) == 0 && (SrcBufferOffset == 0) && \
(BIT_OFFSET(NumBits)==0) && (NumBits != 0)) { \
- sys_memcpy(DstBuffer+BYTE_OFFSET(DstBufOffset), \
+ sys_memcpy(((byte*)DstBuffer)+BYTE_OFFSET(DstBufOffset), \
SrcBuffer, NBYTES(NumBits)); \
} else { \
erts_copy_bits(SrcBuffer, SrcBufferOffset, 1, \
@@ -150,8 +150,11 @@ void erts_bits_destroy_state(ERL_BITS_PROTO_0);
Eterm erts_bs_start_match_2(Process *p, Eterm Bin, Uint Max);
ErlBinMatchState *erts_bs_start_match_3(Process *p, Eterm Bin);
Eterm erts_bs_get_integer_2(Process *p, Uint num_bits, unsigned flags, ErlBinMatchBuffer* mb);
-Eterm erts_bs_get_binary_2(Process *p, Uint num_bits, unsigned flags, ErlBinMatchBuffer* mb);
Eterm erts_bs_get_float_2(Process *p, Uint num_bits, unsigned flags, ErlBinMatchBuffer* mb);
+
+/* These will create heap binaries when appropriate, so they require free space
+ * up to EXTRACT_SUB_BIN_HEAP_NEED. */
+Eterm erts_bs_get_binary_2(Process *p, Uint num_bits, unsigned flags, ErlBinMatchBuffer* mb);
Eterm erts_bs_get_binary_all_2(Process *p, ErlBinMatchBuffer* mb);
/*
@@ -182,6 +185,17 @@ void erts_copy_bits(byte* src, size_t soffs, int sdir,
byte* dst, size_t doffs,int ddir, size_t n);
int erts_cmp_bits(byte* a_ptr, size_t a_offs, byte* b_ptr, size_t b_offs, size_t size);
+
+/* Extracts a region from base_bin as a sub-binary or heap binary, whichever
+ * is the most appropriate.
+ *
+ * The caller must ensure that there's enough free space at *hp */
+Eterm erts_extract_sub_binary(Eterm **hp, Eterm base_bin, byte *base_data,
+ Uint bit_offset, Uint num_bits);
+
+/* Pessimistic estimate of the words required for erts_extract_sub_binary */
+#define EXTRACT_SUB_BIN_HEAP_NEED (heap_bin_size(ERL_ONHEAP_BIN_LIMIT))
+
/*
* Flags for bs_get_* / bs_put_* / bs_init* instructions.
*/
diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c
index d24f30f126..285c23ea01 100644
--- a/erts/emulator/beam/erl_db.c
+++ b/erts/emulator/beam/erl_db.c
@@ -399,7 +399,9 @@ Export ets_select_continue_exp;
* Static traps
*/
static Export ets_delete_continue_exp;
-
+
+static Export *ets_info_binary_trap = NULL;
+
static void
free_dbtable(void *vtb)
{
@@ -675,7 +677,8 @@ static DbTable* handle_lacking_permission(Process* p, DbTable* tb,
tb = NULL;
*freason_p = TRAP;
}
- else if (p->common.id != tb->common.owner) {
+ else if (p->common.id != tb->common.owner
+ && !(p->flags & F_ETS_SUPER_USER)) {
db_unlock(tb, kind);
tb = NULL;
*freason_p = BADARG;
@@ -3411,6 +3414,10 @@ BIF_RETTYPE ets_info_2(BIF_ALIST_2)
}
BIF_RET(erts_make_integer(res, BIF_P));
}
+
+ if (BIF_ARG_2 == am_binary)
+ BIF_TRAP1(ets_info_binary_trap, BIF_P, BIF_ARG_1);
+
if ((tb = db_get_table(BIF_P, BIF_ARG_1, DB_INFO, LCK_READ, &freason)) == NULL) {
if (freason == BADARG && (is_atom(BIF_ARG_1) || is_ref(BIF_ARG_1)))
BIF_RET(am_undefined);
@@ -3509,6 +3516,81 @@ BIF_RETTYPE ets_match_spec_run_r_3(BIF_ALIST_3)
BIF_RET2(ret,i);
}
+BIF_RETTYPE erts_internal_ets_lookup_binary_info_2(BIF_ALIST_2)
+{
+ DbTable* tb;
+ int cret;
+ Eterm ret;
+
+ CHECK_TABLES();
+
+ DB_BIF_GET_TABLE(tb, DB_READ, LCK_READ, BIF_erts_internal_ets_lookup_binary_info_2);
+
+ cret = tb->common.meth->db_get_binary_info(BIF_P, tb, BIF_ARG_2, &ret);
+
+ db_unlock(tb, LCK_READ);
+
+ switch (cret) {
+ case DB_ERROR_NONE:
+ BIF_RET(ret);
+ case DB_ERROR_SYSRES:
+ BIF_ERROR(BIF_P, SYSTEM_LIMIT);
+ default:
+ BIF_ERROR(BIF_P, BADARG);
+ }
+}
+
+BIF_RETTYPE erts_internal_ets_raw_first_1(BIF_ALIST_1)
+{
+ DbTable* tb;
+ int cret;
+ Eterm ret;
+
+ CHECK_TABLES();
+
+ DB_BIF_GET_TABLE(tb, DB_READ, LCK_READ, BIF_erts_internal_ets_raw_first_1);
+
+ cret = tb->common.meth->db_raw_first(BIF_P, tb, &ret);
+
+ db_unlock(tb, LCK_READ);
+
+ if (cret != DB_ERROR_NONE) {
+ BIF_ERROR(BIF_P, BADARG);
+ }
+ BIF_RET(ret);
+}
+
+BIF_RETTYPE erts_internal_ets_raw_next_2(BIF_ALIST_2)
+{
+ DbTable* tb;
+ int cret;
+ Eterm ret;
+
+ CHECK_TABLES();
+
+ DB_BIF_GET_TABLE(tb, DB_READ, LCK_READ, BIF_erts_internal_ets_raw_next_2);
+
+ cret = tb->common.meth->db_raw_next(BIF_P, tb, BIF_ARG_2, &ret);
+
+ db_unlock(tb, LCK_READ);
+
+ if (cret != DB_ERROR_NONE) {
+ BIF_ERROR(BIF_P, BADARG);
+ }
+ BIF_RET(ret);
+}
+
+BIF_RETTYPE
+erts_internal_ets_super_user_1(BIF_ALIST_1)
+{
+ if (BIF_ARG_1 == am_true)
+ BIF_P->flags |= F_ETS_SUPER_USER;
+ else if (BIF_ARG_1 == am_false)
+ BIF_P->flags &= ~F_ETS_SUPER_USER;
+ else
+ BIF_ERROR(BIF_P, BADARG);
+ BIF_RET(am_ok);
+}
/*
** External interface (NOT BIF's)
@@ -3628,6 +3710,12 @@ void init_db(ErtsDbSpinCount db_spin_count)
erts_init_trap_export(&ets_delete_continue_exp,
am_ets, ERTS_MAKE_AM("delete_trap"), 1,
&ets_delete_trap);
+
+ /* ets:info(Tab, binary) trap... */
+
+ ets_info_binary_trap = erts_export_put(am_erts_internal,
+ am_ets_info_binary,
+ 1);
}
void
@@ -4415,7 +4503,7 @@ void db_info(fmtfn_t to, void *to_arg, int show) /* Called by break handler *
pdbi.to_arg = to_arg;
pdbi.show = show;
- erts_db_foreach_table(db_info_print, &pdbi);
+ erts_db_foreach_table(db_info_print, &pdbi, !0);
}
Uint
@@ -4428,7 +4516,7 @@ erts_get_ets_misc_mem_size(void)
/* SMP Note: May only be used when system is locked */
void
-erts_db_foreach_table(void (*func)(DbTable *, void *), void *arg)
+erts_db_foreach_table(void (*func)(DbTable *, void *), void *arg, int alive_only)
{
int ix;
@@ -4440,7 +4528,7 @@ erts_db_foreach_table(void (*func)(DbTable *, void *), void *arg)
if (first) {
DbTable *tb = first;
do {
- if (is_table_alive(tb))
+ if (!alive_only || is_table_alive(tb))
(*func)(tb, arg);
tb = tb->common.all.next;
} while (tb != first);
@@ -4457,6 +4545,15 @@ erts_db_foreach_offheap(DbTable *tb,
tb->common.meth->db_foreach_offheap(tb, func, arg);
}
+void
+erts_db_foreach_thr_prgr_offheap(void (*func)(ErlOffHeap *, void *),
+ void *arg)
+{
+ erts_db_foreach_thr_prgr_offheap_hash(func, arg);
+ erts_db_foreach_thr_prgr_offheap_tree(func, arg);
+ erts_db_foreach_thr_prgr_offheap_catree(func, arg);
+}
+
/* retrieve max number of ets tables */
Uint
erts_db_get_max_tabs()
diff --git a/erts/emulator/beam/erl_db.h b/erts/emulator/beam/erl_db.h
index b3dc1b9ba3..c604744687 100644
--- a/erts/emulator/beam/erl_db.h
+++ b/erts/emulator/beam/erl_db.h
@@ -114,10 +114,12 @@ void init_db(ErtsDbSpinCount);
int erts_db_process_exiting(Process *, ErtsProcLocks, void **);
int erts_db_execute_free_fixation(Process*, DbFixation*);
void db_info(fmtfn_t, void *, int);
-void erts_db_foreach_table(void (*)(DbTable *, void *), void *);
+void erts_db_foreach_table(void (*)(DbTable *, void *), void *, int);
void erts_db_foreach_offheap(DbTable *,
void (*func)(ErlOffHeap *, void *),
void *);
+void erts_db_foreach_thr_prgr_offheap(void (*func)(ErlOffHeap *, void *),
+ void *);
extern int erts_ets_rwmtx_spin_count;
extern int user_requested_db_max_tabs; /* set in erl_init */
diff --git a/erts/emulator/beam/erl_db_catree.c b/erts/emulator/beam/erl_db_catree.c
index fed4b44a9b..176966edb5 100644
--- a/erts/emulator/beam/erl_db_catree.c
+++ b/erts/emulator/beam/erl_db_catree.c
@@ -90,9 +90,9 @@
** Forward declarations
*/
-static SWord do_free_base_node_cont(DbTableCATree *tb, SWord num_left);
-static SWord do_free_routing_nodes_catree_cont(DbTableCATree *tb, SWord num_left);
-static DbTableCATreeNode *catree_first_base_node_from_free_list(DbTableCATree *tb);
+static SWord do_delete_base_node_cont(DbTableCATree *tb,
+ DbTableCATreeNode *base_node,
+ SWord num_left);
/* Method interface functions */
static int db_first_catree(Process *p, DbTable *tbl,
@@ -159,6 +159,7 @@ static int
db_lookup_dbterm_catree(Process *, DbTable *, Eterm key, Eterm obj,
DbUpdateHandle*);
static void db_finalize_dbterm_catree(int cret, DbUpdateHandle *);
+static int db_get_binary_info_catree(Process*, DbTable*, Eterm key, Eterm *ret);
static void split_catree(DbTableCATree *tb,
DbTableCATreeNode* ERTS_RESTRICT base,
@@ -211,8 +212,10 @@ DbTableMethod db_catree =
db_print_catree,
db_foreach_offheap_catree,
db_lookup_dbterm_catree,
- db_finalize_dbterm_catree
-
+ db_finalize_dbterm_catree,
+ db_get_binary_info_catree,
+ db_first_catree, /* raw_first same as first */
+ db_next_catree /* raw_next same as next */
};
/*
@@ -1364,113 +1367,152 @@ static void split_catree(DbTableCATree *tb,
}
}
-/*
- * Helper functions for removing the table
+/* @brief Free the entire catree and its sub-trees.
+ *
+ * @param reds Reductions to spend.
+ * @return Reductions left. Negative value if not done.
*/
-
-static void catree_add_base_node_to_free_list(
- DbTableCATree *tb,
- DbTableCATreeNode *base_node_container)
+static SWord db_free_table_continue_catree(DbTable *tbl, SWord reds)
{
- base_node_container->u.base.next =
- tb->base_nodes_to_free_list;
- tb->base_nodes_to_free_list = base_node_container;
-}
+ DbTableCATree *tb = &tbl->catree;
+ DbTableCATreeNode *node;
+ DbTableCATreeNode *parent;
+ CATreeNodeStack rnode_stack;
+ DbTableCATreeNode *rnode_stack_array[STACK_NEED];
-static void catree_deque_base_node_from_free_list(DbTableCATree *tb)
-{
- if (tb->base_nodes_to_free_list == NULL) {
- return; /* List empty */
- } else {
- DbTableCATreeNode *first = tb->base_nodes_to_free_list;
- tb->base_nodes_to_free_list = first->u.base.next;
+ if (!tb->deletion) {
+ /* First call */
+ tb->deletion = 1;
+ tb->nr_of_deleted_items = 0;
}
-}
-static DbTableCATreeNode *catree_first_base_node_from_free_list(
- DbTableCATree *tb)
-{
- return tb->base_nodes_to_free_list;
-}
+ /*
+ * The route tree is traversed and freed while keeping it consistent
+ * during yields.
+ */
+ rnode_stack.array = rnode_stack_array;
+ rnode_stack.pos = 0;
+ rnode_stack.size = STACK_NEED;
-static SWord do_free_routing_nodes_catree_cont(DbTableCATree *tb, SWord num_left)
-{
- DbTableCATreeNode *root;
- DbTableCATreeNode *p;
- for (;;) {
- root = POP_NODE(&tb->free_stack_rnodes);
- if (root == NULL) break;
- else if(root->is_base_node) {
- catree_add_base_node_to_free_list(tb, root);
- break;
+ node = GET_ROOT(tb);
+ if (node->is_base_node) {
+ if (node->u.base.root) {
+ reds = do_delete_base_node_cont(tb, node, reds);
+ if (reds < 0)
+ return reds; /* Yield */
}
- for (;;) {
- if ((GET_LEFT(root) != NULL) &&
- (p = GET_LEFT(root))->is_base_node) {
- SET_LEFT(root, NULL);
- catree_add_base_node_to_free_list(tb, p);
- } else if ((GET_RIGHT(root) != NULL) &&
- (p = GET_RIGHT(root))->is_base_node) {
- SET_RIGHT(root, NULL);
- catree_add_base_node_to_free_list(tb, p);
- } else if ((p = GET_LEFT(root)) != NULL) {
- SET_LEFT(root, NULL);
- PUSH_NODE(&tb->free_stack_rnodes, root);
- root = p;
- } else if ((p = GET_RIGHT(root)) != NULL) {
- SET_RIGHT(root, NULL);
- PUSH_NODE(&tb->free_stack_rnodes, root);
- root = p;
- } else {
- free_catree_route_node(tb, root);
- if (--num_left >= 0) {
+ free_catree_base_node(tb, node);
+ }
+ else {
+ for (;;) {
+ DbTableCATreeNode* left = GET_LEFT(node);
+ DbTableCATreeNode* right = GET_RIGHT(node);
+
+ if (!left->is_base_node) {
+ PUSH_NODE(&rnode_stack, node);
+ node = left;
+ }
+ else if (!right->is_base_node) {
+ PUSH_NODE(&rnode_stack, node);
+ node = right;
+ }
+ else {
+ if (left->u.base.root) {
+ reds = do_delete_base_node_cont(tb, left, reds);
+ if (reds < 0)
+ return reds; /* Yield */
+ }
+ if (right->u.base.root) {
+ reds = do_delete_base_node_cont(tb, right, reds);
+ if (reds < 0)
+ return reds; /* Yield */
+ }
+
+ free_catree_base_node(tb, right);
+ free_catree_route_node(tb, node);
+ /*
+ * Keep empty left base node to join with its grandparent
+ * for tree consistency during yields.
+ */
+
+ parent = POP_NODE(&rnode_stack);
+ if (parent) {
+ if (node == GET_LEFT(parent)) {
+ SET_LEFT(parent, left);
+ }
+ else {
+ ASSERT(node == GET_RIGHT(parent));
+ SET_RIGHT(parent, left);
+ }
+
+ reds -= 2;
+ if (reds < 0)
+ return reds; /* Yield */
+
+ node = parent;
+ }
+ else { /* Done */
+ free_catree_base_node(tb, left);
break;
- } else {
- return num_left; /* Done enough for now */
}
}
}
}
- return num_left;
+
+ ASSERT(reds >= 0);
+ SET_ROOT(tb, NULL);
+ return reds;
}
-static SWord do_free_base_node_cont(DbTableCATree *tb, SWord num_left)
+/* @brief Free all objects of a base node, but keep the base node.
+ *
+ * @param reds Reductions to spend.
+ * @return Reductions left. Negative value if not done.
+ */
+static SWord do_delete_base_node_cont(DbTableCATree *tb,
+ DbTableCATreeNode *base_node,
+ SWord reds)
{
- TreeDbTerm *root;
TreeDbTerm *p;
- DbTableCATreeNode *base_node_container =
- catree_first_base_node_from_free_list(tb);
+ DbTreeStack stack;
+ TreeDbTerm* stack_array[STACK_NEED];
+
+ stack.pos = 0;
+ stack.array = stack_array;
+
+ p = base_node->u.base.root;
for (;;) {
- root = POP_NODE(&tb->free_stack_elems);
- if (root == NULL) break;
- for (;;) {
- if ((p = root->left) != NULL) {
- root->left = NULL;
- PUSH_NODE(&tb->free_stack_elems, root);
- root = p;
- } else if ((p = root->right) != NULL) {
- root->right = NULL;
- PUSH_NODE(&tb->free_stack_elems, root);
- root = p;
- } else {
- DEC_NITEMS((DbTable*)tb);
- tb->nr_of_deleted_items++;
- free_term((DbTable*)tb, root);
- if (--num_left >= 0) {
- break;
- } else {
- return num_left; /* Done enough for now */
- }
+ if (p->left) {
+ PUSH_NODE(&stack, p);
+ p = p->left;
+ }
+ else if (p->right) {
+ PUSH_NODE(&stack, p);
+ p = p->right;
+ }
+ else {
+ TreeDbTerm *parent;
+
+ DEC_NITEMS((DbTable*)tb);
+ tb->nr_of_deleted_items++;
+ free_term((DbTable*)tb, p);
+
+ parent = POP_NODE(&stack);
+ if (!parent)
+ break;
+ if (parent->left == p)
+ parent->left = NULL;
+ else {
+ ASSERT(parent->right == p);
+ parent->right = NULL;
}
+ if (--reds < 0)
+ return reds; /* Yield */
+ p = parent;
}
}
- catree_deque_base_node_from_free_list(tb);
- free_catree_base_node(tb, base_node_container);
- base_node_container = catree_first_base_node_from_free_list(tb);
- if (base_node_container != NULL) {
- PUSH_NODE(&tb->free_stack_elems, base_node_container->u.base.root);
- }
- return num_left;
+ base_node->u.base.root = NULL;
+ return reds;
}
@@ -1494,7 +1536,6 @@ int db_create_catree(Process *p, DbTable *tbl)
root = create_base_node(tb, NULL);
tb->deletion = 0;
- tb->base_nodes_to_free_list = NULL;
tb->nr_of_deleted_items = 0;
#ifdef DEBUG
tbl->common.status |= DB_CATREE_DEBUG_RANDOM_SPLIT_JOIN;
@@ -2131,57 +2172,6 @@ static int db_free_table_catree(DbTable *tbl)
return 1;
}
-static SWord db_free_table_continue_catree(DbTable *tbl, SWord reds)
-{
- DbTableCATreeNode *first_base_node;
- DbTableCATree *tb = &tbl->catree;
- if (!tb->deletion) {
- tb->deletion = 1;
- tb->free_stack_elems.array =
- erts_db_alloc(ERTS_ALC_T_DB_STK,
- (DbTable *) tb,
- sizeof(TreeDbTerm *) * STACK_NEED);
- tb->free_stack_elems.pos = 0;
- tb->free_stack_elems.slot = 0;
- tb->free_stack_rnodes.array =
- erts_db_alloc(ERTS_ALC_T_DB_STK,
- (DbTable *) tb,
- sizeof(DbTableCATreeNode *) * STACK_NEED);
- tb->free_stack_rnodes.pos = 0;
- tb->free_stack_rnodes.size = STACK_NEED;
- PUSH_NODE(&tb->free_stack_rnodes, GET_ROOT(tb));
- tb->is_routing_nodes_freed = 0;
- tb->base_nodes_to_free_list = NULL;
- tb->nr_of_deleted_items = 0;
- }
- if ( ! tb->is_routing_nodes_freed ) {
- reds = do_free_routing_nodes_catree_cont(tb, reds);
- if (reds < 0) {
- return reds; /* Not finished */
- } else {
- tb->is_routing_nodes_freed = 1; /* Ready with the routing nodes */
- first_base_node = catree_first_base_node_from_free_list(tb);
- PUSH_NODE(&tb->free_stack_elems, first_base_node->u.base.root);
- }
- }
- while (catree_first_base_node_from_free_list(tb) != NULL) {
- reds = do_free_base_node_cont(tb, reds);
- if (reds < 0) {
- return reds; /* Continue later */
- }
- }
- /* Time to free the main structure*/
- erts_db_free(ERTS_ALC_T_DB_STK,
- (DbTable *) tb,
- (void *) tb->free_stack_elems.array,
- sizeof(TreeDbTerm *) * STACK_NEED);
- erts_db_free(ERTS_ALC_T_DB_STK,
- (DbTable *) tb,
- (void *) tb->free_stack_rnodes.array,
- sizeof(DbTableCATreeNode *) * STACK_NEED);
- return 1;
-}
-
static
int db_catree_nr_of_items_deleted_wb_dtor(Binary *context_bin) {
(void)context_bin;
@@ -2258,10 +2248,15 @@ static void db_foreach_offheap_catree(DbTable *tbl,
void (*func)(ErlOffHeap *, void *),
void *arg)
{
+ DbTableCATree* tb = &tbl->catree;
CATreeRootIterator iter;
TreeDbTerm** root;
- init_root_iterator(&tbl->catree, &iter, 1);
+ if (!GET_ROOT(tb)) {
+ ASSERT(tb->common.status & DB_DELETE);
+ return;
+ }
+ init_root_iterator(tb, &iter, 1);
root = catree_find_first_root(&iter);
do {
db_foreach_offheap_tree_common(*root, func, arg);
@@ -2269,7 +2264,7 @@ static void db_foreach_offheap_catree(DbTable *tbl,
} while (root);
destroy_root_iterator(&iter);
- do_for_route_nodes(GET_ROOT(&tbl->catree), func, arg);
+ do_for_route_nodes(GET_ROOT(tb), func, arg);
}
static int db_lookup_dbterm_catree(Process *p, DbTable *tbl, Eterm key, Eterm obj,
@@ -2301,6 +2296,19 @@ static void db_finalize_dbterm_catree(int cret, DbUpdateHandle *handle)
return;
}
+static int db_get_binary_info_catree(Process *p, DbTable *tbl, Eterm key,
+ Eterm *ret)
+{
+ DbTableCATree *tb = &tbl->catree;
+ FindBaseNode fbn;
+ DbTableCATreeNode* node = find_wlock_valid_base_node(tb, key, &fbn);
+ TreeDbTerm* this = db_find_tree_node_common(&tbl->common, node->u.base.root,
+ key);
+ *ret = db_binary_info_tree_common(p, this);
+ wunlock_base_node(node);
+ return DB_ERROR_NONE;
+}
+
#ifdef ERTS_ENABLE_LOCK_COUNT
static void erts_lcnt_enable_db_catree_lock_count_helper(DbTableCATree *tb,
DbTableCATreeNode *node,
@@ -2395,6 +2403,34 @@ void db_calc_stats_catree(DbTableCATree* tb, DbCATreeStats* stats)
} while (depth > 0);
}
+struct debug_catree_fa {
+ void (*func)(ErlOffHeap *, void *);
+ void *arg;
+};
+
+static void debug_free_route_node(void *vfap, ErtsThrPrgrVal val, void *vnp)
+{
+ DbTableCATreeNode *np = vnp;
+ if (np->u.route.key.oh) {
+ struct debug_catree_fa *fap = vfap;
+ ErlOffHeap oh;
+ ERTS_INIT_OFF_HEAP(&oh);
+ oh.first = np->u.route.key.oh;
+ (*fap->func)(&oh, fap->arg);
+ }
+}
+
+void
+erts_db_foreach_thr_prgr_offheap_catree(void (*func)(ErlOffHeap *, void *),
+ void *arg)
+{
+ struct debug_catree_fa fa;
+ fa.func = func;
+ fa.arg = arg;
+ erts_debug_later_op_foreach(do_free_route_node, debug_free_route_node, &fa);
+}
+
+
#ifdef HARDDEBUG
/*
diff --git a/erts/emulator/beam/erl_db_catree.h b/erts/emulator/beam/erl_db_catree.h
index c2c884eee3..00141ef86d 100644
--- a/erts/emulator/beam/erl_db_catree.h
+++ b/erts/emulator/beam/erl_db_catree.h
@@ -46,7 +46,6 @@ typedef struct {
int is_valid; /* If this base node is still valid */
TreeDbTerm *root; /* The root of the sequential tree */
ErtsThrPrgrLaterOp free_item; /* Used when freeing using thread progress */
- struct DbTableCATreeNode * next; /* Used when gradually deleting */
char end_of_struct__;
} DbTableCATreeBaseNode;
@@ -83,9 +82,6 @@ typedef struct db_table_catree {
/* CA Tree-specific fields */
erts_atomic_t root; /* The tree root (DbTableCATreeNode*) */
Uint deletion; /* Being deleted */
- DbTreeStack free_stack_elems;/* Used for deletion ...*/
- CATreeNodeStack free_stack_rnodes;
- DbTableCATreeNode *base_nodes_to_free_list;
int is_routing_nodes_freed;
/* The fields below are used by delete_all_objects and
select_delete(DeleteAll)*/
@@ -132,6 +128,9 @@ typedef struct {
Uint max_depth;
} DbCATreeStats;
void db_calc_stats_catree(DbTableCATree*, DbCATreeStats*);
+void
+erts_db_foreach_thr_prgr_offheap_catree(void (*func)(ErlOffHeap *, void *),
+ void *arg);
#endif /* _DB_CATREE_H */
diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c
index d80d7985cb..2bcdb47a54 100644
--- a/erts/emulator/beam/erl_db_hash.c
+++ b/erts/emulator/beam/erl_db_hash.c
@@ -472,6 +472,11 @@ db_lookup_dbterm_hash(Process *p, DbTable *tbl, Eterm key, Eterm obj,
static void
db_finalize_dbterm_hash(int cret, DbUpdateHandle* handle);
+static int
+db_get_binary_info_hash(Process *p, DbTable *tbl, Eterm key, Eterm *ret);
+static int db_raw_first_hash(Process* p, DbTable *tbl, Eterm *ret);
+static int db_raw_next_hash(Process* p, DbTable *tbl, Eterm key, Eterm *ret);
+
static ERTS_INLINE void try_shrink(DbTableHash* tb)
{
int nitems = NITEMS(tb);
@@ -570,7 +575,10 @@ DbTableMethod db_hash =
db_print_hash,
db_foreach_offheap_hash,
db_lookup_dbterm_hash,
- db_finalize_dbterm_hash
+ db_finalize_dbterm_hash,
+ db_get_binary_info_hash,
+ db_raw_first_hash,
+ db_raw_next_hash
};
#ifdef DEBUG
@@ -3320,6 +3328,12 @@ void db_foreach_offheap_hash(DbTable *tbl,
int i;
int nactive = NACTIVE(tb);
+ if (nactive > tb->nslots) {
+ /* Table is being emptied by delete/1 or delete_all_objects/1 */
+ ASSERT(!(tb->common.status & (DB_PRIVATE|DB_PROTECTED|DB_PUBLIC)));
+ nactive = tb->nslots;
+ }
+
for (i = 0; i < nactive; i++) {
list = BUCKET(tb,i);
while(list != 0) {
@@ -3368,12 +3382,142 @@ void db_calc_stats_hash(DbTableHash* tb, DbHashStats* stats)
stats->kept_items = kept_items;
}
+
+/*
+ * erts_internal:ets_lookup_binary_info/2
+ */
+static int db_get_binary_info_hash(Process *p, DbTable *tbl, Eterm key, Eterm *ret)
+{
+ DbTableHash *tb = &tbl->hash;
+ HashValue hval;
+ int ix;
+ HashDbTerm *b, *first, *end;
+ erts_rwmtx_t* lck;
+ Eterm *hp, *hp_end;
+ Uint hsz;
+ Eterm list;
+
+ hval = MAKE_HASH(key);
+ lck = RLOCK_HASH(tb,hval);
+ ix = hash_to_ix(tb, hval);
+ b = BUCKET(tb, ix);
+
+ while(b != 0) {
+ if (has_key(tb, b, key, hval)) {
+ goto found_key;
+ }
+ b = b->next;
+ }
+ RUNLOCK_HASH(lck);
+ *ret = NIL;
+ return DB_ERROR_NONE;
+
+found_key:
+
+ first = b;
+ hsz = 0;
+ do {
+ ErlOffHeap oh;
+ oh.first = b->dbterm.first_oh;
+ erts_bld_bin_list(NULL, &hsz, &oh, NIL);
+ b = b->next;
+ } while (b && has_key(tb, b, key, hval));
+ end = b;
+
+ hp = HAlloc(p, hsz);
+ hp_end = hp + hsz;
+ list = NIL;
+ for (b = first; b != end; b = b->next) {
+ ErlOffHeap oh;
+ oh.first = b->dbterm.first_oh;
+ list = erts_bld_bin_list(&hp, NULL, &oh, list);
+ }
+ ASSERT(hp == hp_end); (void)hp_end;
+
+ RUNLOCK_HASH(lck);
+ *ret = list;
+ return DB_ERROR_NONE;
+}
+
+static int raw_find_next(Process *p, DbTable* tbl, Uint ix,
+ erts_rwmtx_t* lck, Eterm *ret)
+{
+ DbTableHash *tb = &tbl->hash;
+ HashDbTerm *b;
+
+ do {
+ b = BUCKET(tb,ix);
+ if (b) {
+ *ret = db_copy_key(p, tbl, &b->dbterm);
+ RUNLOCK_HASH(lck);
+ return DB_ERROR_NONE;
+ }
+ ix = next_slot(tb, ix, &lck);
+ } while (ix);
+
+ *ret = am_EOT;
+ return DB_ERROR_NONE;
+}
+
+static int db_raw_first_hash(Process *p, DbTable *tbl, Eterm *ret)
+{
+ const Uint ix = 0;
+ return raw_find_next(p, tbl, ix, RLOCK_HASH(&tbl->hash, ix), ret);
+}
+
+static int db_raw_next_hash(Process *p, DbTable *tbl, Eterm key, Eterm *ret)
+{
+ DbTableHash *tb = &tbl->hash;
+ HashValue hval;
+ Uint ix;
+ HashDbTerm* b;
+ erts_rwmtx_t* lck;
+
+ hval = MAKE_HASH(key);
+ lck = RLOCK_HASH(tb,hval);
+ ix = hash_to_ix(tb, hval);
+ b = BUCKET(tb, ix);
+
+ for (;;) {
+ if (b == NULL) {
+ RUNLOCK_HASH(lck);
+ return DB_ERROR_BADKEY;
+ }
+ if (has_key(tb, b, key, hval)) {
+ break;
+ }
+ b = b->next;
+ }
+ /* Key found */
+
+ for (b = b->next; b; b = b->next) {
+ if (!has_key(tb, b, key, hval)) {
+ *ret = db_copy_key(p, tbl, &b->dbterm);
+ RUNLOCK_HASH(lck);
+ return DB_ERROR_NONE;
+ }
+ }
+
+ ix = next_slot(tb, ix, &lck);
+ if (ix)
+ return raw_find_next(p, tbl, ix, lck, ret);
+
+ *ret = am_EOT;
+ return DB_ERROR_NONE;
+}
+
/* For testing only */
Eterm erts_ets_hash_sizeof_ext_segtab(void)
{
return make_small(((SIZEOF_EXT_SEGTAB(0)-1) / sizeof(UWord)) + 1);
}
+void
+erts_db_foreach_thr_prgr_offheap_hash(void (*func)(ErlOffHeap *, void *),
+ void *arg)
+{
+}
+
#ifdef ERTS_ENABLE_LOCK_COUNT
void erts_lcnt_enable_db_hash_lock_count(DbTableHash *tb, int enable) {
int i;
diff --git a/erts/emulator/beam/erl_db_hash.h b/erts/emulator/beam/erl_db_hash.h
index ecd2ca74a1..b26b82056f 100644
--- a/erts/emulator/beam/erl_db_hash.h
+++ b/erts/emulator/beam/erl_db_hash.h
@@ -111,6 +111,9 @@ typedef struct {
void db_calc_stats_hash(DbTableHash* tb, DbHashStats*);
Eterm erts_ets_hash_sizeof_ext_segtab(void);
+void
+erts_db_foreach_thr_prgr_offheap_hash(void (*func)(ErlOffHeap *, void *),
+ void *arg);
#ifdef ERTS_ENABLE_LOCK_COUNT
void erts_lcnt_enable_db_hash_lock_count(DbTableHash *tb, int enable);
diff --git a/erts/emulator/beam/erl_db_tree.c b/erts/emulator/beam/erl_db_tree.c
index 492ea81b63..723b3c5d29 100644
--- a/erts/emulator/beam/erl_db_tree.c
+++ b/erts/emulator/beam/erl_db_tree.c
@@ -459,6 +459,9 @@ db_lookup_dbterm_tree(Process *, DbTable *, Eterm key, Eterm obj,
static void
db_finalize_dbterm_tree(int cret, DbUpdateHandle *);
+static int db_get_binary_info_tree(Process*, DbTable*, Eterm key, Eterm *ret);
+
+
/*
** Static variables
*/
@@ -499,8 +502,10 @@ DbTableMethod db_tree =
db_print_tree,
db_foreach_offheap_tree,
db_lookup_dbterm_tree,
- db_finalize_dbterm_tree
-
+ db_finalize_dbterm_tree,
+ db_get_binary_info_tree,
+ db_first_tree, /* raw_first same as first */
+ db_next_tree /* raw_next same as next */
};
@@ -3151,6 +3156,14 @@ static TreeDbTerm *find_node(DbTableCommon *tb, TreeDbTerm *root,
return this;
}
+
+TreeDbTerm *db_find_tree_node_common(DbTableCommon *tb, TreeDbTerm *root,
+ Eterm key)
+{
+ return find_node(tb, root, key, NULL);
+}
+
+
/*
* Lookup a node and return the address of the node pointer in the tree
*/
@@ -3303,6 +3316,38 @@ db_finalize_dbterm_tree(int cret, DbUpdateHandle *handle)
db_finalize_dbterm_tree_common(cret, handle, tb);
}
+static int db_get_binary_info_tree(Process *p, DbTable *tbl, Eterm key, Eterm *ret)
+{
+ *ret = db_binary_info_tree_common(p, find_node(&tbl->common, tbl->tree.root,
+ key, &tbl->tree));
+ return DB_ERROR_NONE;
+}
+
+Eterm db_binary_info_tree_common(Process* p, TreeDbTerm* this)
+{
+ Eterm *hp, *hp_end;
+ Uint hsz;
+ Eterm ret;
+
+ if (this == NULL) {
+ ret = NIL;
+ } else {
+ ErlOffHeap oh;
+ hsz = 0;
+
+ oh.first = this->dbterm.first_oh;
+ erts_bld_bin_list(NULL, &hsz, &oh, NIL);
+
+ hp = HAlloc(p, hsz);
+ hp_end = hp + hsz;
+ oh.first = this->dbterm.first_oh;
+ ret = erts_bld_bin_list(&hp, NULL, &oh, NIL);
+ ASSERT(hp == hp_end); (void)hp_end;
+ }
+ return ret;
+}
+
+
/*
* Traverse the tree with a callback function, used by db_match_xxx
*/
@@ -3936,6 +3981,12 @@ static int doit_select_replace(DbTableCommon *tb, TreeDbTerm **this,
return 1;
}
+void
+erts_db_foreach_thr_prgr_offheap_tree(void (*func)(ErlOffHeap *, void *),
+ void *arg)
+{
+}
+
#ifdef TREE_DEBUG
static void do_dump_tree2(DbTableCommon* tb, int to, void *to_arg, int show,
TreeDbTerm *t, int offset)
diff --git a/erts/emulator/beam/erl_db_tree.h b/erts/emulator/beam/erl_db_tree.h
index 54da2a6bc1..06e0005801 100644
--- a/erts/emulator/beam/erl_db_tree.h
+++ b/erts/emulator/beam/erl_db_tree.h
@@ -53,4 +53,8 @@ void db_initialize_tree(void);
int db_create_tree(Process *p, DbTable *tbl);
+void
+erts_db_foreach_thr_prgr_offheap_tree(void (*func)(ErlOffHeap *, void *),
+ void *arg);
+
#endif /* _DB_TREE_H */
diff --git a/erts/emulator/beam/erl_db_tree_util.h b/erts/emulator/beam/erl_db_tree_util.h
index ba4a8f79e5..14afbd56f7 100644
--- a/erts/emulator/beam/erl_db_tree_util.h
+++ b/erts/emulator/beam/erl_db_tree_util.h
@@ -171,4 +171,8 @@ void db_finalize_dbterm_tree_common(int cret, DbUpdateHandle *handle,
DbTableTree *stack_container);
Sint cmp_partly_bound(Eterm partly_bound_key, Eterm bound_key);
+TreeDbTerm *db_find_tree_node_common(DbTableCommon*, TreeDbTerm *root,
+ Eterm key);
+Eterm db_binary_info_tree_common(Process*, TreeDbTerm*);
+
#endif /* _DB_TREE_UTIL_H */
diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c
index 1ea7074d21..096cb8a778 100644
--- a/erts/emulator/beam/erl_db_util.c
+++ b/erts/emulator/beam/erl_db_util.c
@@ -2612,7 +2612,10 @@ restart:
break;
case matchCaller:
ASSERT(c_p == self);
- if (!(c_p->cp) || !(cp = find_function_from_pc(c_p->cp))) {
+ t = c_p->stop[0];
+ if (is_not_CP(t)) {
+ *esp++ = am_undefined;
+ } else if (!(cp = find_function_from_pc(cp_val(t)))) {
*esp++ = am_undefined;
} else {
ehp = HAllocX(build_proc, 4, HEAP_XTRA);
@@ -3285,22 +3288,15 @@ Eterm db_copy_element_from_ets(DbTableCommon* tb, Process* p,
void db_cleanup_offheap_comp(DbTerm* obj)
{
union erl_off_heap_ptr u;
- ProcBin tmp;
+ struct erts_tmp_aligned_offheap tmp;
for (u.hdr = obj->first_oh; u.hdr; u.hdr = u.hdr->next) {
- if ((UWord)u.voidp % sizeof(Uint) != 0) { /* unaligned ptr */
- sys_memcpy(&tmp, u.voidp, sizeof(tmp));
- /* Warning, must pass (void*)-variable to memcpy. Otherwise it will
- cause Bus error on Sparc due to false compile time assumptions
- about word aligned memory (type cast is not enough) */
- u.pb = &tmp;
- }
+ erts_align_offheap(&u, &tmp);
switch (thing_subtag(u.hdr->thing_word)) {
case REFC_BINARY_SUBTAG:
erts_bin_release(u.pb->val);
break;
case FUN_SUBTAG:
- ASSERT(u.pb != &tmp);
if (erts_refc_dectest(&u.fun->fe->refc, 0) == 0) {
erts_erase_fun_entry(u.fun->fe);
}
@@ -3311,7 +3307,6 @@ void db_cleanup_offheap_comp(DbTerm* obj)
break;
default:
ASSERT(is_external_header(u.hdr->thing_word));
- ASSERT(u.pb != &tmp);
erts_deref_node_entry(u.ext->node, make_boxed(u.ep));
break;
}
@@ -5226,7 +5221,7 @@ static Eterm match_spec_test(Process *p, Eterm against, Eterm spec, int trace)
Eterm l;
Uint32 ret_flags;
Uint sz;
- BeamInstr *save_cp;
+ Eterm save_cp;
if (trace && !(is_list(against) || against == NIL)) {
return THE_NON_VALUE;
@@ -5270,13 +5265,13 @@ static Eterm match_spec_test(Process *p, Eterm against, Eterm spec, int trace)
++n;
l = CDR(list_val(l));
}
- save_cp = p->cp;
- p->cp = NULL;
+ save_cp = p->stop[0];
+ p->stop[0] = NIL;
res = erts_match_set_run_trace(p, p,
mps, arr, n,
ERTS_PAM_COPY_RESULT|ERTS_PAM_IGNORE_TRACE_SILENT,
&ret_flags);
- p->cp = save_cp;
+ p->stop[0] = save_cp;
} else {
n = 0;
arr = NULL;
diff --git a/erts/emulator/beam/erl_db_util.h b/erts/emulator/beam/erl_db_util.h
index 02d4dd6c9a..f038dba89a 100644
--- a/erts/emulator/beam/erl_db_util.h
+++ b/erts/emulator/beam/erl_db_util.h
@@ -235,6 +235,13 @@ typedef struct db_table_method
** not DB_ERROR_NONE, the object is removed from the table. */
void (*db_finalize_dbterm)(int cret, DbUpdateHandle* handle);
+ int (*db_get_binary_info)(Process*, DbTable* tb, Eterm key, Eterm* ret);
+
+ /* Raw first/next same as first/next but also return pseudo deleted keys.
+ Only internal use by ets:info(_,binary) */
+ int (*db_raw_first)(Process*, DbTable*, Eterm* ret);
+ int (*db_raw_next)(Process*, DbTable*, Eterm key, Eterm* ret);
+
} DbTableMethod;
typedef struct db_fixation {
@@ -536,6 +543,17 @@ ERTS_GLB_INLINE Eterm erts_db_make_match_prog_ref(Process *p, Binary *mp, Eterm
ERTS_GLB_INLINE Binary *erts_db_get_match_prog_binary(Eterm term);
ERTS_GLB_INLINE Binary *erts_db_get_match_prog_binary_unchecked(Eterm term);
+/* @brief Ensure off-heap header is word aligned, make a temporary copy if not.
+ * Needed when inspecting ETS off-heap lists that may contain unaligned
+ * ProcBins if table is 'compressed'.
+ */
+struct erts_tmp_aligned_offheap
+{
+ ProcBin proc_bin;
+};
+ERTS_GLB_INLINE void erts_align_offheap(union erl_off_heap_ptr*,
+ struct erts_tmp_aligned_offheap* tmp);
+
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
/*
@@ -568,6 +586,25 @@ erts_db_get_match_prog_binary(Eterm term)
return bp;
}
+ERTS_GLB_INLINE void
+erts_align_offheap(union erl_off_heap_ptr* ohp,
+ struct erts_tmp_aligned_offheap* tmp)
+{
+ if ((UWord)ohp->voidp % sizeof(UWord) != 0) {
+ /*
+ * ETS store word unaligned ProcBins in its compressed format.
+ * Make a temporary aligned copy.
+ *
+ * Warning, must pass (void*)-variable to memcpy. Otherwise it will
+ * cause Bus error on Sparc due to false compile time assumptions
+ * about word aligned memory (type cast is not enough).
+ */
+ sys_memcpy(tmp, ohp->voidp, sizeof(*tmp));
+ ASSERT(tmp->proc_bin.thing_word == HEADER_PROC_BIN);
+ ohp->pb = &tmp->proc_bin;
+ }
+}
+
#endif
/*
diff --git a/erts/emulator/beam/erl_fun.c b/erts/emulator/beam/erl_fun.c
index 257f9bf5b3..79a1fdb8b9 100644
--- a/erts/emulator/beam/erl_fun.c
+++ b/erts/emulator/beam/erl_fun.c
@@ -171,36 +171,33 @@ erts_erase_fun_entry(ErlFunEntry* fe)
erts_fun_write_unlock();
}
+struct fun_purge_foreach_args {
+ BeamInstr *start;
+ BeamInstr *end;
+};
+
+static void fun_purge_foreach(ErlFunEntry *fe, struct fun_purge_foreach_args *arg)
+{
+ BeamInstr* addr = fe->address;
+ if (arg->start <= addr && addr < arg->end) {
+ fe->pend_purge_address = addr;
+ ERTS_THR_WRITE_MEMORY_BARRIER;
+ fe->address = unloaded_fun;
+#ifdef HIPE
+ fe->pend_purge_native_address = fe->native_address;
+ hipe_set_closure_stub(fe);
+#endif
+ erts_purge_state_add_fun(fe);
+ }
+}
+
void
erts_fun_purge_prepare(BeamInstr* start, BeamInstr* end)
{
- int limit;
- HashBucket** bucket;
- int i;
+ struct fun_purge_foreach_args args = {start, end};
erts_fun_read_lock();
- limit = erts_fun_table.size;
- bucket = erts_fun_table.bucket;
- for (i = 0; i < limit; i++) {
- HashBucket* b = bucket[i];
-
- while (b) {
- ErlFunEntry* fe = (ErlFunEntry *) b;
- BeamInstr* addr = fe->address;
-
- if (start <= addr && addr < end) {
- fe->pend_purge_address = addr;
- ERTS_THR_WRITE_MEMORY_BARRIER;
- fe->address = unloaded_fun;
-#ifdef HIPE
- fe->pend_purge_native_address = fe->native_address;
- hipe_set_closure_stub(fe);
-#endif
- erts_purge_state_add_fun(fe);
- }
- b = b->next;
- }
- }
+ hash_foreach(&erts_fun_table, (HFOREACH_FUN)fun_purge_foreach, &args);
erts_fun_read_unlock();
}
@@ -250,36 +247,34 @@ erts_fun_purge_complete(ErlFunEntry **funs, Uint no)
ERTS_THR_WRITE_MEMORY_BARRIER;
}
+struct dump_fun_foreach_args {
+ fmtfn_t to;
+ void *to_arg;
+};
+
+static void
+dump_fun_foreach(ErlFunEntry *fe, struct dump_fun_foreach_args *args)
+{
+ erts_print(args->to, args->to_arg, "=fun\n");
+ erts_print(args->to, args->to_arg, "Module: %T\n", fe->module);
+ erts_print(args->to, args->to_arg, "Uniq: %d\n", fe->old_uniq);
+ erts_print(args->to, args->to_arg, "Index: %d\n",fe->old_index);
+ erts_print(args->to, args->to_arg, "Address: %p\n", fe->address);
+#ifdef HIPE
+ erts_print(args->to, args->to_arg, "Native_address: %p\n", fe->native_address);
+#endif
+ erts_print(args->to, args->to_arg, "Refc: %ld\n", erts_refc_read(&fe->refc, 1));
+}
+
void
erts_dump_fun_entries(fmtfn_t to, void *to_arg)
{
- int limit;
- HashBucket** bucket;
- int i;
+ struct dump_fun_foreach_args args = {to, to_arg};
int lock = !ERTS_IS_CRASH_DUMPING;
-
if (lock)
erts_fun_read_lock();
- limit = erts_fun_table.size;
- bucket = erts_fun_table.bucket;
- for (i = 0; i < limit; i++) {
- HashBucket* b = bucket[i];
-
- while (b) {
- ErlFunEntry* fe = (ErlFunEntry *) b;
- erts_print(to, to_arg, "=fun\n");
- erts_print(to, to_arg, "Module: %T\n", fe->module);
- erts_print(to, to_arg, "Uniq: %d\n", fe->old_uniq);
- erts_print(to, to_arg, "Index: %d\n",fe->old_index);
- erts_print(to, to_arg, "Address: %p\n", fe->address);
-#ifdef HIPE
- erts_print(to, to_arg, "Native_address: %p\n", fe->native_address);
-#endif
- erts_print(to, to_arg, "Refc: %ld\n", erts_refc_read(&fe->refc, 1));
- b = b->next;
- }
- }
+ hash_foreach(&erts_fun_table, (HFOREACH_FUN)dump_fun_foreach, &args);
if (lock)
erts_fun_read_unlock();
}
diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c
index 7ab8034606..4a6f204cb5 100644
--- a/erts/emulator/beam/erl_gc.c
+++ b/erts/emulator/beam/erl_gc.c
@@ -65,6 +65,8 @@
# define HARDDEBUG 1
#endif
+extern BeamInstr beam_apply[2];
+
/*
* Returns number of elements in an array.
*/
@@ -934,13 +936,15 @@ garbage_collect_hibernate(Process* p, int check_long_gc)
*/
erts_atomic32_read_bor_nob(&p->state, ERTS_PSFLG_GC);
ErtsGcQuickSanityCheck(p);
- ASSERT(p->stop == p->hend); /* Stack must be empty. */
+ ASSERT(p->stop == p->hend - 1); /* Only allow one continuation pointer. */
+ ASSERT(p->stop[0] == make_cp(beam_apply+1));
/*
* Do it.
*/
heap_size = p->heap_sz + (p->old_htop - p->old_heap) + p->mbuf_sz;
+ heap_size += 1; /* Reserve place for continuation pointer */
heap = (Eterm*) ERTS_HEAP_ALLOC(ERTS_ALC_T_TMP_HEAP,
sizeof(Eterm)*heap_size);
@@ -966,13 +970,11 @@ garbage_collect_hibernate(Process* p, int check_long_gc)
p->high_water = htop;
p->htop = htop;
p->hend = p->heap + heap_size;
- p->stop = p->hend;
+ p->stop = p->hend - 1;
p->heap_sz = heap_size;
heap_size = actual_size = p->htop - p->heap;
- if (heap_size == 0) {
- heap_size = 1; /* We want a heap... */
- }
+ heap_size += 1; /* Reserve place for continuation pointer */
FLAGS(p) &= ~F_FORCE_GC;
p->live_hf_end = ERTS_INVALID_HFRAG_PTR;
@@ -988,14 +990,15 @@ garbage_collect_hibernate(Process* p, int check_long_gc)
* hibernated.
*/
- ASSERT(p->hend - p->stop == 0); /* Empty stack */
ASSERT(actual_size < p->heap_sz);
heap = ERTS_HEAP_ALLOC(ERTS_ALC_T_HEAP, sizeof(Eterm)*heap_size);
sys_memcpy((void *) heap, (void *) p->heap, actual_size*sizeof(Eterm));
ERTS_HEAP_FREE(ERTS_ALC_T_TMP_HEAP, p->heap, p->heap_sz*sizeof(Eterm));
- p->stop = p->hend = heap + heap_size;
+ p->hend = heap + heap_size;
+ p->stop = p->hend - 1;
+ p->stop[0] = make_cp(beam_apply+1);
offs = heap - p->heap;
area = (char *) p->heap;
@@ -2585,7 +2588,7 @@ setup_rootset(Process *p, Eterm *objv, int nobj, Rootset *rootset)
/*
* If a NIF or BIF has saved arguments, they need to be added
*/
- if (erts_setup_nif_export_rootset(p, &roots[n].v, &roots[n].sz))
+ if (erts_setup_nfunc_rootset(p, &roots[n].v, &roots[n].sz))
n++;
ASSERT(n <= rootset->size);
@@ -2887,7 +2890,7 @@ sweep_off_heap(Process *p, int fullsweep)
if (is_external_header(((struct erl_off_heap_header*) boxed_val(ptr->thing_word))->thing_word))
erts_node_bookkeep(((ExternalThing*)ptr)->node,
make_boxed(&ptr->thing_word),
- ERL_NODE_DEC);
+ ERL_NODE_DEC, __FILE__, __LINE__);
*prev = ptr = (struct erl_off_heap_header*) boxed_val(ptr->thing_word);
ASSERT(!IS_MOVED_BOXED(ptr->thing_word));
switch (ptr->thing_word) {
@@ -2918,7 +2921,7 @@ sweep_off_heap(Process *p, int fullsweep)
if (is_external_header(ptr->thing_word)) {
erts_node_bookkeep(((ExternalThing*)ptr)->node,
make_boxed(&ptr->thing_word),
- ERL_NODE_INC);
+ ERL_NODE_INC, __FILE__, __LINE__);
}
prev = &ptr->next;
ptr = ptr->next;
@@ -3089,9 +3092,11 @@ offset_heap(Eterm* hp, Uint sz, Sint offs, char* area, Uint area_size)
if (is_external_header(oh->thing_word)) {
erts_node_bookkeep(((ExternalThing*)oh)->node,
- make_boxed(((Eterm*)oh)-offs), ERL_NODE_DEC);
+ make_boxed(((Eterm*)oh)-offs),
+ ERL_NODE_DEC, __FILE__, __LINE__);
erts_node_bookkeep(((ExternalThing*)oh)->node,
- make_boxed((Eterm*)oh), ERL_NODE_INC);
+ make_boxed((Eterm*)oh), ERL_NODE_INC,
+ __FILE__, __LINE__);
}
if (ErtsInArea(oh->next, area, area_size)) {
@@ -3231,7 +3236,7 @@ offset_one_rootset(Process *p, Sint offs, char* area, Uint area_size,
offset_heap_ptr(objv, nobj, offs, area, area_size);
}
offset_off_heap(p, offs, area, area_size);
- if (erts_setup_nif_export_rootset(p, &v, &sz))
+ if (erts_setup_nfunc_rootset(p, &v, &sz))
offset_heap_ptr(v, sz, offs, area, area_size);
}
diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c
index 547e4064a2..4d0ebbd1ed 100644
--- a/erts/emulator/beam/erl_init.c
+++ b/erts/emulator/beam/erl_init.c
@@ -354,6 +354,7 @@ erl_init(int ncpu,
erts_init_bif_chksum();
erts_init_bif_binary();
erts_init_bif_guard();
+ erts_init_bif_lists();
erts_init_bif_persistent_term();
erts_init_bif_re();
erts_init_unicode(); /* after RE to get access to PCRE unicode */
diff --git a/erts/emulator/beam/erl_monitor_link.c b/erts/emulator/beam/erl_monitor_link.c
index 1c6b4afaa3..43028be39d 100644
--- a/erts/emulator/beam/erl_monitor_link.c
+++ b/erts/emulator/beam/erl_monitor_link.c
@@ -671,6 +671,24 @@ erts_monitor_tree_foreach(ErtsMonitor *root,
arg);
}
+void
+erts_debug_monitor_tree_destroying_foreach(ErtsMonitor *root,
+ ErtsMonitorFunc func,
+ void *arg,
+ void *vysp)
+{
+ void *tmp_vysp = erts_alloc(ERTS_ALC_T_ML_YIELD_STATE,
+ sizeof(ErtsMonLnkYieldState));
+ Sint reds;
+ sys_memcpy(tmp_vysp, tmp_vysp, sizeof(ErtsMonLnkYieldState));
+ do {
+ reds = ml_rbt_foreach_yielding((ErtsMonLnkNode *) root,
+ (ErtsMonLnkNodeFunc) func,
+ arg, &tmp_vysp, (Sint) INT_MAX);
+ } while (reds <= 0);
+ ERTS_ML_ASSERT(!tmp_vysp);
+}
+
int
erts_monitor_tree_foreach_yielding(ErtsMonitor *root,
ErtsMonitorFunc func,
@@ -716,6 +734,19 @@ erts_monitor_list_foreach(ErtsMonitor *list,
arg, &ystate, (Sint) INT_MAX));
}
+void
+erts_debug_monitor_list_destroying_foreach(ErtsMonitor *list,
+ ErtsMonitorFunc func,
+ void *arg,
+ void *vysp)
+{
+ void *tmp_vysp = vysp;
+ while (!ml_dl_list_foreach_yielding((ErtsMonLnkNode *) list,
+ (int (*)(ErtsMonLnkNode *, void *, Sint)) func,
+ arg, &tmp_vysp, (Sint) INT_MAX));
+ ERTS_ML_ASSERT(!tmp_vysp);
+}
+
int
erts_monitor_list_foreach_yielding(ErtsMonitor *list,
ErtsMonitorFunc func,
@@ -1080,6 +1111,24 @@ erts_link_tree_foreach(ErtsLink *root,
}
+void
+erts_debug_link_tree_destroying_foreach(ErtsLink *root,
+ ErtsLinkFunc func,
+ void *arg,
+ void *vysp)
+{
+ void *tmp_vysp = erts_alloc(ERTS_ALC_T_ML_YIELD_STATE,
+ sizeof(ErtsMonLnkYieldState));
+ Sint reds;
+ sys_memcpy(tmp_vysp, vysp, sizeof(ErtsMonLnkYieldState));
+ do {
+ reds = ml_rbt_foreach_yielding((ErtsMonLnkNode *) root,
+ (ErtsMonLnkNodeFunc) func,
+ arg, &tmp_vysp, (Sint) INT_MAX);
+ } while (reds <= 0);
+ ERTS_ML_ASSERT(!tmp_vysp);
+}
+
int
erts_link_tree_foreach_yielding(ErtsLink *root,
ErtsLinkFunc func,
diff --git a/erts/emulator/beam/erl_monitor_link.h b/erts/emulator/beam/erl_monitor_link.h
index eff861fce8..86be400c09 100644
--- a/erts/emulator/beam/erl_monitor_link.h
+++ b/erts/emulator/beam/erl_monitor_link.h
@@ -1509,6 +1509,17 @@ ERTS_GLB_INLINE ErtsMonitorSuspend *erts_monitor_suspend(ErtsMonitor *mon)
#endif
+void
+erts_debug_monitor_tree_destroying_foreach(ErtsMonitor *root,
+ ErtsMonitorFunc func,
+ void *arg,
+ void *vysp);
+void
+erts_debug_monitor_list_destroying_foreach(ErtsMonitor *list,
+ ErtsMonitorFunc func,
+ void *arg,
+ void *vysp);
+
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
* Link Operations *
\* */
@@ -2365,4 +2376,10 @@ erts_link_dist_delete(ErtsLink *lnk)
#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */
+void
+erts_debug_link_tree_destroying_foreach(ErtsLink *root,
+ ErtsLinkFunc func,
+ void *arg,
+ void *vysp);
+
#endif /* ERL_MONITOR_LINK_H__ */
diff --git a/erts/emulator/beam/erl_nfunc_sched.c b/erts/emulator/beam/erl_nfunc_sched.c
index b8cf2bee0e..8263a6e9b7 100644
--- a/erts/emulator/beam/erl_nfunc_sched.c
+++ b/erts/emulator/beam/erl_nfunc_sched.c
@@ -30,77 +30,37 @@
#include "erl_nfunc_sched.h"
#include "erl_trace.h"
-NifExport *
-erts_new_proc_nif_export(Process *c_p, int argc)
+ErtsNativeFunc *
+erts_new_proc_nfunc(Process *c_p, int argc)
{
+ ErtsNativeFunc *nep, *old_nep;
size_t size;
- int i;
- NifExport *nep, *old_nep;
-
- size = sizeof(NifExport) + (argc-1)*sizeof(Eterm);
- nep = erts_alloc(ERTS_ALC_T_NIF_TRAP_EXPORT, size);
- for (i = 0; i < ERTS_NUM_CODE_IX; i++)
- nep->exp.addressv[i] = &nep->exp.beam[0];
+ size = sizeof(ErtsNativeFunc) + (argc-1)*sizeof(Eterm);
+ nep = erts_alloc(ERTS_ALC_T_NFUNC_TRAP_WRAPPER, size);
nep->argc = -1; /* unused marker */
nep->argv_size = argc;
- nep->trace = NULL;
- old_nep = ERTS_PROC_SET_NIF_TRAP_EXPORT(c_p, nep);
+ old_nep = ERTS_PROC_SET_NFUNC_TRAP_WRAPPER(c_p, nep);
if (old_nep) {
- ASSERT(!nep->trace);
- erts_free(ERTS_ALC_T_NIF_TRAP_EXPORT, old_nep);
+ erts_free(ERTS_ALC_T_NFUNC_TRAP_WRAPPER, old_nep);
}
return nep;
}
void
-erts_destroy_nif_export(Process *p)
+erts_destroy_nfunc(Process *p)
{
- NifExport *nep = ERTS_PROC_SET_NIF_TRAP_EXPORT(p, NULL);
+ ErtsNativeFunc *nep = ERTS_PROC_SET_NFUNC_TRAP_WRAPPER(p, NULL);
if (nep) {
if (nep->m)
- erts_nif_export_cleanup_nif_mod(nep);
- erts_free(ERTS_ALC_T_NIF_TRAP_EXPORT, nep);
+ erts_nfunc_cleanup_nif_mod(nep);
+ erts_free(ERTS_ALC_T_NFUNC_TRAP_WRAPPER, nep);
}
}
-void
-erts_nif_export_save_trace(Process *c_p, NifExport *nep, int applying,
- Export* ep, BeamInstr *cp, Uint32 flags,
- Uint32 flags_meta, BeamInstr* I,
- ErtsTracer meta_tracer)
-{
- NifExportTrace *netp;
- ASSERT(nep && nep->argc >= 0);
- ASSERT(!nep->trace);
- netp = erts_alloc(ERTS_ALC_T_NIF_EXP_TRACE,
- sizeof(NifExportTrace));
- netp->applying = applying;
- netp->ep = ep;
- netp->cp = cp;
- netp->flags = flags;
- netp->flags_meta = flags_meta;
- netp->I = I;
- netp->meta_tracer = NIL;
- erts_tracer_update(&netp->meta_tracer, meta_tracer);
- nep->trace = netp;
-}
-
-void
-erts_nif_export_restore_trace(Process *c_p, Eterm result, NifExport *nep)
-{
- NifExportTrace *netp = nep->trace;
- nep->trace = NULL;
- erts_bif_trace_epilogue(c_p, result, netp->applying, netp->ep,
- netp->cp, netp->flags, netp->flags_meta,
- netp->I, netp->meta_tracer);
- erts_tracer_update(&netp->meta_tracer, NIL);
- erts_free(ERTS_ALC_T_NIF_EXP_TRACE, netp);
-}
-
-NifExport *
-erts_nif_export_schedule(Process *c_p, Process *dirty_shadow_proc,
+ErtsNativeFunc *
+erts_nfunc_schedule(Process *c_p, Process *dirty_shadow_proc,
ErtsCodeMFA *mfa, BeamInstr *pc,
BeamInstr instr,
void *dfunc, void *ifunc,
@@ -110,7 +70,7 @@ erts_nif_export_schedule(Process *c_p, Process *dirty_shadow_proc,
Process *used_proc;
ErtsSchedulerData *esdp;
Eterm* reg;
- NifExport* nep;
+ ErtsNativeFunc* nep;
int i;
ERTS_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p)
@@ -133,10 +93,10 @@ erts_nif_export_schedule(Process *c_p, Process *dirty_shadow_proc,
reg = esdp->x_reg_array;
if (mfa)
- nep = erts_get_proc_nif_export(c_p, (int) mfa->arity);
+ nep = erts_get_proc_nfunc(c_p, (int) mfa->arity);
else {
/* If no mfa, this is not the first schedule... */
- nep = ERTS_PROC_GET_NIF_TRAP_EXPORT(c_p);
+ nep = ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(c_p);
ASSERT(nep && nep->argc >= 0);
}
@@ -148,16 +108,15 @@ erts_nif_export_schedule(Process *c_p, Process *dirty_shadow_proc,
for (i = 0; i < (int) mfa->arity; i++)
nep->argv[i] = reg[i];
nep->pc = pc;
- nep->cp = c_p->cp;
nep->mfa = mfa;
nep->current = c_p->current;
ASSERT(argc >= 0);
nep->argc = (int) mfa->arity;
nep->m = NULL;
- ASSERT(!erts_check_nif_export_in_area(c_p,
+ ASSERT(!erts_check_nfunc_in_area(c_p,
(char *) nep,
- (sizeof(NifExport)
+ (sizeof(ErtsNativeFunc)
+ (sizeof(Eterm)
*(nep->argc-1)))));
}
@@ -167,14 +126,14 @@ erts_nif_export_schedule(Process *c_p, Process *dirty_shadow_proc,
reg[i] = argv[i];
}
ASSERT(is_atom(mod) && is_atom(func));
- nep->exp.info.mfa.module = mod;
- nep->exp.info.mfa.function = func;
- nep->exp.info.mfa.arity = (Uint) argc;
- nep->exp.beam[0] = (BeamInstr) instr; /* call_nif || apply_bif */
- nep->exp.beam[1] = (BeamInstr) dfunc;
+ nep->trampoline.info.mfa.module = mod;
+ nep->trampoline.info.mfa.function = func;
+ nep->trampoline.info.mfa.arity = (Uint) argc;
+ nep->trampoline.call_op = (BeamInstr) instr; /* call_bif || call_nif */
+ nep->trampoline.dfunc = (BeamInstr) dfunc;
nep->func = ifunc;
used_proc->arity = argc;
used_proc->freason = TRAP;
- used_proc->i = (BeamInstr*) nep->exp.addressv[0];
+ used_proc->i = (BeamInstr*)&nep->trampoline.call_op;
return nep;
}
diff --git a/erts/emulator/beam/erl_nfunc_sched.h b/erts/emulator/beam/erl_nfunc_sched.h
index 1cb252eba5..4dae242d4f 100644
--- a/erts/emulator/beam/erl_nfunc_sched.h
+++ b/erts/emulator/beam/erl_nfunc_sched.h
@@ -25,92 +25,78 @@
#include "bif.h"
#include "error.h"
-typedef struct {
- int applying;
- Export* ep;
- BeamInstr *cp;
- Uint32 flags;
- Uint32 flags_meta;
- BeamInstr* I;
- ErtsTracer meta_tracer;
-} NifExportTrace;
-
/*
- * NIF exports need a few more items than the Export struct provides,
- * including the erl_module_nif* and a NIF function pointer, so the
- * NifExport below adds those. The Export member must be first in the
- * struct. A number of values are stored for error handling purposes
- * only.
+ * Native function wrappers are used to schedule native functions on both
+ * normal and dirty schedulers.
+ *
+ * A number of values are only stored for error handling, and the fields
+ * following `current` can be omitted when a wrapper is statically "scheduled"
+ * through placement in a function stub.
*
- * 'argc' is >= 0 when NifExport is in use, and < 0 when not.
+ * 'argc' is >= 0 when ErtsNativeFunc is in use, and < 0 when not.
*/
typedef struct {
- Export exp;
+ struct {
+ ErtsCodeInfo info;
+ BeamInstr call_op; /* call_bif || call_nif */
+ BeamInstr dfunc;
+ } trampoline;
+
struct erl_module_nif* m; /* NIF module, or NULL if BIF */
void *func; /* Indirect NIF or BIF to execute (may be unused) */
ErtsCodeMFA *current;/* Current as set when originally called */
- NifExportTrace *trace;
/* --- The following is only used on error --- */
BeamInstr *pc; /* Program counter */
- BeamInstr *cp; /* Continuation pointer */
ErtsCodeMFA *mfa; /* MFA of original call */
int argc; /* Number of arguments in original call */
int argv_size; /* Allocated size of argv */
Eterm argv[1]; /* Saved arguments from the original call */
-} NifExport;
-
-NifExport *erts_new_proc_nif_export(Process *c_p, int argc);
-void erts_nif_export_save_trace(Process *c_p, NifExport *nep, int applying,
- Export* ep, BeamInstr *cp, Uint32 flags,
- Uint32 flags_meta, BeamInstr* I,
- ErtsTracer meta_tracer);
-void erts_nif_export_restore_trace(Process *c_p, Eterm result, NifExport *nep);
-void erts_destroy_nif_export(Process *p);
-NifExport *erts_nif_export_schedule(Process *c_p, Process *dirty_shadow_proc,
- ErtsCodeMFA *mfa, BeamInstr *pc,
- BeamInstr instr,
- void *dfunc, void *ifunc,
- Eterm mod, Eterm func,
- int argc, const Eterm *argv);
-void erts_nif_export_cleanup_nif_mod(NifExport *ep); /* erl_nif.c */
-ERTS_GLB_INLINE NifExport *erts_get_proc_nif_export(Process *c_p, int extra);
-ERTS_GLB_INLINE int erts_setup_nif_export_rootset(Process* proc, Eterm** objv,
- Uint* nobj);
-ERTS_GLB_INLINE int erts_check_nif_export_in_area(Process *p,
- char *start, Uint size);
-ERTS_GLB_INLINE void erts_nif_export_restore(Process *c_p, NifExport *ep,
- Eterm result);
-ERTS_GLB_INLINE void erts_nif_export_restore_error(Process* c_p, BeamInstr **pc,
- Eterm *reg, ErtsCodeMFA **nif_mfa);
-ERTS_GLB_INLINE int erts_nif_export_check_save_trace(Process *c_p, Eterm result,
- int applying, Export* ep,
- BeamInstr *cp, Uint32 flags,
- Uint32 flags_meta, BeamInstr* I,
- ErtsTracer meta_tracer);
+} ErtsNativeFunc;
+
+ErtsNativeFunc *erts_new_proc_nfunc(Process *c_p, int argc);
+void erts_destroy_nfunc(Process *p);
+ErtsNativeFunc *erts_nfunc_schedule(Process *c_p, Process *dirty_shadow_proc,
+ ErtsCodeMFA *mfa, BeamInstr *pc,
+ BeamInstr instr,
+ void *dfunc, void *ifunc,
+ Eterm mod, Eterm func,
+ int argc, const Eterm *argv);
+void erts_nfunc_cleanup_nif_mod(ErtsNativeFunc *ep); /* erl_nif.c */
+ERTS_GLB_INLINE ErtsNativeFunc *erts_get_proc_nfunc(Process *c_p, int extra);
+ERTS_GLB_INLINE int erts_setup_nfunc_rootset(Process* proc, Eterm** objv,
+ Uint* nobj);
+ERTS_GLB_INLINE int erts_check_nfunc_in_area(Process *p,
+ char *start, Uint size);
+ERTS_GLB_INLINE void erts_nfunc_restore(Process *c_p, ErtsNativeFunc *ep,
+ Eterm result);
+ERTS_GLB_INLINE void erts_nfunc_restore_error(Process* c_p,
+ BeamInstr **pc,
+ Eterm *reg,
+ ErtsCodeMFA **nif_mfa);
ERTS_GLB_INLINE Process *erts_proc_shadow2real(Process *c_p);
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
-ERTS_GLB_INLINE NifExport *
-erts_get_proc_nif_export(Process *c_p, int argc)
+ERTS_GLB_INLINE ErtsNativeFunc *
+erts_get_proc_nfunc(Process *c_p, int argc)
{
- NifExport *nep = ERTS_PROC_GET_NIF_TRAP_EXPORT(c_p);
+ ErtsNativeFunc *nep = ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(c_p);
if (!nep || (nep->argc < 0 && nep->argv_size < argc))
- return erts_new_proc_nif_export(c_p, argc);
+ return erts_new_proc_nfunc(c_p, argc);
return nep;
}
/*
* If a process has saved arguments, they need to be part of the GC
* rootset. The function below is called from setup_rootset() in
- * erl_gc.c. Any exception term saved in the NifExport is also made
+ * erl_gc.c. Any exception term saved in the ErtsNativeFunc is also made
* part of the GC rootset here; it always resides in rootset[0].
*/
ERTS_GLB_INLINE int
-erts_setup_nif_export_rootset(Process* proc, Eterm** objv, Uint* nobj)
+erts_setup_nfunc_rootset(Process* proc, Eterm** objv, Uint* nobj)
{
- NifExport* ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc);
+ ErtsNativeFunc* ep = (ErtsNativeFunc*) ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(proc);
if (!ep || ep->argc <= 0)
return 0;
@@ -121,18 +107,16 @@ erts_setup_nif_export_rootset(Process* proc, Eterm** objv, Uint* nobj)
}
/*
- * Check if nif export points into code area...
+ * Check if native func wrapper points into code area...
*/
ERTS_GLB_INLINE int
-erts_check_nif_export_in_area(Process *p, char *start, Uint size)
+erts_check_nfunc_in_area(Process *p, char *start, Uint size)
{
- NifExport *nep = ERTS_PROC_GET_NIF_TRAP_EXPORT(p);
+ ErtsNativeFunc *nep = ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(p);
if (!nep || nep->argc < 0)
return 0;
if (ErtsInArea(nep->pc, start, size))
return 1;
- if (ErtsInArea(nep->cp, start, size))
- return 1;
if (ErtsInArea(nep->mfa, start, size))
return 1;
if (ErtsInArea(nep->current, start, size))
@@ -141,7 +125,7 @@ erts_check_nif_export_in_area(Process *p, char *start, Uint size)
}
ERTS_GLB_INLINE void
-erts_nif_export_restore(Process *c_p, NifExport *ep, Eterm result)
+erts_nfunc_restore(Process *c_p, ErtsNativeFunc *ep, Eterm result)
{
ASSERT(!ERTS_SCHEDULER_IS_DIRTY(erts_get_scheduler_data()));
ERTS_LC_ASSERT(!(c_p->static_flags
@@ -151,43 +135,21 @@ erts_nif_export_restore(Process *c_p, NifExport *ep, Eterm result)
c_p->current = ep->current;
ep->argc = -1; /* Unused nif-export marker... */
- if (ep->trace)
- erts_nif_export_restore_trace(c_p, result, ep);
}
ERTS_GLB_INLINE void
-erts_nif_export_restore_error(Process* c_p, BeamInstr **pc,
+erts_nfunc_restore_error(Process* c_p, BeamInstr **pc,
Eterm *reg, ErtsCodeMFA **nif_mfa)
{
- NifExport *nep = (NifExport *) ERTS_PROC_GET_NIF_TRAP_EXPORT(c_p);
+ ErtsNativeFunc *nep = (ErtsNativeFunc *) ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(c_p);
int ix;
ASSERT(nep);
*pc = nep->pc;
- c_p->cp = nep->cp;
*nif_mfa = nep->mfa;
for (ix = 0; ix < nep->argc; ix++)
reg[ix] = nep->argv[ix];
- erts_nif_export_restore(c_p, nep, THE_NON_VALUE);
-}
-
-ERTS_GLB_INLINE int
-erts_nif_export_check_save_trace(Process *c_p, Eterm result,
- int applying, Export* ep,
- BeamInstr *cp, Uint32 flags,
- Uint32 flags_meta, BeamInstr* I,
- ErtsTracer meta_tracer)
-{
- if (is_non_value(result) && c_p->freason == TRAP) {
- NifExport *nep = ERTS_PROC_GET_NIF_TRAP_EXPORT(c_p);
- if (nep && nep->argc >= 0) {
- erts_nif_export_save_trace(c_p, nep, applying, ep,
- cp, flags, flags_meta,
- I, meta_tracer);
- return 1;
- }
- }
- return 0;
+ erts_nfunc_restore(c_p, nep, THE_NON_VALUE);
}
ERTS_GLB_INLINE Process *
@@ -210,10 +172,10 @@ erts_proc_shadow2real(Process *c_p)
#if defined(ERTS_WANT_NFUNC_SCHED_INTERNALS__) && !defined(ERTS_NFUNC_SCHED_INTERNALS__)
#define ERTS_NFUNC_SCHED_INTERNALS__
-#define ERTS_I_BEAM_OP_TO_NIF_EXPORT(I) \
- (ASSERT(BeamIsOpCode(*(I), op_apply_bif) || \
- BeamIsOpCode(*(I), op_call_nif)), \
- ((NifExport *) (((char *) (I)) - offsetof(NifExport, exp.beam[0]))))
+#define ERTS_I_BEAM_OP_TO_NFUNC(I) \
+ (ASSERT(BeamIsOpCode(*(I), op_call_bif_W) || \
+ BeamIsOpCode(*(I), op_call_nif_WWW)), \
+ ((ErtsNativeFunc *) (((char *) (I)) - offsetof(ErtsNativeFunc, trampoline.call_op))))
#include "erl_message.h"
diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c
index ce43cb9e71..6e27b4b7cb 100644
--- a/erts/emulator/beam/erl_nif.c
+++ b/erts/emulator/beam/erl_nif.c
@@ -309,10 +309,10 @@ void erts_post_nif(ErlNifEnv* env)
/*
- * Initialize a NifExport struct. Create it if needed and store it in the
+ * Initialize a ErtsNativeFunc struct. Create it if needed and store it in the
* proc. The direct_fp function is what will be invoked by op_call_nif, and
* the indirect_fp function, if not NULL, is what the direct_fp function
- * will call. If the allocated NifExport isn't enough to hold all of argv,
+ * will call. If the allocated ErtsNativeFunc isn't enough to hold all of argv,
* allocate a larger one. Save 'current' and registers if first time this
* call is scheduled.
*/
@@ -321,7 +321,7 @@ static ERTS_INLINE ERL_NIF_TERM
schedule(ErlNifEnv* env, NativeFunPtr direct_fp, NativeFunPtr indirect_fp,
Eterm mod, Eterm func_name, int argc, const ERL_NIF_TERM argv[])
{
- NifExport *ep;
+ ErtsNativeFunc *ep;
Process *c_p, *dirty_shadow_proc;
execution_state(env, &c_p, NULL);
@@ -332,10 +332,10 @@ schedule(ErlNifEnv* env, NativeFunPtr direct_fp, NativeFunPtr indirect_fp,
ERTS_LC_ASSERT(ERTS_PROC_LOCK_MAIN & erts_proc_lc_my_proc_locks(c_p));
- ep = erts_nif_export_schedule(c_p, dirty_shadow_proc,
+ ep = erts_nfunc_schedule(c_p, dirty_shadow_proc,
c_p->current,
- c_p->cp,
- BeamOpCodeAddr(op_call_nif),
+ cp_val(c_p->stop[0]),
+ BeamOpCodeAddr(op_call_nif_WWW),
direct_fp, indirect_fp,
mod, func_name,
argc, (const Eterm *) argv);
@@ -356,7 +356,7 @@ erts_call_dirty_nif(ErtsSchedulerData *esdp, Process *c_p, BeamInstr *I, Eterm *
{
int exiting;
ERL_NIF_TERM *argv = (ERL_NIF_TERM *) reg;
- NifExport *nep = ERTS_I_BEAM_OP_TO_NIF_EXPORT(I);
+ ErtsNativeFunc *nep = ERTS_I_BEAM_OP_TO_NFUNC(I);
ErtsCodeMFA *codemfa = erts_code_to_codemfa(I);
NativeFunPtr dirty_nif = (NativeFunPtr) I[1];
ErlNifEnv env;
@@ -364,7 +364,7 @@ erts_call_dirty_nif(ErtsSchedulerData *esdp, Process *c_p, BeamInstr *I, Eterm *
#ifdef DEBUG
erts_aint32_t state = erts_atomic32_read_nob(&c_p->state);
- ASSERT(nep == ERTS_PROC_GET_NIF_TRAP_EXPORT(c_p));
+ ASSERT(nep == ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(c_p));
ASSERT(!c_p->scheduler_data);
ASSERT((state & ERTS_PSFLG_DIRTY_RUNNING)
@@ -2823,7 +2823,7 @@ int enif_consume_timeslice(ErlNifEnv* env, int percent)
}
static ERTS_INLINE void
-nif_export_cleanup_nif_mod(NifExport *ep)
+nfunc_cleanup_nif_mod(ErtsNativeFunc *ep)
{
if (erts_refc_dectest(&ep->m->rt_dtor_cnt, 0) == 0 && ep->m->mod == NULL)
close_lib(ep->m);
@@ -2831,17 +2831,17 @@ nif_export_cleanup_nif_mod(NifExport *ep)
}
void
-erts_nif_export_cleanup_nif_mod(NifExport *ep)
+erts_nfunc_cleanup_nif_mod(ErtsNativeFunc *ep)
{
- nif_export_cleanup_nif_mod(ep);
+ nfunc_cleanup_nif_mod(ep);
}
static ERTS_INLINE void
-nif_export_restore(Process *c_p, NifExport *ep, Eterm res)
+nfunc_restore(Process *c_p, ErtsNativeFunc *ep, Eterm res)
{
- erts_nif_export_restore(c_p, ep, res);
+ erts_nfunc_restore(c_p, ep, res);
ASSERT(ep->m);
- nif_export_cleanup_nif_mod(ep);
+ nfunc_cleanup_nif_mod(ep);
}
@@ -2858,15 +2858,15 @@ static ERL_NIF_TERM
dirty_nif_finalizer(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
Process* proc;
- NifExport* ep;
+ ErtsNativeFunc* ep;
execution_state(env, &proc, NULL);
ASSERT(argc == 1);
ASSERT(!ERTS_SCHEDULER_IS_DIRTY(erts_proc_sched_data(proc)));
- ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc);
+ ep = (ErtsNativeFunc*) ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(proc);
ASSERT(ep);
- nif_export_restore(proc, ep, argv[0]);
+ nfunc_restore(proc, ep, argv[0]);
return argv[0];
}
@@ -2878,21 +2878,22 @@ dirty_nif_exception(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
ERL_NIF_TERM ret;
Process* proc;
- NifExport* ep;
+ ErtsNativeFunc* ep;
Eterm exception;
execution_state(env, &proc, NULL);
ASSERT(argc == 1);
ASSERT(!ERTS_SCHEDULER_IS_DIRTY(erts_proc_sched_data(proc)));
- ep = (NifExport*) ERTS_PROC_GET_NIF_TRAP_EXPORT(proc);
+ ep = (ErtsNativeFunc*) ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(proc);
ASSERT(ep);
exception = argv[0]; /* argv overwritten by restore below... */
- nif_export_cleanup_nif_mod(ep);
+ nfunc_cleanup_nif_mod(ep);
ret = enif_raise_exception(env, exception);
- /* Restore orig info for error and clear nif export in handle_error() */
- proc->freason |= EXF_RESTORE_NIF;
+ /* Restore orig info for error and clear native func wrapper in
+ * handle_error() */
+ proc->freason |= EXF_RESTORE_NFUNC;
return ret;
}
@@ -2929,7 +2930,7 @@ static_schedule_dirty_nif(ErlNifEnv* env, erts_aint32_t dirty_psflg,
int argc, const ERL_NIF_TERM argv[])
{
Process *proc;
- NifExport *ep;
+ ErtsNativeFunc *ep;
Eterm mod, func;
NativeFunPtr fp;
@@ -2939,12 +2940,11 @@ static_schedule_dirty_nif(ErlNifEnv* env, erts_aint32_t dirty_psflg,
* Called in order to schedule statically determined
* dirty NIF calls...
*
- * Note that 'current' does not point into a NifExport
- * structure; only a structure with similar
- * parts (located in code).
+ * Note that 'current' does not point into a ErtsNativeFunc
+ * structure; only a structure with similar parts (located in code).
*/
- ep = ErtsContainerStruct(proc->current, NifExport, exp.info.mfa);
+ ep = ErtsContainerStruct(proc->current, ErtsNativeFunc, trampoline.info.mfa);
mod = proc->current->module;
func = proc->current->function;
fp = (NativeFunPtr) ep->func;
@@ -2983,12 +2983,12 @@ execute_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
Process* proc;
NativeFunPtr fp;
- NifExport* ep;
+ ErtsNativeFunc* ep;
ERL_NIF_TERM result;
execution_state(env, &proc, NULL);
- ep = ErtsContainerStruct(proc->current, NifExport, exp.info.mfa);
+ ep = ErtsContainerStruct(proc->current, ErtsNativeFunc, trampoline.info.mfa);
fp = ep->func;
ASSERT(ep);
ASSERT(!env->exception_thrown);
@@ -3001,20 +3001,20 @@ execute_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
result = (*fp)(env, argc, argv);
- ASSERT(ep == ERTS_PROC_GET_NIF_TRAP_EXPORT(proc));
+ ASSERT(ep == ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(proc));
if (is_value(result) || proc->freason != TRAP) {
/* Done (not rescheduled)... */
ASSERT(ep->func == ERTS_DBG_NIF_NOT_SCHED_MARKER);
if (!env->exception_thrown)
- nif_export_restore(proc, ep, result);
+ nfunc_restore(proc, ep, result);
else {
- nif_export_cleanup_nif_mod(ep);
+ nfunc_cleanup_nif_mod(ep);
/*
* Restore orig info for error and clear nif
* export in handle_error()
*/
- proc->freason |= EXF_RESTORE_NIF;
+ proc->freason |= EXF_RESTORE_NFUNC;
}
}
@@ -4117,8 +4117,23 @@ static struct erl_module_nif* create_lib(const ErlNifEntry* src)
return lib;
};
+/* load_nif/2 is implemented as an instruction as it needs to know where it
+ * was called from, and it's a pain to get that information in a BIF.
+ *
+ * This is a small stub that rejects apply(erlang, load_nif, [Path, Args]). */
+BIF_RETTYPE load_nif_2(BIF_ALIST_2) {
+ if (BIF_P->flags & F_HIPE_MODE) {
+ BIF_RET(load_nif_error(BIF_P, "notsup",
+ "Calling load_nif from HiPE compiled modules "
+ "not supported"));
+ }
+
+ BIF_RET(load_nif_error(BIF_P, "bad_lib",
+ "load_nif/2 must be explicitly called from the NIF "
+ "module. It cannot be called through apply/3."));
+}
-BIF_RETTYPE load_nif_2(BIF_ALIST_2)
+Eterm erts_load_nif(Process *c_p, BeamInstr *I, Eterm filename, Eterm args)
{
static const char bad_lib[] = "bad_lib";
static const char upgrade[] = "upgrade";
@@ -4141,41 +4156,25 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
struct erl_module_instance* this_mi;
struct erl_module_instance* prev_mi;
- if (BIF_P->flags & F_HIPE_MODE) {
- ret = load_nif_error(BIF_P, "notsup", "Calling load_nif from HiPE compiled "
- "modules not supported");
- BIF_RET(ret);
- }
-
encoding = erts_get_native_filename_encoding();
if (encoding == ERL_FILENAME_WIN_WCHAR) {
/* Do not convert the lib name to utf-16le yet, do that in win32 specific code */
/* since lib_name is used in error messages */
encoding = ERL_FILENAME_UTF8;
}
- lib_name = erts_convert_filename_to_encoding(BIF_ARG_1, NULL, 0,
+ lib_name = erts_convert_filename_to_encoding(filename, NULL, 0,
ERTS_ALC_T_TMP, 1, 0, encoding,
NULL, 0);
if (!lib_name) {
- BIF_ERROR(BIF_P, BADARG);
- }
-
- if (!erts_try_seize_code_write_permission(BIF_P)) {
- erts_free(ERTS_ALC_T_TMP, lib_name);
- ERTS_BIF_YIELD2(bif_export[BIF_load_nif_2],
- BIF_P, BIF_ARG_1, BIF_ARG_2);
+ return THE_NON_VALUE;
}
/* Block system (is this the right place to do it?) */
- erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ erts_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN);
erts_thr_progress_block();
/* Find calling module */
- ASSERT(BIF_P->current != NULL);
- ASSERT(BIF_P->current->module == am_erlang
- && BIF_P->current->function == am_load_nif
- && BIF_P->current->arity == 2);
- caller = find_function_from_pc(BIF_P->cp);
+ caller = find_function_from_pc(I);
ASSERT(caller != NULL);
mod_atom = caller->module;
ASSERT(is_atom(mod_atom));
@@ -4195,7 +4194,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
this_mi = &module_p->curr;
prev_mi = &module_p->old;
if (in_area(caller, module_p->old.code_hdr, module_p->old.code_length)) {
- ret = load_nif_error(BIF_P, "old_code", "Calling load_nif from old "
+ ret = load_nif_error(c_p, "old_code", "Calling load_nif from old "
"module '%T' not allowed", mod_atom);
goto error;
} else if (module_p->on_load) {
@@ -4209,52 +4208,52 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
}
if (this_mi->nif != NULL) {
- ret = load_nif_error(BIF_P,"reload","NIF library already loaded"
+ ret = load_nif_error(c_p,"reload","NIF library already loaded"
" (reload disallowed since OTP 20).");
}
else if (init_func == NULL &&
(err=erts_sys_ddll_open(lib_name, &handle, &errdesc)) != ERL_DE_NO_ERROR) {
const char slogan[] = "Failed to load NIF library";
if (strstr(errdesc.str, lib_name) != NULL) {
- ret = load_nif_error(BIF_P, "load_failed", "%s: '%s'", slogan, errdesc.str);
+ ret = load_nif_error(c_p, "load_failed", "%s: '%s'", slogan, errdesc.str);
}
else {
- ret = load_nif_error(BIF_P, "load_failed", "%s %s: '%s'", slogan, lib_name, errdesc.str);
+ ret = load_nif_error(c_p, "load_failed", "%s %s: '%s'", slogan, lib_name, errdesc.str);
}
}
else if (init_func == NULL &&
erts_sys_ddll_load_nif_init(handle, &init_func, &errdesc) != ERL_DE_NO_ERROR) {
- ret = load_nif_error(BIF_P, bad_lib, "Failed to find library init"
+ ret = load_nif_error(c_p, bad_lib, "Failed to find library init"
" function: '%s'", errdesc.str);
}
else if ((taint ? erts_add_taint(mod_atom) : 0,
(entry = erts_sys_ddll_call_nif_init(init_func)) == NULL)) {
- ret = load_nif_error(BIF_P, bad_lib, "Library init-call unsuccessful");
+ ret = load_nif_error(c_p, bad_lib, "Library init-call unsuccessful");
}
else if (entry->major > ERL_NIF_MAJOR_VERSION
|| (entry->major == ERL_NIF_MAJOR_VERSION
&& entry->minor > ERL_NIF_MINOR_VERSION)) {
char* fmt = "That '%T' NIF library needs %s or newer. Either try to"
" recompile the NIF lib or use a newer erts runtime.";
- ret = load_nif_error(BIF_P, bad_lib, fmt, mod_atom, entry->min_erts);
+ ret = load_nif_error(c_p, bad_lib, fmt, mod_atom, entry->min_erts);
}
else if (entry->major < ERL_NIF_MIN_REQUIRED_MAJOR_VERSION_ON_LOAD
|| (entry->major==2 && entry->minor == 5)) { /* experimental maps */
char* fmt = "That old NIF library (%d.%d) is not compatible with this "
"erts runtime (%d.%d). Try recompile the NIF lib.";
- ret = load_nif_error(BIF_P, bad_lib, fmt, entry->major, entry->minor,
+ ret = load_nif_error(c_p, bad_lib, fmt, entry->major, entry->minor,
ERL_NIF_MAJOR_VERSION, ERL_NIF_MINOR_VERSION);
}
else if (AT_LEAST_VERSION(entry, 2, 1)
&& sys_strcmp(entry->vm_variant, ERL_NIF_VM_VARIANT) != 0) {
- ret = load_nif_error(BIF_P, bad_lib, "Library (%s) not compiled for "
+ ret = load_nif_error(c_p, bad_lib, "Library (%s) not compiled for "
"this vm variant (%s).",
entry->vm_variant, ERL_NIF_VM_VARIANT);
}
else if (!erts_is_atom_str((char*)entry->name, mod_atom, 1)) {
- ret = load_nif_error(BIF_P, bad_lib, "Library module name '%s' does not"
+ ret = load_nif_error(c_p, bad_lib, "Library module name '%s' does not"
" match calling module '%T'", entry->name, mod_atom);
}
else {
@@ -4273,7 +4272,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
if (!erts_atom_get(f->name, sys_strlen(f->name), &f_atom, ERTS_ATOM_ENC_LATIN1)
|| (ci_pp = get_func_pp(this_mi->code_hdr, f_atom, f->arity))==NULL) {
- ret = load_nif_error(BIF_P,bad_lib,"Function not found %T:%s/%u",
+ ret = load_nif_error(c_p,bad_lib,"Function not found %T:%s/%u",
mod_atom, f->name, f->arity);
}
else if (f->flags) {
@@ -4285,16 +4284,13 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
* a load error.
*/
if (f->flags != ERL_NIF_DIRTY_JOB_IO_BOUND && f->flags != ERL_NIF_DIRTY_JOB_CPU_BOUND)
- ret = load_nif_error(BIF_P, bad_lib, "Illegal flags field value %d for NIF %T:%s/%u",
+ ret = load_nif_error(c_p, bad_lib, "Illegal flags field value %d for NIF %T:%s/%u",
f->flags, mod_atom, f->name, f->arity);
}
- else if (erts_codeinfo_to_code(ci_pp[1]) - erts_codeinfo_to_code(ci_pp[0])
- < BEAM_NIF_MIN_FUNC_SZ)
- {
- ret = load_nif_error(BIF_P,bad_lib,"No explicit call to load_nif"
- " in module (%T:%s/%u too small)",
- mod_atom, f->name, f->arity);
- }
+
+ ASSERT(erts_codeinfo_to_code(ci_pp[1]) - erts_codeinfo_to_code(ci_pp[0])
+ >= BEAM_NATIVE_MIN_FUNC_SZ);
+
/*erts_fprintf(stderr, "Found NIF %T:%s/%u\r\n",
mod_atom, f->name, f->arity);*/
}
@@ -4313,23 +4309,23 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
if (prev_mi->nif != NULL) { /**************** Upgrade ***************/
void* prev_old_data = prev_mi->nif->priv_data;
if (entry->upgrade == NULL) {
- ret = load_nif_error(BIF_P, upgrade, "Upgrade not supported by this NIF library.");
+ ret = load_nif_error(c_p, upgrade, "Upgrade not supported by this NIF library.");
goto error;
}
- erts_pre_nif(&env, BIF_P, lib, NULL);
- veto = entry->upgrade(&env, &lib->priv_data, &prev_mi->nif->priv_data, BIF_ARG_2);
+ erts_pre_nif(&env, c_p, lib, NULL);
+ veto = entry->upgrade(&env, &lib->priv_data, &prev_mi->nif->priv_data, args);
erts_post_nif(&env);
if (veto) {
prev_mi->nif->priv_data = prev_old_data;
- ret = load_nif_error(BIF_P, upgrade, "Library upgrade-call unsuccessful (%d).", veto);
+ ret = load_nif_error(c_p, upgrade, "Library upgrade-call unsuccessful (%d).", veto);
}
}
else if (entry->load != NULL) { /********* Initial load ***********/
- erts_pre_nif(&env, BIF_P, lib, NULL);
- veto = entry->load(&env, &lib->priv_data, BIF_ARG_2);
+ erts_pre_nif(&env, c_p, lib, NULL);
+ veto = entry->load(&env, &lib->priv_data, args);
erts_post_nif(&env);
if (veto) {
- ret = load_nif_error(BIF_P, "load", "Library load-call unsuccessful (%d).", veto);
+ ret = load_nif_error(c_p, "load", "Library load-call unsuccessful (%d).", veto);
}
}
if (ret == am_ok) {
@@ -4351,12 +4347,12 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
code_ptr = erts_codeinfo_to_code(ci);
if (ci->u.gen_bp == NULL) {
- code_ptr[0] = BeamOpCodeAddr(op_call_nif);
+ code_ptr[0] = BeamOpCodeAddr(op_call_nif_WWW);
}
else { /* Function traced, patch the original instruction word */
GenericBp* g = ci->u.gen_bp;
ASSERT(BeamIsOpCode(code_ptr[0], op_i_generic_breakpoint));
- g->orig_instr = BeamOpCodeAddr(op_call_nif);
+ g->orig_instr = BeamOpCodeAddr(op_call_nif_WWW);
}
if (f->flags) {
code_ptr[3] = (BeamInstr) f->fptr;
@@ -4383,8 +4379,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2)
}
erts_thr_progress_unblock();
- erts_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
- erts_release_code_write_permission();
+ erts_proc_lock(c_p, ERTS_PROC_LOCK_MAIN);
erts_free(ERTS_ALC_T_TMP, lib_name);
BIF_RET(ret);
diff --git a/erts/emulator/beam/erl_node_tables.c b/erts/emulator/beam/erl_node_tables.c
index 11df871763..ae3193be81 100644
--- a/erts/emulator/beam/erl_node_tables.c
+++ b/erts/emulator/beam/erl_node_tables.c
@@ -932,7 +932,8 @@ static void try_delete_node(void *venp)
*
* If refc > 0, the entry is in use. Keep the entry.
*/
- erts_node_bookkeep(enp, THE_NON_VALUE, ERL_NODE_DEC);
+ erts_node_bookkeep(enp, THE_NON_VALUE, ERL_NODE_DEC,
+ __FILE__, __LINE__);
refc = erts_refc_dectest(&enp->refc, -1);
if (refc == -1)
(void) hash_erase(&erts_node_table, (void *) enp);
@@ -1164,6 +1165,12 @@ void erts_lcnt_update_distribution_locks(int enable) {
* can damage the real-time properties of the system. *
\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+#ifdef ERL_NODE_BOOKKEEP
+#define ERTS_DBG_NC_ALLOC_TYPE ERTS_ALC_T_NC_STD
+#else
+#define ERTS_DBG_NC_ALLOC_TYPE ERTS_ALC_T_NC_TMP
+#endif
+
#include "erl_db.h"
#undef INIT_AM
@@ -1188,10 +1195,12 @@ static Eterm AM_delayed_delete_timer;
static Eterm AM_thread_progress_delete_timer;
static Eterm AM_sequence;
static Eterm AM_signal;
+static Eterm AM_persistent_term;
static void setup_reference_table(void);
static Eterm reference_table_term(Uint **hpp, ErlOffHeap *ohp, Uint *szp);
static void delete_reference_table(void);
+static void clear_system(void);
#undef ERTS_MAX__
#define ERTS_MAX__(A, B) ((A) > (B) ? (A) : (B))
@@ -1244,11 +1253,11 @@ typedef struct inserted_bin_ {
Binary *bin_val;
} InsertedBin;
-static ReferredNode *referred_nodes;
+static ReferredNode *referred_nodes = NULL;
static int no_referred_nodes;
-static ReferredDist *referred_dists;
+static ReferredDist *referred_dists = NULL;
static int no_referred_dists;
-static InsertedBin *inserted_bins;
+static InsertedBin *inserted_bins = NULL;
Eterm
erts_get_node_and_dist_references(struct process *proc)
@@ -1284,9 +1293,15 @@ erts_get_node_and_dist_references(struct process *proc)
INIT_AM(thread_progress_delete_timer);
INIT_AM(signal);
INIT_AM(sequence);
+ INIT_AM(persistent_term);
references_atoms_need_init = 0;
}
+#ifdef ERL_NODE_BOOKKEEP
+ if (referred_nodes || referred_dists || inserted_bins)
+ delete_reference_table();
+#endif
+
setup_reference_table();
/* Get term size */
@@ -1304,7 +1319,11 @@ erts_get_node_and_dist_references(struct process *proc)
ASSERT(endp == hp);
+#ifndef ERL_NODE_BOOKKEEP
delete_reference_table();
+#endif
+
+ clear_system();
erts_thr_progress_unblock();
erts_proc_lock(proc, ERTS_PROC_LOCK_MAIN);
@@ -1339,7 +1358,7 @@ insert_dist_referrer(ReferredDist *referred_dist,
break;
if(!drp) {
- drp = (DistReferrer *) erts_alloc(ERTS_ALC_T_NC_TMP,
+ drp = (DistReferrer *) erts_alloc(ERTS_DBG_NC_ALLOC_TYPE,
sizeof(DistReferrer));
drp->next = referred_dist->referrers;
referred_dist->referrers = drp;
@@ -1402,7 +1421,7 @@ insert_node_referrer(ReferredNode *referred_node, int type, Eterm id)
break;
if(!nrp) {
- nrp = (NodeReferrer *) erts_alloc(ERTS_ALC_T_NC_TMP,
+ nrp = (NodeReferrer *) erts_alloc(ERTS_DBG_NC_ALLOC_TYPE,
sizeof(NodeReferrer));
nrp->next = referred_node->referrers;
ERTS_INIT_OFF_HEAP(&nrp->off_heap);
@@ -1490,10 +1509,12 @@ static void
insert_offheap(ErlOffHeap *oh, int type, Eterm id)
{
union erl_off_heap_ptr u;
+ struct erts_tmp_aligned_offheap tmp;
struct insert_offheap2_arg a;
a.type = BIN_REF;
for (u.hdr = oh->first; u.hdr; u.hdr = u.hdr->next) {
+ erts_align_offheap(&u, &tmp);
switch (thing_subtag(u.hdr->thing_word)) {
case REF_SUBTAG:
if (ErtsIsDistEntryBinary(u.mref->mb))
@@ -1516,7 +1537,8 @@ insert_offheap(ErlOffHeap *oh, int type, Eterm id)
erts_match_prog_foreach_offheap((Binary *) u.mref->mb,
insert_offheap2,
(void *) &a);
- nib = erts_alloc(ERTS_ALC_T_NC_TMP, sizeof(InsertedBin));
+ nib = erts_alloc(ERTS_DBG_NC_ALLOC_TYPE,
+ sizeof(InsertedBin));
nib->bin_val = (Binary *) u.mref->mb;
nib->next = inserted_bins;
inserted_bins = nib;
@@ -1574,18 +1596,6 @@ static int clear_visited_monitor(ErtsMonitor *mon, void *p, Sint reds)
}
static void
-insert_p_monitors(ErtsPTabElementCommon *p)
-{
- Eterm id = p->id;
- erts_monitor_tree_foreach(p->u.alive.monitors,
- insert_monitor,
- (void *) &id);
- erts_monitor_list_foreach(p->u.alive.lt_monitors,
- insert_monitor,
- (void *) &id);
-}
-
-static void
insert_dist_monitors(DistEntry *dep)
{
if (dep->mld) {
@@ -1600,8 +1610,10 @@ insert_dist_monitors(DistEntry *dep)
static int
-insert_sequence(ErtsDistExternal *edep, void *arg, Sint reds)
+insert_sequence(DistSeqNode *seq, void *arg, Sint reds)
{
+ ErtsDistExternal *edep = erts_get_dist_ext(&seq->hfrag);
+ insert_offheap(&seq->hfrag.off_heap, SEQUENCE_REF, *(Eterm*)arg);
insert_dist_entry(edep->dep, SEQUENCE_REF, *(Eterm*)arg, 0);
return 1;
}
@@ -1609,18 +1621,7 @@ insert_sequence(ErtsDistExternal *edep, void *arg, Sint reds)
static void
insert_dist_sequences(DistEntry *dep)
{
- erts_dist_seq_tree_foreach(dep, insert_sequence, (void *) &dep->sysname);
-}
-
-static void
-clear_visited_p_monitors(ErtsPTabElementCommon *p)
-{
- erts_monitor_tree_foreach(p->u.alive.monitors,
- clear_visited_monitor,
- NULL);
- erts_monitor_list_foreach(p->u.alive.lt_monitors,
- clear_visited_monitor,
- NULL);
+ erts_debug_dist_seq_tree_foreach(dep, insert_sequence, (void *) &dep->sysname);
}
static void
@@ -1667,13 +1668,6 @@ static int clear_visited_link(ErtsLink *lnk, void *p, Sint reds)
}
static void
-insert_p_links(ErtsPTabElementCommon *p)
-{
- Eterm id = p->id;
- erts_link_tree_foreach(p->u.alive.links, insert_link, (void *) &id);
-}
-
-static void
insert_dist_links(DistEntry *dep)
{
if (dep->mld)
@@ -1683,14 +1677,6 @@ insert_dist_links(DistEntry *dep)
}
static void
-clear_visited_p_links(ErtsPTabElementCommon *p)
-{
- erts_link_tree_foreach(p->u.alive.links,
- clear_visited_link,
- NULL);
-}
-
-static void
clear_visited_dist_links(DistEntry *dep)
{
if (dep->mld)
@@ -1885,15 +1871,11 @@ insert_process(Process *proc)
insert_sig_ext,
(void *) proc);
- /* If the process is FREE, the proc->common field has been
- re-used by the ptab delete, so we cannot trust it. */
- if (!(erts_atomic32_read_nob(&proc->state) & ERTS_PSFLG_FREE)) {
- /* Insert links */
- insert_p_links(&proc->common);
-
- /* Insert monitors */
- insert_p_monitors(&proc->common);
- }
+ /* Insert monitors and links... */
+ erts_debug_proc_monitor_link_foreach(proc,
+ insert_monitor,
+ insert_link,
+ (void *) &proc->common.id);
{
DistEntry *dep = ERTS_PROC_GET_DIST_ENTRY(proc);
@@ -1906,6 +1888,12 @@ insert_process(Process *proc)
}
static void
+insert_process2(Process *proc, void *arg)
+{
+ insert_process(proc);
+}
+
+static void
insert_dist_suspended_procs(DistEntry *dep)
{
ErtsProcList *plist = erts_proclist_peek_first(dep->suspended);
@@ -1916,16 +1904,47 @@ insert_dist_suspended_procs(DistEntry *dep)
}
}
+static void clear_process(Process *proc);
+
+static void
+clear_dist_suspended_procs(DistEntry *dep)
+{
+ ErtsProcList *plist = erts_proclist_peek_first(dep->suspended);
+ while (plist) {
+ if (is_not_immed(plist->u.pid))
+ clear_process(plist->u.p);
+ plist = erts_proclist_peek_next(dep->suspended, plist);
+ }
+}
+
+static void
+insert_persistent_term(ErlOffHeap *ohp, void *arg)
+{
+ Eterm heap[3];
+ insert_offheap(ohp, SYSTEM_REF,
+ TUPLE2(&heap[0], AM_system, AM_persistent_term));
+}
+
+static void
+insert_ets_offheap_thr_prgr(ErlOffHeap *ohp, void *arg)
+{
+ Eterm heap[3];
+ insert_offheap(ohp, ETS_REF,
+ TUPLE2(&heap[0], AM_system, AM_ets));
+}
+
#ifdef ERL_NODE_BOOKKEEP
void
-erts_node_bookkeep(ErlNode *np, Eterm term, int what)
+erts_node_bookkeep(ErlNode *np, Eterm term, int what, char *f, int l)
{
- erts_aint_t slot = (erts_atomic_inc_read_nob(&np->slot) - 1) % 1024;
+ erts_aint_t slot = (erts_atomic_inc_read_nob(&np->slot) - 1) % ERTS_BOOKKEEP_SIZE;
ErtsSchedulerData *esdp = erts_get_scheduler_data();
Eterm who = THE_NON_VALUE;
ASSERT(np);
np->books[slot].what = what;
np->books[slot].term = term;
+ np->books[slot].file = f;
+ np->books[slot].line = l;
if (esdp->current_process) {
who = esdp->current_process->common.id;
} else if (esdp->current_port) {
@@ -1946,14 +1965,14 @@ setup_reference_table(void)
inserted_bins = NULL;
hash_get_info(&hi, &erts_node_table);
- referred_nodes = erts_alloc(ERTS_ALC_T_NC_TMP,
+ referred_nodes = erts_alloc(ERTS_DBG_NC_ALLOC_TYPE,
hi.objs*sizeof(ReferredNode));
no_referred_nodes = 0;
hash_foreach(&erts_node_table, init_referred_node, NULL);
ASSERT(no_referred_nodes == hi.objs);
hash_get_info(&hi, &erts_dist_table);
- referred_dists = erts_alloc(ERTS_ALC_T_NC_TMP,
+ referred_dists = erts_alloc(ERTS_DBG_NC_ALLOC_TYPE,
hi.objs*sizeof(ReferredDist));
no_referred_dists = 0;
hash_foreach(&erts_dist_table, init_referred_dist, NULL);
@@ -1990,8 +2009,9 @@ setup_reference_table(void)
if (proc)
insert_process(proc);
}
+ erts_debug_free_process_foreach(insert_process2, NULL);
- erts_foreach_sys_msg_in_q(insert_sys_msg);
+ erts_debug_foreach_sys_msg_in_q(insert_sys_msg);
/* Insert all ports */
max = erts_ptab_max(&erts_port);
@@ -2008,10 +2028,18 @@ setup_reference_table(void)
if (state & ERTS_PORT_SFLGS_DEAD)
continue;
- /* Insert links */
- insert_p_links(&prt->common);
- /* Insert monitors */
- insert_p_monitors(&prt->common);
+ /* Insert links */
+ erts_link_tree_foreach(ERTS_P_LINKS(prt),
+ insert_link,
+ (void *) &prt->common.id);
+ /* Insert monitors */
+ erts_monitor_tree_foreach(ERTS_P_MONITORS(prt),
+ insert_monitor,
+ (void *) &prt->common.id);
+ /* Insert local target monitors */
+ erts_monitor_list_foreach(ERTS_P_LT_MONITORS(prt),
+ insert_monitor,
+ (void *) &prt->common.id);
/* Insert port data */
ohp = erts_port_data_offheap(prt);
if (ohp)
@@ -2091,11 +2119,16 @@ setup_reference_table(void)
}
/* Insert all ets tables */
- erts_db_foreach_table(insert_ets_table, NULL);
+ erts_db_foreach_table(insert_ets_table, NULL, 0);
+ erts_db_foreach_thr_prgr_offheap(insert_ets_offheap_thr_prgr, NULL);
/* Insert all bif timers */
erts_debug_bif_timer_foreach(insert_bif_timer, NULL);
+ /* Insert persistent term storage */
+ erts_debug_foreach_persistent_term_off_heap(insert_persistent_term,
+ NULL);
+
/* Insert node table (references to dist) */
hash_foreach(&erts_node_table, insert_erl_node, NULL);
}
@@ -2348,8 +2381,7 @@ static void noop_sig_ext(ErtsDistExternal *ext, void *arg)
static void
delete_reference_table(void)
{
- DistEntry *dep;
- int i, max;
+ int i;
for(i = 0; i < no_referred_nodes; i++) {
NodeReferrer *nrp;
NodeReferrer *tnrp;
@@ -2358,11 +2390,13 @@ delete_reference_table(void)
erts_cleanup_offheap(&nrp->off_heap);
tnrp = nrp;
nrp = nrp->next;
- erts_free(ERTS_ALC_T_NC_TMP, (void *) tnrp);
+ erts_free(ERTS_DBG_NC_ALLOC_TYPE, (void *) tnrp);
}
}
- if (referred_nodes)
- erts_free(ERTS_ALC_T_NC_TMP, (void *) referred_nodes);
+ if (referred_nodes) {
+ erts_free(ERTS_DBG_NC_ALLOC_TYPE, (void *) referred_nodes);
+ referred_nodes = NULL;
+ }
for(i = 0; i < no_referred_dists; i++) {
DistReferrer *drp;
@@ -2371,34 +2405,57 @@ delete_reference_table(void)
while(drp) {
tdrp = drp;
drp = drp->next;
- erts_free(ERTS_ALC_T_NC_TMP, (void *) tdrp);
+ erts_free(ERTS_DBG_NC_ALLOC_TYPE, (void *) tdrp);
}
}
- if (referred_dists)
- erts_free(ERTS_ALC_T_NC_TMP, (void *) referred_dists);
+ if (referred_dists) {
+ erts_free(ERTS_DBG_NC_ALLOC_TYPE, (void *) referred_dists);
+ referred_dists = NULL;
+ }
while(inserted_bins) {
InsertedBin *ib = inserted_bins;
inserted_bins = inserted_bins->next;
- erts_free(ERTS_ALC_T_NC_TMP, (void *)ib);
+ erts_free(ERTS_DBG_NC_ALLOC_TYPE, (void *)ib);
}
+}
+
+static void clear_process(Process *proc)
+{
+ erts_proc_sig_debug_foreach_sig(proc,
+ noop_sig_msg,
+ noop_sig_offheap,
+ clear_visited_monitor,
+ clear_visited_link,
+ noop_sig_ext,
+ (void *) proc);
+
+
+ /* Clear monitors and links... */
+ erts_debug_proc_monitor_link_foreach(proc,
+ clear_visited_monitor,
+ clear_visited_link,
+ (void *) &proc->common.id);
+}
+
+static void clear_process2(Process *proc, void *arg)
+{
+ clear_process(proc);
+}
- /* Cleanup... */
+static void
+clear_system(void)
+{
+ DistEntry *dep;
+ int i, max;
+ /* Clear... */
max = erts_ptab_max(&erts_proc);
for (i = 0; i < max; i++) {
Process *proc = erts_pix2proc(i);
- if (proc) {
- clear_visited_p_links(&proc->common);
- clear_visited_p_monitors(&proc->common);
- erts_proc_sig_debug_foreach_sig(proc,
- noop_sig_msg,
- noop_sig_offheap,
- clear_visited_monitor,
- clear_visited_link,
- noop_sig_ext,
- (void *) proc);
- }
+ if (proc)
+ clear_process(proc);
}
+ erts_debug_free_process_foreach(clear_process2, NULL);
max = erts_ptab_max(&erts_port);
for (i = 0; i < max; i++) {
@@ -2413,28 +2470,42 @@ delete_reference_table(void)
if (state & ERTS_PORT_SFLGS_DEAD)
continue;
- clear_visited_p_links(&prt->common);
- clear_visited_p_monitors(&prt->common);
+ /* Clear links */
+ erts_link_tree_foreach(ERTS_P_LINKS(prt),
+ clear_visited_link,
+ (void *) &prt->common.id);
+ /* Clear monitors */
+ erts_monitor_tree_foreach(ERTS_P_MONITORS(prt),
+ clear_visited_monitor,
+ (void *) &prt->common.id);
+ /* Clear local target monitors */
+ erts_monitor_list_foreach(ERTS_P_LT_MONITORS(prt),
+ clear_visited_monitor,
+ (void *) &prt->common.id);
}
for(dep = erts_visible_dist_entries; dep; dep = dep->next) {
clear_visited_dist_links(dep);
clear_visited_dist_monitors(dep);
+ clear_dist_suspended_procs(dep);
}
for(dep = erts_hidden_dist_entries; dep; dep = dep->next) {
clear_visited_dist_links(dep);
clear_visited_dist_monitors(dep);
+ clear_dist_suspended_procs(dep);
}
for(dep = erts_pending_dist_entries; dep; dep = dep->next) {
clear_visited_dist_links(dep);
clear_visited_dist_monitors(dep);
+ clear_dist_suspended_procs(dep);
}
for(dep = erts_not_connected_dist_entries; dep; dep = dep->next) {
clear_visited_dist_links(dep);
clear_visited_dist_monitors(dep);
+ clear_dist_suspended_procs(dep);
}
}
diff --git a/erts/emulator/beam/erl_node_tables.h b/erts/emulator/beam/erl_node_tables.h
index fc3e117463..beae2df75f 100644
--- a/erts/emulator/beam/erl_node_tables.h
+++ b/erts/emulator/beam/erl_node_tables.h
@@ -187,6 +187,7 @@ set pagination off
set $i = 0
set $node = referred_nodes[$node_ix].node
while $i < $node->slot.counter
+ printf "%s:%d ", $node->books[$i].file, $node->books[$i].line
printf "%p: ", $node->books[$i].term
etp-1 $node->books[$i].who
printf " "
@@ -211,8 +212,12 @@ lists:usort(lists:filter(fun({V,N}) -> N /= 0 end, maps:to_list(Accs))).
struct erl_node_bookkeeping {
Eterm who;
Eterm term;
+ char *file;
+ int line;
enum { ERL_NODE_INC, ERL_NODE_DEC } what;
};
+
+#define ERTS_BOOKKEEP_SIZE (1024)
#endif
typedef struct erl_node_ {
@@ -222,7 +227,7 @@ typedef struct erl_node_ {
Uint32 creation; /* Creation */
DistEntry *dist_entry; /* Corresponding dist entry */
#ifdef ERL_NODE_BOOKKEEP
- struct erl_node_bookkeeping books[1024];
+ struct erl_node_bookkeeping books[ERTS_BOOKKEEP_SIZE];
erts_atomic_t slot;
#endif
} ErlNode;
@@ -276,14 +281,21 @@ Eterm erts_build_dhandle(Eterm **hpp, ErlOffHeap*, DistEntry*, Uint32 conn_id);
Eterm erts_make_dhandle(Process *c_p, DistEntry*, Uint32 conn_id);
ERTS_GLB_INLINE void erts_init_node_entry(ErlNode *np, erts_aint_t val);
+#ifdef ERL_NODE_BOOKKEEP
+#define erts_ref_node_entry(NP, MIN, T) erts_ref_node_entry__((NP), (MIN), (T), __FILE__, __LINE__)
+#define erts_deref_node_entry(NP, T) erts_deref_node_entry__((NP), (T), __FILE__, __LINE__)
+ERTS_GLB_INLINE erts_aint_t erts_ref_node_entry__(ErlNode *np, int min_val, Eterm term, char *file, int line);
+ERTS_GLB_INLINE void erts_deref_node_entry__(ErlNode *np, Eterm term, char *file, int line);
+#else
ERTS_GLB_INLINE erts_aint_t erts_ref_node_entry(ErlNode *np, int min_val, Eterm term);
ERTS_GLB_INLINE void erts_deref_node_entry(ErlNode *np, Eterm term);
+#endif
ERTS_GLB_INLINE void erts_de_rlock(DistEntry *dep);
ERTS_GLB_INLINE void erts_de_runlock(DistEntry *dep);
ERTS_GLB_INLINE void erts_de_rwlock(DistEntry *dep);
ERTS_GLB_INLINE void erts_de_rwunlock(DistEntry *dep);
#ifdef ERL_NODE_BOOKKEEP
-void erts_node_bookkeep(ErlNode *, Eterm , int);
+void erts_node_bookkeep(ErlNode *, Eterm , int, char *file, int line);
#else
#define erts_node_bookkeep(...)
#endif
@@ -296,21 +308,40 @@ erts_init_node_entry(ErlNode *np, erts_aint_t val)
erts_refc_init(&np->refc, val);
}
+#ifdef ERL_NODE_BOOKKEEP
+
+ERTS_GLB_INLINE erts_aint_t
+erts_ref_node_entry__(ErlNode *np, int min_val, Eterm term, char *file, int line)
+{
+ erts_node_bookkeep(np, term, ERL_NODE_INC, file, line);
+ return erts_refc_inctest(&np->refc, min_val);
+}
+
+ERTS_GLB_INLINE void
+erts_deref_node_entry__(ErlNode *np, Eterm term, char *file, int line)
+{
+ erts_node_bookkeep(np, term, ERL_NODE_DEC, file, line);
+ if (erts_refc_dectest(&np->refc, 0) == 0)
+ erts_schedule_delete_node(np);
+}
+
+#else
+
ERTS_GLB_INLINE erts_aint_t
erts_ref_node_entry(ErlNode *np, int min_val, Eterm term)
{
- erts_node_bookkeep(np, term, ERL_NODE_INC);
return erts_refc_inctest(&np->refc, min_val);
}
ERTS_GLB_INLINE void
erts_deref_node_entry(ErlNode *np, Eterm term)
{
- erts_node_bookkeep(np, term, ERL_NODE_DEC);
if (erts_refc_dectest(&np->refc, 0) == 0)
erts_schedule_delete_node(np);
}
+#endif
+
ERTS_GLB_INLINE void
erts_de_rlock(DistEntry *dep)
{
diff --git a/erts/emulator/beam/erl_printf_term.c b/erts/emulator/beam/erl_printf_term.c
index 2e33a8a782..67c486a0db 100644
--- a/erts/emulator/beam/erl_printf_term.c
+++ b/erts/emulator/beam/erl_printf_term.c
@@ -533,13 +533,34 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount) {
case EXPORT_DEF:
{
Export* ep = *((Export **) (export_val(wobj) + 1));
- Atom* module = atom_tab(atom_val(ep->info.mfa.module));
- Atom* name = atom_tab(atom_val(ep->info.mfa.function));
+ long tdcount;
+ int tres;
PRINT_STRING(res, fn, arg, "fun ");
- PRINT_BUF(res, fn, arg, module->name, module->len);
+
+ /* We pass a temporary 'dcount' and adjust the real one later to ensure
+ * that the fun doesn't get split up between the module and function
+ * name. */
+ tdcount = MAX_ATOM_SZ_LIMIT;
+ tres = print_atom_name(fn, arg, ep->info.mfa.module, &tdcount);
+ if (tres < 0) {
+ res = tres;
+ goto L_done;
+ }
+ *dcount -= (MAX_ATOM_SZ_LIMIT - tdcount);
+ res += tres;
+
PRINT_CHAR(res, fn, arg, ':');
- PRINT_BUF(res, fn, arg, name->name, name->len);
+
+ tdcount = MAX_ATOM_SZ_LIMIT;
+ tres = print_atom_name(fn, arg, ep->info.mfa.function, &tdcount);
+ if (tres < 0) {
+ res = tres;
+ goto L_done;
+ }
+ *dcount -= (MAX_ATOM_SZ_LIMIT - tdcount);
+ res += tres;
+
PRINT_CHAR(res, fn, arg, '/');
PRINT_SWORD(res, fn, arg, 'd', 0, 1,
(ErlPfSWord) ep->info.mfa.arity);
diff --git a/erts/emulator/beam/erl_proc_sig_queue.c b/erts/emulator/beam/erl_proc_sig_queue.c
index 6bbd59e8e3..b60fb64342 100644
--- a/erts/emulator/beam/erl_proc_sig_queue.c
+++ b/erts/emulator/beam/erl_proc_sig_queue.c
@@ -4689,10 +4689,12 @@ erts_proc_sig_debug_foreach_sig(Process *c_p,
case ERTS_SIG_Q_OP_MONITOR_DOWN:
switch (type) {
case ERTS_SIG_Q_TYPE_GEN_EXIT:
- if (ERTS_SIG_IS_GEN_EXIT_EXTERNAL(sig))
- debug_foreach_sig_external(sig, ext_func, arg);
- else
+ if (!ERTS_SIG_IS_GEN_EXIT_EXTERNAL(sig))
debug_foreach_sig_heap_frags(&sig->hfrag, oh_func, arg);
+ else {
+ oh_func(&sig->hfrag.off_heap, arg);
+ debug_foreach_sig_external(sig, ext_func, arg);
+ }
break;
case ERTS_LNK_TYPE_PORT:
case ERTS_LNK_TYPE_PROC:
diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c
index 9e385310a8..b62ec77d65 100644
--- a/erts/emulator/beam/erl_process.c
+++ b/erts/emulator/beam/erl_process.c
@@ -708,10 +708,10 @@ erts_pre_init_process(void)
erts_psd_required_locks[ERTS_PSD_DELAYED_GC_TASK_QS].set_locks
= ERTS_PSD_DELAYED_GC_TASK_QS_SET_LOCKS;
- erts_psd_required_locks[ERTS_PSD_NIF_TRAP_EXPORT].get_locks
- = ERTS_PSD_NIF_TRAP_EXPORT_GET_LOCKS;
- erts_psd_required_locks[ERTS_PSD_NIF_TRAP_EXPORT].set_locks
- = ERTS_PSD_NIF_TRAP_EXPORT_SET_LOCKS;
+ erts_psd_required_locks[ERTS_PSD_NFUNC_TRAP_WRAPPER].get_locks
+ = ERTS_PSD_NFUNC_TRAP_WRAPPER_GET_LOCKS;
+ erts_psd_required_locks[ERTS_PSD_NFUNC_TRAP_WRAPPER].set_locks
+ = ERTS_PSD_NFUNC_TRAP_WRAPPER_SET_LOCKS;
erts_psd_required_locks[ERTS_PSD_ETS_OWNED_TABLES].get_locks
= ERTS_PSD_ETS_OWNED_TABLES_GET_LOCKS;
@@ -6478,8 +6478,8 @@ schedule_out_process(ErtsRunQueue *c_rq, erts_aint32_t state, Process *p,
ASSERT(!(state & (ERTS_PSFLG_DIRTY_IO_PROC
|ERTS_PSFLG_DIRTY_CPU_PROC))
- || (BeamIsOpCode(*p->i, op_call_nif)
- || BeamIsOpCode(*p->i, op_apply_bif)));
+ || (BeamIsOpCode(*p->i, op_call_nif_WWW)
+ || BeamIsOpCode(*p->i, op_call_bif_W)));
a = state;
@@ -11458,7 +11458,7 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
#else
arg_size = size_object_litopt(args, &litarea);
#endif
- heap_need = arg_size;
+ heap_need = arg_size + 1; /* Reserve place for continuation pointer */
p->flags = flags;
@@ -11507,7 +11507,8 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
p->old_hend = p->old_htop = p->old_heap = NULL;
p->high_water = p->heap;
p->gen_gcs = 0;
- p->stop = p->hend = p->heap + sz;
+ p->hend = p->heap + sz;
+ p->stop = p->hend - 1; /* Reserve place for continuation pointer */
p->htop = p->heap;
p->heap_sz = sz;
p->abandoned_heap = NULL;
@@ -11525,7 +11526,7 @@ erl_create_process(Process* parent, /* Parent of process (default group leader).
p->current = &p->u.initial;
p->i = (BeamInstr *) beam_apply;
- p->cp = (BeamInstr *) beam_apply+1;
+ p->stop[0] = make_cp(beam_apply + 1);
p->arg_reg = p->def_arg_reg;
p->max_arg_reg = sizeof(p->def_arg_reg)/sizeof(p->def_arg_reg[0]);
@@ -11839,7 +11840,6 @@ void erts_init_empty_process(Process *p)
p->u.initial.function = 0;
p->u.initial.arity = 0;
p->catches = 0;
- p->cp = NULL;
p->i = NULL;
p->current = NULL;
@@ -11917,7 +11917,6 @@ erts_debug_verify_clean_empty_process(Process* p)
ASSERT(p->bif_timers == NULL);
ASSERT(p->dictionary == NULL);
ASSERT(p->catches == 0);
- ASSERT(p->cp == NULL);
ASSERT(p->i == NULL);
ASSERT(p->current == NULL);
@@ -11977,7 +11976,7 @@ delete_process(Process* p)
if (pbt)
erts_free(ERTS_ALC_T_BPD, (void *) pbt);
- erts_destroy_nif_export(p);
+ erts_destroy_nfunc(p);
/* Cleanup psd */
@@ -12118,6 +12117,8 @@ erts_proc_exit_handle_dist_monitor(ErtsMonitor *mon, void *vctxt, Sint reds)
ASSERT(c_p->flags & F_DISABLE_GC);
ASSERT(erts_monitor_is_target(mon) && mon->type == ERTS_MON_TYPE_DIST_PROC);
+ ASSERT(ctxt->dist_state == NIL);
+ ASSERT(!ctxt->yield);
mdp = erts_monitor_to_data(mon);
@@ -12162,10 +12163,14 @@ erts_proc_exit_handle_dist_monitor(ErtsMonitor *mon, void *vctxt, Sint reds)
reason);
reds_consumed = reds - (ctx.reds / TERM_TO_BINARY_LOOP_FACTOR);
switch (code) {
- case ERTS_DSIG_SEND_CONTINUE:
case ERTS_DSIG_SEND_YIELD:
+ reds_consumed = reds; /* force yield */
+ ctxt->yield = 1;
+ break;
+ case ERTS_DSIG_SEND_CONTINUE:
ctxt->dist_state = erts_dsend_export_trap_context(c_p, &ctx);
reds_consumed = reds; /* force yield */
+ ctxt->yield = 1;
break;
case ERTS_DSIG_SEND_OK:
break;
@@ -12376,6 +12381,9 @@ erts_proc_exit_handle_dist_link(ErtsLink *lnk, void *vctxt, Sint reds)
ASSERT(c_p->flags & F_DISABLE_GC);
ASSERT(lnk->type == ERTS_LNK_TYPE_DIST_PROC);
+ ASSERT(ctxt->dist_state == NIL);
+ ASSERT(!ctxt->yield);
+
dlnk = erts_link_to_other(lnk, &ldp);
dist = ((ErtsLinkDataExtended *) ldp)->dist;
@@ -12413,9 +12421,13 @@ erts_proc_exit_handle_dist_link(ErtsLink *lnk, void *vctxt, Sint reds)
reds_consumed = reds - (ctx.reds / TERM_TO_BINARY_LOOP_FACTOR);
switch (code) {
case ERTS_DSIG_SEND_YIELD:
+ reds_consumed = reds; /* force yield */
+ ctxt->yield = 1;
+ break;
case ERTS_DSIG_SEND_CONTINUE:
ctxt->dist_state = erts_dsend_export_trap_context(c_p, &ctx);
reds_consumed = reds; /* force yield */
+ ctxt->yield = 1;
break;
case ERTS_DSIG_SEND_OK:
break;
@@ -12838,6 +12850,7 @@ restart:
trap_state->pectxt.dist_links = NULL;
trap_state->pectxt.dist_monitors = NULL;
trap_state->pectxt.dist_state = NIL;
+ trap_state->pectxt.yield = 0;
erts_proc_lock(p, ERTS_PROC_LOCK_MSGQ);
@@ -12921,6 +12934,8 @@ restart:
erts_kill_dist_connection(ctx->dep, ctx->connection_id);
break;
case ERTS_DSIG_SEND_YIELD: /*SEND_YIELD_RETURN*/
+ trap_state->pectxt.dist_state = NIL;
+ goto yield;
case ERTS_DSIG_SEND_CONTINUE: { /*SEND_YIELD_CONTINUE*/
goto yield;
}
@@ -12938,7 +12953,7 @@ restart:
(void *) &trap_state->pectxt,
&trap_state->yield_state,
reds);
- if (reds <= 0 || is_not_nil(trap_state->pectxt.dist_state))
+ if (reds <= 0 || trap_state->pectxt.yield)
goto yield;
trap_state->phase = ERTS_CONTINUE_EXIT_DIST_MONITORS;
}
@@ -12953,7 +12968,7 @@ restart:
(void *) &trap_state->pectxt,
&trap_state->yield_state,
reds);
- if (reds <= 0 || is_not_nil(trap_state->pectxt.dist_state))
+ if (reds <= 0 || trap_state->pectxt.yield)
goto yield;
trap_state->phase = ERTS_CONTINUE_EXIT_DONE;
@@ -13038,6 +13053,7 @@ restart:
sys_memcpy(trap_state, &static_state, sizeof(*trap_state));
p->u.terminate = trap_state;
}
+ trap_state->pectxt.yield = 0;
ASSERT(p->scheduler_data);
ASSERT(p->scheduler_data->current_process == p);
@@ -13146,9 +13162,6 @@ erts_program_counter_info(fmtfn_t to, void *to_arg, Process *p)
erts_print(to, to_arg, "Program counter: %p (", p->i);
print_function_from_pc(to, to_arg, p->i);
erts_print(to, to_arg, ")\n");
- erts_print(to, to_arg, "CP: %p (", p->cp);
- print_function_from_pc(to, to_arg, p->cp);
- erts_print(to, to_arg, ")\n");
state = erts_atomic32_read_acqb(&p->state);
if (!(state & (ERTS_PSFLG_RUNNING
| ERTS_PSFLG_RUNNING_SYS
@@ -13425,9 +13438,6 @@ static void print_current_process_info(fmtfn_t to, void *to_arg,
erts_print(to, to_arg, "Current Process Program counter: %p (", p->i);
print_function_from_pc(to, to_arg, p->i);
erts_print(to, to_arg, ")\n");
- erts_print(to, to_arg, "Current Process CP: %p (", p->cp);
- print_function_from_pc(to, to_arg, p->cp);
- erts_print(to, to_arg, ")\n");
/* Getting this stacktrace can segfault if we are very very
unlucky if called while a process is being garbage collected.
@@ -13514,3 +13524,79 @@ erts_debug_later_op_foreach(void (*callback)(void*),
}
}
}
+
+void
+erts_debug_free_process_foreach(void (*func)(Process *, void *), void *arg)
+{
+ ErtsRunQueue *rq;
+ int ix, prio;
+ for (ix = 0; ix < erts_no_run_queues; ix++) {
+ rq = ERTS_RUNQ_IX(ix);
+ for (prio = PRIORITY_MAX; prio < PRIORITY_LOW; prio++) {
+ Process *p = rq->procs.prio[prio].first;
+ for (; p; p = p->next) {
+ if (ERTS_PSFLG_FREE & erts_atomic32_read_nob(&p->state))
+ (*func)(p, arg);
+ }
+ }
+ }
+}
+
+void
+erts_debug_proc_monitor_link_foreach(Process *proc,
+ int (*monitor_func)(ErtsMonitor *, void *, Sint ),
+ int (*link_func)(ErtsLink *, void *, Sint ),
+ void *arg)
+{
+ if (!(erts_atomic32_read_nob(&proc->state) & ERTS_PSFLG_FREE)) {
+ /* For all links */
+ erts_link_tree_foreach(ERTS_P_LINKS(proc),
+ link_func,
+ arg);
+ /* For all monitors */
+ erts_monitor_tree_foreach(ERTS_P_MONITORS(proc),
+ monitor_func,
+ arg);
+ /* For all local target monitors */
+ erts_monitor_list_foreach(ERTS_P_LT_MONITORS(proc),
+ monitor_func,
+ arg);
+ }
+ else {
+ struct continue_exit_state *ce_state = proc->u.terminate;
+
+ /* For all links */
+ if (ce_state->phase == ERTS_CONTINUE_EXIT_LINKS)
+ erts_debug_link_tree_destroying_foreach(ce_state->links,
+ link_func,
+ arg,
+ ce_state->yield_state);
+ else
+ erts_link_tree_foreach(ce_state->links,
+ link_func,
+ arg);
+
+ /* For all monitors */
+ if (ce_state->phase == ERTS_CONTINUE_EXIT_MONITORS)
+ erts_debug_monitor_tree_destroying_foreach(ce_state->monitors,
+ monitor_func,
+ arg,
+ ce_state->yield_state);
+ else
+ erts_monitor_tree_foreach(ce_state->monitors,
+ monitor_func,
+ arg);
+
+ /* For all local target monitors */
+ if (ce_state->phase == ERTS_CONTINUE_EXIT_LT_MONITORS)
+ erts_debug_monitor_list_destroying_foreach(ce_state->lt_monitors,
+ monitor_func,
+ arg,
+ ce_state->yield_state);
+ else
+ erts_monitor_list_foreach(ce_state->lt_monitors,
+ monitor_func,
+ arg);
+
+ }
+}
diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h
index 2e2ab01d86..09a6c0e961 100644
--- a/erts/emulator/beam/erl_process.h
+++ b/erts/emulator/beam/erl_process.h
@@ -701,6 +701,13 @@ void
erts_debug_later_op_foreach(void (*callback)(void*),
void (*func)(void *, ErtsThrPrgrVal, void *),
void *arg);
+void
+erts_debug_free_process_foreach(void (*func)(Process *, void *), void *arg);
+void
+erts_debug_proc_monitor_link_foreach(Process *proc,
+ int (*monitor_func)(ErtsMonitor *, void *, Sint ),
+ int (*link_func)(ErtsLink *, void *, Sint ),
+ void *arg);
#ifdef ERTS_INCLUDE_SCHEDULER_INTERNALS
@@ -805,7 +812,7 @@ erts_reset_max_len(ErtsRunQueue *rq, ErtsRunQueueInfo *rqi)
#define ERTS_PSD_SCHED_ID 2
#define ERTS_PSD_CALL_TIME_BP 3
#define ERTS_PSD_DELAYED_GC_TASK_QS 4
-#define ERTS_PSD_NIF_TRAP_EXPORT 5
+#define ERTS_PSD_NFUNC_TRAP_WRAPPER 5
#define ERTS_PSD_ETS_OWNED_TABLES 6
#define ERTS_PSD_ETS_FIXED_TABLES 7
#define ERTS_PSD_DIST_ENTRY 8
@@ -842,8 +849,8 @@ typedef struct {
#define ERTS_PSD_DELAYED_GC_TASK_QS_GET_LOCKS ERTS_PROC_LOCK_MAIN
#define ERTS_PSD_DELAYED_GC_TASK_QS_SET_LOCKS ERTS_PROC_LOCK_MAIN
-#define ERTS_PSD_NIF_TRAP_EXPORT_GET_LOCKS ERTS_PROC_LOCK_MAIN
-#define ERTS_PSD_NIF_TRAP_EXPORT_SET_LOCKS ERTS_PROC_LOCK_MAIN
+#define ERTS_PSD_NFUNC_TRAP_WRAPPER_GET_LOCKS ERTS_PROC_LOCK_MAIN
+#define ERTS_PSD_NFUNC_TRAP_WRAPPER_SET_LOCKS ERTS_PROC_LOCK_MAIN
#define ERTS_PSD_ETS_OWNED_TABLES_GET_LOCKS ERTS_PROC_LOCK_STATUS
#define ERTS_PSD_ETS_OWNED_TABLES_SET_LOCKS ERTS_PROC_LOCK_STATUS
@@ -968,7 +975,6 @@ struct process {
unsigned max_arg_reg; /* Maximum number of argument registers available. */
Eterm def_arg_reg[6]; /* Default array for argument registers. */
- BeamInstr* cp; /* (untagged) Continuation pointer (for threaded code). */
BeamInstr* i; /* Program counter for threaded code. */
Sint catches; /* Number of catches on stack */
Sint fcalls; /*
@@ -1364,7 +1370,7 @@ extern int erts_system_profile_ts_type;
#define F_DISTRIBUTION (1 << 6) /* Process used in distribution */
#define F_USING_DDLL (1 << 7) /* Process has used the DDLL interface */
#define F_HAVE_BLCKD_MSCHED (1 << 8) /* Process has blocked multi-scheduling */
-#define F_UNUSED (1 << 9)
+#define F_ETS_SUPER_USER (1 << 9) /* Process is ETS super user */
#define F_FORCE_GC (1 << 10) /* Force gc at process in-scheduling */
#define F_DISABLE_GC (1 << 11) /* Disable GC (see below) */
#define F_OFF_HEAP_MSGQ (1 << 12) /* Off heap msg queue */
@@ -1826,6 +1832,7 @@ typedef struct {
ErtsLink *dist_links;
ErtsMonitor *dist_monitors;
Eterm dist_state;
+ int yield;
} ErtsProcExitContext;
int erts_proc_exit_handle_monitor(ErtsMonitor *mon, void *vctxt, Sint reds);
int erts_proc_exit_handle_link(ErtsLink *lnk, void *vctxt, Sint reds);
@@ -2031,10 +2038,10 @@ erts_psd_set(Process *p, int ix, void *data)
#define ERTS_PROC_SET_DELAYED_GC_TASK_QS(P, PBT) \
((ErtsProcSysTaskQs *) erts_psd_set((P), ERTS_PSD_DELAYED_GC_TASK_QS, (void *) (PBT)))
-#define ERTS_PROC_GET_NIF_TRAP_EXPORT(P) \
- erts_psd_get((P), ERTS_PSD_NIF_TRAP_EXPORT)
-#define ERTS_PROC_SET_NIF_TRAP_EXPORT(P, NTE) \
- erts_psd_set((P), ERTS_PSD_NIF_TRAP_EXPORT, (void *) (NTE))
+#define ERTS_PROC_GET_NFUNC_TRAP_WRAPPER(P) \
+ erts_psd_get((P), ERTS_PSD_NFUNC_TRAP_WRAPPER)
+#define ERTS_PROC_SET_NFUNC_TRAP_WRAPPER(P, NTE) \
+ erts_psd_set((P), ERTS_PSD_NFUNC_TRAP_WRAPPER, (void *) (NTE))
#define ERTS_PROC_GET_DIST_ENTRY(P) \
((DistEntry *) erts_psd_get((P), ERTS_PSD_DIST_ENTRY))
diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c
index ffe0752b46..5c46a10d64 100644
--- a/erts/emulator/beam/erl_trace.c
+++ b/erts/emulator/beam/erl_trace.c
@@ -2195,11 +2195,12 @@ sys_msg_dispatcher_wait(void *vwait_p)
erts_mtx_unlock(&smq_mtx);
}
+static ErtsSysMsgQ *local_sys_message_queue = NULL;
+
static void *
sys_msg_dispatcher_func(void *unused)
{
ErtsThrPrgrCallbacks callbacks;
- ErtsSysMsgQ *local_sys_message_queue = NULL;
ErtsThrPrgrData *tpd;
int wait = 0;
@@ -2207,6 +2208,8 @@ sys_msg_dispatcher_func(void *unused)
erts_lc_set_thread_name("system message dispatcher");
#endif
+ local_sys_message_queue = NULL;
+
callbacks.arg = (void *) &wait;
callbacks.wakeup = sys_msg_dispatcher_wakeup;
callbacks.prepare_wait = sys_msg_dispatcher_prep_wait;
@@ -2263,6 +2266,8 @@ sys_msg_dispatcher_func(void *unused)
Process *proc = NULL;
Port *port = NULL;
+ ASSERT(is_value(smqp->msg));
+
if (erts_thr_progress_update(tpd))
erts_thr_progress_leader_update(tpd);
@@ -2375,6 +2380,7 @@ sys_msg_dispatcher_func(void *unused)
erts_fprintf(stderr, "dropped\n");
#endif
}
+ smqp->msg = THE_NON_VALUE;
}
}
@@ -2382,32 +2388,38 @@ sys_msg_dispatcher_func(void *unused)
}
void
-erts_foreach_sys_msg_in_q(void (*func)(Eterm,
- Eterm,
- Eterm,
- ErlHeapFragment *))
+erts_debug_foreach_sys_msg_in_q(void (*func)(Eterm,
+ Eterm,
+ Eterm,
+ ErlHeapFragment *))
{
- ErtsSysMsgQ *sm;
- erts_mtx_lock(&smq_mtx);
- for (sm = sys_message_queue; sm; sm = sm->next) {
- Eterm to;
- switch (sm->type) {
- case SYS_MSG_TYPE_SYSMON:
- to = erts_get_system_monitor();
- break;
- case SYS_MSG_TYPE_SYSPROF:
- to = erts_get_system_profile();
- break;
- case SYS_MSG_TYPE_ERRLGR:
- to = erts_get_system_logger();
- break;
- default:
- to = NIL;
- break;
- }
- (*func)(sm->from, to, sm->msg, sm->bp);
+ ErtsSysMsgQ *smq[] = {sys_message_queue, local_sys_message_queue};
+ int i;
+
+ ERTS_LC_ASSERT(erts_thr_progress_is_blocking());
+
+ for (i = 0; i < sizeof(smq)/sizeof(smq[0]); i++) {
+ ErtsSysMsgQ *sm;
+ for (sm = smq[i]; sm; sm = sm->next) {
+ Eterm to;
+ switch (sm->type) {
+ case SYS_MSG_TYPE_SYSMON:
+ to = erts_get_system_monitor();
+ break;
+ case SYS_MSG_TYPE_SYSPROF:
+ to = erts_get_system_profile();
+ break;
+ case SYS_MSG_TYPE_ERRLGR:
+ to = erts_get_system_logger();
+ break;
+ default:
+ to = NIL;
+ break;
+ }
+ if (is_value(sm->msg))
+ (*func)(sm->from, to, sm->msg, sm->bp);
+ }
}
- erts_mtx_unlock(&smq_mtx);
}
diff --git a/erts/emulator/beam/erl_trace.h b/erts/emulator/beam/erl_trace.h
index bb5c9ac276..c0f31e0cb6 100644
--- a/erts/emulator/beam/erl_trace.h
+++ b/erts/emulator/beam/erl_trace.h
@@ -90,10 +90,10 @@ int erts_is_tracer_valid(Process* p);
void erts_check_my_tracer_proc(Process *);
void erts_block_sys_msg_dispatcher(void);
void erts_release_sys_msg_dispatcher(void);
-void erts_foreach_sys_msg_in_q(void (*func)(Eterm,
- Eterm,
- Eterm,
- ErlHeapFragment *));
+void erts_debug_foreach_sys_msg_in_q(void (*func)(Eterm,
+ Eterm,
+ Eterm,
+ ErlHeapFragment *));
Eterm erts_set_system_logger(Eterm);
Eterm erts_get_system_logger(void);
void erts_queue_error_logger_message(Eterm, Eterm, ErlHeapFragment *);
@@ -142,12 +142,6 @@ void monitor_generic(Process *p, Eterm type, Eterm spec);
Uint erts_trace_flag2bit(Eterm flag);
int erts_trace_flags(Eterm List,
Uint *pMask, ErtsTracer *pTracer, int *pCpuTimestamp);
-Eterm erts_bif_trace(int bif_index, Process* p, Eterm* args, BeamInstr *I);
-Eterm
-erts_bif_trace_epilogue(Process *p, Eterm result, int applying,
- Export* ep, BeamInstr *cp, Uint32 flags,
- Uint32 flags_meta, BeamInstr* I,
- ErtsTracer meta_tracer);
void erts_send_pending_trace_msgs(ErtsSchedulerData *esdp);
#define ERTS_CHK_PEND_TRACE_MSGS(ESDP) \
diff --git a/erts/emulator/beam/erl_unicode.c b/erts/emulator/beam/erl_unicode.c
index 1d6869a7cd..9fd3012888 100644
--- a/erts/emulator/beam/erl_unicode.c
+++ b/erts/emulator/beam/erl_unicode.c
@@ -800,7 +800,29 @@ static int check_leftovers(byte *source, int size)
}
return -1;
}
-
+
+
+static Eterm
+mk_utf8_result_bin(Process *p, Eterm bin)
+{
+ /*
+ * Don't let small refc-binaries escape out in the system
+ * when done. That is, convert such to heap binaries.
+ */
+ Uint size = binary_size(bin);
+
+ ASSERT(*binary_val(bin) == HEADER_PROC_BIN);
+
+ if (size <= ERL_ONHEAP_BIN_LIMIT) {
+ ErlHeapBin* hb = (ErlHeapBin *) HAlloc(p, heap_bin_size(size));
+ hb->thing_word = header_heap_bin(size);
+ hb->size = size;
+ sys_memcpy(hb->data, binary_bytes(bin), size);
+ return make_binary(hb);
+ }
+
+ return bin;
+}
static BIF_RETTYPE build_utf8_return(Process *p,Eterm bin,Uint pos,
@@ -822,15 +844,15 @@ static BIF_RETTYPE build_utf8_return(Process *p,Eterm bin,Uint pos,
} else {
hp = HAlloc(p,4);
}
- ret = TUPLE3(hp,am_error,bin,rest_term);
+ ret = TUPLE3(hp,am_error,mk_utf8_result_bin(p,bin),rest_term);
} else if (rest_term == NIL && num_leftovers != 0) {
Eterm leftover_bin = new_binary(p, leftover, num_leftovers);
if (check_leftovers(leftover,num_leftovers) != 0) {
hp = HAlloc(p,4);
- ret = TUPLE3(hp,am_error,bin,leftover_bin);
+ ret = TUPLE3(hp,am_error,mk_utf8_result_bin(p,bin),leftover_bin);
} else {
hp = HAlloc(p,4);
- ret = TUPLE3(hp,am_incomplete,bin,leftover_bin);
+ ret = TUPLE3(hp,am_incomplete,mk_utf8_result_bin(p,bin),leftover_bin);
}
} else { /* All OK */
if (rest_term != NIL) { /* Trap */
@@ -843,8 +865,8 @@ static BIF_RETTYPE build_utf8_return(Process *p,Eterm bin,Uint pos,
BIF_TRAP3(&characters_to_utf8_trap_exp, p, bin, rest_term, latin1);
} else { /* Success */
/*hp = HAlloc(p,5);
- ret = TUPLE4(hp,bin,rest_term,make_small(pos),make_small(err));*/
- ret = bin;
+ ret = TUPLE4(hp,mk_utf8_result_bin(p,bin),rest_term,make_small(pos),make_small(err));*/
+ ret = mk_utf8_result_bin(p,bin);
}
}
BIF_RET(ret);
diff --git a/erts/emulator/beam/error.h b/erts/emulator/beam/error.h
index 64c08b1570..44a9809a18 100644
--- a/erts/emulator/beam/error.h
+++ b/erts/emulator/beam/error.h
@@ -66,13 +66,13 @@
#define EXF_OFFSET EXTAG_BITS
#define EXF_BITS 7
-#define EXF_PANIC (1<<(0+EXF_OFFSET)) /* ignore catches */
-#define EXF_THROWN (1<<(1+EXF_OFFSET)) /* nonlocal return */
-#define EXF_LOG (1<<(2+EXF_OFFSET)) /* write to logger on termination */
-#define EXF_NATIVE (1<<(3+EXF_OFFSET)) /* occurred in native code */
-#define EXF_SAVETRACE (1<<(4+EXF_OFFSET)) /* save stack trace in internal form */
-#define EXF_ARGLIST (1<<(5+EXF_OFFSET)) /* has arglist for top of trace */
-#define EXF_RESTORE_NIF (1<<(6+EXF_OFFSET)) /* restore original bif/nif */
+#define EXF_PANIC (1<<(0+EXF_OFFSET)) /* ignore catches */
+#define EXF_THROWN (1<<(1+EXF_OFFSET)) /* nonlocal return */
+#define EXF_LOG (1<<(2+EXF_OFFSET)) /* write to logger on termination */
+#define EXF_NATIVE (1<<(3+EXF_OFFSET)) /* occurred in native code */
+#define EXF_SAVETRACE (1<<(4+EXF_OFFSET)) /* save stack trace in internal form */
+#define EXF_ARGLIST (1<<(5+EXF_OFFSET)) /* has arglist for top of trace */
+#define EXF_RESTORE_NFUNC (1<<(6+EXF_OFFSET)) /* restore original bif/nif */
#define EXC_FLAGBITS (((1<<(EXF_BITS+EXF_OFFSET))-1) \
& ~((1<<(EXF_OFFSET))-1))
diff --git a/erts/emulator/beam/export.c b/erts/emulator/beam/export.c
index 946ffeffb8..ca16bfd20e 100644
--- a/erts/emulator/beam/export.c
+++ b/erts/emulator/beam/export.c
@@ -129,14 +129,17 @@ export_alloc(struct export_entry* tmpl_e)
obj->info.mfa.module = tmpl->info.mfa.module;
obj->info.mfa.function = tmpl->info.mfa.function;
obj->info.mfa.arity = tmpl->info.mfa.arity;
- obj->beam[0] = 0;
+ obj->bif_table_index = -1;
+ obj->is_bif_traced = 0;
+
+ memset(&obj->trampoline, 0, sizeof(obj->trampoline));
+
if (BeamOpsAreInitialized()) {
- obj->beam[0] = BeamOpCodeAddr(op_call_error_handler);
+ obj->trampoline.op = BeamOpCodeAddr(op_call_error_handler);
}
- obj->beam[1] = 0;
for (ix=0; ix<ERTS_NUM_CODE_IX; ix++) {
- obj->addressv[ix] = obj->beam;
+ obj->addressv[ix] = obj->trampoline.raw;
blob->entryv[ix].slot.index = -1;
blob->entryv[ix].ep = &blob->exp;
@@ -196,6 +199,19 @@ init_export_table(void)
}
}
+static struct export_entry* init_template(struct export_templ* templ,
+ Eterm m, Eterm f, unsigned a)
+{
+ templ->entry.ep = &templ->exp;
+ templ->entry.slot.index = -1;
+ templ->exp.info.mfa.module = m;
+ templ->exp.info.mfa.function = f;
+ templ->exp.info.mfa.arity = a;
+ templ->exp.bif_table_index = -1;
+ templ->exp.is_bif_traced = 0;
+ return &templ->entry;
+}
+
/*
* Return a pointer to the export entry for the given function,
* or NULL otherwise. Notes:
@@ -214,41 +230,15 @@ erts_find_export_entry(Eterm m, Eterm f, unsigned int a,ErtsCodeIndex code_ix);
Export*
erts_find_export_entry(Eterm m, Eterm f, unsigned int a, ErtsCodeIndex code_ix)
{
- HashValue hval = EXPORT_HASH((BeamInstr) m, (BeamInstr) f, (BeamInstr) a);
- int ix;
- HashBucket* b;
-
- ix = hval % export_tables[code_ix].htable.size;
- b = export_tables[code_ix].htable.bucket[ix];
-
- /*
- * Note: We have inlined the code from hash.c for speed.
- */
-
- while (b != (HashBucket*) 0) {
- Export* ep = ((struct export_entry*) b)->ep;
- if (ep->info.mfa.module == m &&
- ep->info.mfa.function == f &&
- ep->info.mfa.arity == a) {
- return ep;
- }
- b = b->next;
- }
+ struct export_templ templ;
+ struct export_entry *ee =
+ hash_fetch(&export_tables[code_ix].htable,
+ init_template(&templ, m, f, a),
+ (H_FUN)export_hash, (HCMP_FUN)export_cmp);
+ if (ee) return ee->ep;
return NULL;
}
-static struct export_entry* init_template(struct export_templ* templ,
- Eterm m, Eterm f, unsigned a)
-{
- templ->entry.ep = &templ->exp;
- templ->entry.slot.index = -1;
- templ->exp.info.mfa.module = m;
- templ->exp.info.mfa.function = f;
- templ->exp.info.mfa.arity = a;
- return &templ->entry;
-}
-
-
/*
* Find the export entry for a loaded function.
* Returns a NULL pointer if the given function is not loaded, or
@@ -268,8 +258,8 @@ erts_find_function(Eterm m, Eterm f, unsigned int a, ErtsCodeIndex code_ix)
ee = hash_get(&export_tables[code_ix].htable, init_template(&templ, m, f, a));
if (ee == NULL ||
- (ee->ep->addressv[code_ix] == ee->ep->beam &&
- ! BeamIsOpCode(ee->ep->beam[0], op_i_generic_breakpoint))) {
+ (ee->ep->addressv[code_ix] == ee->ep->trampoline.raw &&
+ ! BeamIsOpCode(ee->ep->trampoline.op, op_i_generic_breakpoint))) {
return NULL;
}
return ee->ep;
diff --git a/erts/emulator/beam/export.h b/erts/emulator/beam/export.h
index ae8dfa4cf8..0190624f79 100644
--- a/erts/emulator/beam/export.h
+++ b/erts/emulator/beam/export.h
@@ -31,24 +31,72 @@
typedef struct export
{
- void* addressv[ERTS_NUM_CODE_IX]; /* Pointer to code for function. */
-
- ErtsCodeInfo info; /* MUST be just before beam[] */
-
- /*
- * beam[0]: This entry is 0 unless the 'addressv' field points to it.
- * Threaded code instruction to load function
- * (em_call_error_handler), execute BIF (em_apply_bif),
- * or a breakpoint instruction (op_i_generic_breakpoint).
- * beam[1]: Function pointer to BIF function (for BIFs only),
- * or pointer to threaded code if the module has an
- * on_load function that has not been run yet, or pointer
- * to code if function beam[0] is a breakpoint instruction.
- * Otherwise: 0.
- */
- BeamInstr beam[2];
+ /* Pointer to code for function. */
+ void* addressv[ERTS_NUM_CODE_IX];
+
+ /* Index into bif_table[], or -1 if not a BIF. */
+ int bif_table_index;
+ /* Non-zero if this is a BIF that's traced. */
+ int is_bif_traced;
+
+ /* This is a small trampoline function that can be used for lazy code
+ * loading, global call tracing, and so on. It's only valid when
+ * addressv points to it and should otherwise be left zeroed.
+ *
+ * Needless to say, the order of the fields below is significant. */
+ ErtsCodeInfo info;
+ union {
+ BeamInstr op; /* Union discriminant. */
+
+ struct {
+ BeamInstr op; /* op_i_generic_breakpoint */
+ BeamInstr address; /* Address of the original function */
+ } breakpoint;
+
+ /* This is used when a module refers to (imports) a function that
+ * hasn't been loaded yet. Upon loading we create an export entry which
+ * redirects to the error_handler so that the appropriate module will
+ * be loaded when called (or crash).
+ *
+ * This is also used when a module has an on_load callback as we need
+ * to defer all calls until the callback returns. `deferred` contains
+ * the address of the original function in this case, and there's an
+ * awkward condiditon where `deferred` may be set while op is zero. See
+ * erlang:finish_after_on_load/2 for details. */
+ struct {
+ BeamInstr op; /* op_call_error_handler, or 0 during the last
+ * phase of code loading when on_load is
+ * present. See above. */
+ BeamInstr deferred;
+ } not_loaded;
+
+ struct {
+ BeamInstr op; /* op_trace_jump_W */
+ BeamInstr address; /* Address of the traced function */
+ } trace;
+
+ BeamInstr raw[2]; /* For use in address comparisons, should not
+ * be tampered directly. */
+ } trampoline;
} Export;
+#ifdef DEBUG
+#define DBG_CHECK_EXPORT(EP, CX) \
+ do { \
+ if((EP)->addressv[CX] == (EP)->trampoline.raw) { \
+ /* The entry currently points at the trampoline, so the
+ * instructions must be valid. */ \
+ ASSERT(((BeamIsOpCode((EP)->trampoline.op, op_i_generic_breakpoint)) && \
+ (EP)->trampoline.breakpoint.address != 0) || \
+ ((BeamIsOpCode((EP)->trampoline.op, op_trace_jump_W)) && \
+ (EP)->trampoline.trace.address != 0) || \
+ /* (EP)->trampoline.not_loaded.deferred may be zero. */ \
+ (BeamIsOpCode((EP)->trampoline.op, op_call_error_handler))); \
+ } \
+ } while(0)
+#else
+#define DBG_CHECK_EXPORT(EP, CX) ((void)(EP), (void)(CX))
+#endif
void init_export_table(void);
void export_info(fmtfn_t, void *);
@@ -71,9 +119,6 @@ extern erts_mtx_t export_staging_lock;
#define export_staging_unlock() erts_mtx_unlock(&export_staging_lock)
#include "beam_load.h" /* For em_* extern declarations */
-#define ExportIsBuiltIn(EntryPtr) \
-(((EntryPtr)->addressv[erts_active_code_ix()] == (EntryPtr)->beam) && \
- (BeamIsOpCode((EntryPtr)->beam[0], op_apply_bif)))
#if ERTS_GLB_INLINE_INCL_FUNC_DEF
diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c
index 5cea253ebe..a575e1d743 100644
--- a/erts/emulator/beam/external.c
+++ b/erts/emulator/beam/external.c
@@ -1413,7 +1413,7 @@ enum B2TState { /* order is somewhat significant */
};
typedef struct {
- int heap_size;
+ Sint heap_size;
int terms;
byte* ep;
int atom_extra_skip;
@@ -4536,7 +4536,7 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj,
static Sint
decoded_size(byte *ep, byte* endp, int internal_tags, B2TContext* ctx)
{
- int heap_size;
+ Sint heap_size;
int terms;
int atom_extra_skip;
Uint n;
diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h
index 4c8d3d3dbe..b86709b093 100644
--- a/erts/emulator/beam/global.h
+++ b/erts/emulator/beam/global.h
@@ -122,6 +122,10 @@ void erts_nif_demonitored(ErtsResource* resource);
extern void erts_add_taint(Eterm mod_atom);
extern Eterm erts_nif_taints(Process* p);
extern void erts_print_nif_taints(fmtfn_t to, void* to_arg);
+
+/* Loads the specified NIF. The caller must have code write permission. */
+Eterm erts_load_nif(Process *c_p, BeamInstr *I, Eterm filename, Eterm args);
+
void erts_unload_nif(struct erl_module_nif* nif);
extern void erl_nif_init(void);
extern int erts_nif_get_funcs(struct erl_module_nif*,
@@ -878,11 +882,15 @@ erts_bld_port_info(Eterm **hpp,
Uint *szp,
Port *prt,
Eterm item);
+Eterm erts_bld_bin_list(Uint **hpp, Uint *szp, ErlOffHeap* oh, Eterm tail);
+
void erts_bif_info_init(void);
/* bif.c */
+void erts_write_bif_wrapper(Export *export, BeamInstr *address);
+
void erts_queue_monitor_message(Process *,
ErtsProcLocks*,
Eterm,
@@ -904,10 +912,14 @@ Eterm erts_trapping_length_1(Process* p, Eterm* args);
Eterm erl_is_function(Process* p, Eterm arg1, Eterm arg2);
+/* beam_bif_lists.c */
+void erts_init_bif_lists(void);
+
/* beam_bif_load.c */
Eterm erts_check_process_code(Process *c_p, Eterm module, int *redsp, int fcalls);
Eterm erts_proc_copy_literal_area(Process *c_p, int *redsp, int fcalls, int gc_allowed);
-
+void erts_debug_foreach_release_literal_area_off_heap(void (*func)(ErlOffHeap *, void *),
+ void *arg);
typedef struct ErtsLiteralArea_ {
struct erl_off_heap_header *off_heap;
Eterm *end;
@@ -1072,17 +1084,46 @@ Uint size_object_x(Eterm, erts_literal_area_t*);
#define size_object_litopt(Term,LitArea) size_object_x(Term,LitArea)
Uint copy_shared_calculate(Eterm, erts_shcopy_t*);
-Eterm copy_shared_perform(Eterm, Uint, erts_shcopy_t*, Eterm**, ErlOffHeap*);
-
Uint size_shared(Eterm);
+/* #define ERTS_COPY_REGISTER_LOCATION */
+
+#ifdef ERTS_COPY_REGISTER_LOCATION
+
+#define copy_shared_perform(U, V, X, Y, Z) \
+ copy_shared_perform_x((U), (V), (X), (Y), (Z), __FILE__, __LINE__)
+Eterm copy_shared_perform_x(Eterm, Uint, erts_shcopy_t*, Eterm**, ErlOffHeap*,
+ char *file, int line);
+
+Eterm copy_struct_x(Eterm, Uint, Eterm**, ErlOffHeap*, Uint*, erts_literal_area_t*,
+ char *file, int line);
+#define copy_struct(Obj,Sz,HPP,OH) \
+ copy_struct_x(Obj,Sz,HPP,OH,NULL,NULL,__FILE__,__LINE__)
+#define copy_struct_litopt(Obj,Sz,HPP,OH,LitArea) \
+ copy_struct_x(Obj,Sz,HPP,OH,NULL,LitArea,__FILE__,__LINE__)
+
+#define copy_shallow(R, SZ, HPP, OH) \
+ copy_shallow_x((R), (SZ), (HPP), (OH), __FILE__, __LINE__)
+Eterm copy_shallow_x(Eterm* ERTS_RESTRICT, Uint, Eterm**, ErlOffHeap*,
+ char *file, int line);
+
+#else
+
+#define copy_shared_perform(U, V, X, Y, Z) \
+ copy_shared_perform_x((U), (V), (X), (Y), (Z))
+Eterm copy_shared_perform_x(Eterm, Uint, erts_shcopy_t*, Eterm**, ErlOffHeap*);
+
Eterm copy_struct_x(Eterm, Uint, Eterm**, ErlOffHeap*, Uint*, erts_literal_area_t*);
#define copy_struct(Obj,Sz,HPP,OH) \
copy_struct_x(Obj,Sz,HPP,OH,NULL,NULL)
#define copy_struct_litopt(Obj,Sz,HPP,OH,LitArea) \
copy_struct_x(Obj,Sz,HPP,OH,NULL,LitArea)
-Eterm copy_shallow(Eterm* ERTS_RESTRICT, Uint, Eterm**, ErlOffHeap*);
+#define copy_shallow(R, SZ, HPP, OH) \
+ copy_shallow_x((R), (SZ), (HPP), (OH))
+Eterm copy_shallow_x(Eterm* ERTS_RESTRICT, Uint, Eterm**, ErlOffHeap*);
+
+#endif
void erts_move_multi_frags(Eterm** hpp, ErlOffHeap*, ErlHeapFragment* first,
Eterm* refs, unsigned nrefs, int literals);
@@ -1115,6 +1156,7 @@ void erts_dirty_process_main(ErtsSchedulerData *);
Eterm build_stacktrace(Process* c_p, Eterm exc);
Eterm expand_error_value(Process* c_p, Uint freason, Eterm Value);
void erts_save_stacktrace(Process* p, struct StackTrace* s, int depth);
+BeamInstr *erts_printable_return_address(Process* p, Eterm *E) ERTS_NOINLINE;
/* erl_init.c */
@@ -1257,6 +1299,10 @@ Uint erts_persistent_term_count(void);
void erts_init_persistent_dumping(void);
extern ErtsLiteralArea** erts_persistent_areas;
extern Uint erts_num_persistent_areas;
+void erts_debug_foreach_persistent_term_off_heap(void (*func)(ErlOffHeap *, void *),
+ void *arg);
+int erts_debug_have_accessed_literal_area(ErtsLiteralArea *lap);
+void erts_debug_save_accessed_literal_area(ErtsLiteralArea *lap);
/* external.c */
void erts_init_external(void);
diff --git a/erts/emulator/beam/hash.c b/erts/emulator/beam/hash.c
index 8954dbb06c..177b7cc3d1 100644
--- a/erts/emulator/beam/hash.c
+++ b/erts/emulator/beam/hash.c
@@ -30,37 +30,19 @@
#include "hash.h"
/*
-** List of sizes (all are primes)
-*/
-static const int h_size_table[] = {
- 2, 5, 11, 23, 47, 97, 197, 397, 797, /* double upto here */
- 1201, 1597,
- 2411, 3203,
- 4813, 6421,
- 9643, 12853,
- 19289, 25717,
- 51437,
- 102877,
- 205759,
- 411527,
- 823117,
- 1646237,
- 3292489,
- 6584983,
- 13169977,
- 26339969,
- 52679969,
- -1
-};
-
-/*
** Get info about hash
**
*/
+#define MAX_SHIFT (ERTS_SIZEOF_TERM * 8)
+
+static int hash_get_slots(Hash *h) {
+ return UWORD_CONSTANT(1) << (MAX_SHIFT - h->shift);
+}
+
void hash_get_info(HashInfo *hi, Hash *h)
{
- int size = h->size;
+ int size = hash_get_slots(h);
int i;
int max_depth = 0;
int objects = 0;
@@ -84,7 +66,7 @@ void hash_get_info(HashInfo *hi, Hash *h)
ASSERT(objects == h->nobjs);
hi->name = h->name;
- hi->size = h->size;
+ hi->size = hash_get_slots(h);
hi->used = used;
hi->objs = h->nobjs;
hi->depth = max_depth;
@@ -118,15 +100,15 @@ hash_table_sz(Hash *h)
int i;
for(i=0;h->name[i];i++);
i++;
- return sizeof(Hash) + h->size*sizeof(HashBucket*) + i;
+ return sizeof(Hash) + hash_get_slots(h)*sizeof(HashBucket*) + i;
}
static ERTS_INLINE void set_thresholds(Hash* h)
{
- h->grow_threshold = (8*h->size)/5; /* grow at 160% load */
- if (h->size_ix > h->min_size_ix)
- h->shrink_threshold = h->size / 5; /* shrink at 20% load */
+ h->grow_threshold = (8*hash_get_slots(h))/5; /* grow at 160% load */
+ if (h->shift < h->max_shift)
+ h->shrink_threshold = hash_get_slots(h) / 5; /* shrink at 20% load */
else
h->shrink_threshold = -1; /* never shrink below inital size */
}
@@ -138,29 +120,27 @@ static ERTS_INLINE void set_thresholds(Hash* h)
Hash* hash_init(int type, Hash* h, char* name, int size, HashFunctions fun)
{
int sz;
- int ix = 0;
+ int shift = 1;
h->meta_alloc_type = type;
- while (h_size_table[ix] != -1 && h_size_table[ix] < size)
- ix++;
- if (h_size_table[ix] == -1)
- return NULL;
-
- size = h_size_table[ix];
- sz = size*sizeof(HashBucket*);
+ while ((UWORD_CONSTANT(1) << shift) < size)
+ shift++;
- h->bucket = (HashBucket**) fun.meta_alloc(h->meta_alloc_type, sz);
-
- memzero(h->bucket, sz);
h->is_allocated = 0;
h->name = name;
h->fun = fun;
- h->size = size;
- h->size_ix = ix;
- h->min_size_ix = ix;
+ h->shift = MAX_SHIFT - shift;
+ h->max_shift = h->shift;
h->nobjs = 0;
set_thresholds(h);
+
+ sz = hash_get_slots(h) * sizeof(HashBucket*);
+ h->bucket = (HashBucket**) fun.meta_alloc(h->meta_alloc_type, sz);
+ memzero(h->bucket, sz);
+
+ ASSERT(h->shift > 0 && h->shift < 64);
+
return h;
}
@@ -183,7 +163,7 @@ Hash* hash_new(int type, char* name, int size, HashFunctions fun)
*/
void hash_delete(Hash* h)
{
- int old_size = h->size;
+ int old_size = hash_get_slots(h);
int i;
for (i = 0; i < old_size; i++) {
@@ -206,22 +186,20 @@ void hash_delete(Hash* h)
static void rehash(Hash* h, int grow)
{
int sz;
- int old_size = h->size;
+ int old_size = hash_get_slots(h);
HashBucket** new_bucket;
int i;
if (grow) {
- if ((h_size_table[h->size_ix+1]) == -1)
- return;
- h->size_ix++;
+ h->shift--;
}
else {
- if (h->size_ix == 0)
+ if (h->shift == h->max_shift)
return;
- h->size_ix--;
+ h->shift++;
}
- h->size = h_size_table[h->size_ix];
- sz = h->size*sizeof(HashBucket*);
+
+ sz = hash_get_slots(h)*sizeof(HashBucket*);
new_bucket = (HashBucket **) h->fun.meta_alloc(h->meta_alloc_type, sz);
memzero(new_bucket, sz);
@@ -230,7 +208,7 @@ static void rehash(Hash* h, int grow)
HashBucket* b = h->bucket[i];
while (b != (HashBucket*) 0) {
HashBucket* b_next = b->next;
- int ix = b->hvalue % h->size;
+ Uint ix = hash_get_slot(h, b->hvalue);
b->next = new_bucket[ix];
new_bucket[ix] = b;
b = b_next;
@@ -247,16 +225,7 @@ static void rehash(Hash* h, int grow)
*/
void* hash_get(Hash* h, void* tmpl)
{
- HashValue hval = h->fun.hash(tmpl);
- int ix = hval % h->size;
- HashBucket* b = h->bucket[ix];
-
- while(b != (HashBucket*) 0) {
- if ((b->hvalue == hval) && (h->fun.cmp(tmpl, (void*)b) == 0))
- return (void*) b;
- b = b->next;
- }
- return (void*) 0;
+ return hash_fetch(h, tmpl, h->fun.hash, h->fun.cmp);
}
/*
@@ -265,7 +234,7 @@ void* hash_get(Hash* h, void* tmpl)
void* hash_put(Hash* h, void* tmpl)
{
HashValue hval = h->fun.hash(tmpl);
- int ix = hval % h->size;
+ Uint ix = hash_get_slot(h, hval);
HashBucket* b = h->bucket[ix];
while(b != (HashBucket*) 0) {
@@ -291,7 +260,7 @@ void* hash_put(Hash* h, void* tmpl)
void* hash_erase(Hash* h, void* tmpl)
{
HashValue hval = h->fun.hash(tmpl);
- int ix = hval % h->size;
+ Uint ix = hash_get_slot(h, hval);
HashBucket* b = h->bucket[ix];
HashBucket* prev = 0;
@@ -323,7 +292,7 @@ void *
hash_remove(Hash *h, void *tmpl)
{
HashValue hval = h->fun.hash(tmpl);
- int ix = hval % h->size;
+ Uint ix = hash_get_slot(h, hval);
HashBucket *b = h->bucket[ix];
HashBucket *prev = NULL;
@@ -343,11 +312,11 @@ hash_remove(Hash *h, void *tmpl)
return NULL;
}
-void hash_foreach(Hash* h, void (*func)(void *, void *), void *func_arg2)
+void hash_foreach(Hash* h, HFOREACH_FUN func, void *func_arg2)
{
int i;
- for (i = 0; i < h->size; i++) {
+ for (i = 0; i < hash_get_slots(h); i++) {
HashBucket* b = h->bucket[i];
while(b != (HashBucket*) 0) {
(*func)((void *) b, func_arg2);
diff --git a/erts/emulator/beam/hash.h b/erts/emulator/beam/hash.h
index d319aaca83..4e8eb6594b 100644
--- a/erts/emulator/beam/hash.h
+++ b/erts/emulator/beam/hash.h
@@ -18,16 +18,16 @@
* %CopyrightEnd%
*/
-/*
-** General hash functions
-**
-*/
+/**
+ * General hash functions
+ *
+ **/
#ifndef __HASH_H__
#define __HASH_H__
#include "sys.h"
-typedef unsigned long HashValue;
+typedef UWord HashValue;
typedef struct hash Hash;
typedef int (*HCMP_FUN)(void*, void*);
@@ -38,6 +38,7 @@ typedef void (*HFREE_FUN)(void*);
typedef void* (*HMALLOC_FUN)(int,size_t);
typedef void (*HMFREE_FUN)(int,void*);
typedef int (*HMPRINT_FUN)(fmtfn_t,void*,char*, ...);
+typedef void (*HFOREACH_FUN)(void *, void *);
/*
** This bucket must be placed in top of
@@ -75,11 +76,10 @@ struct hash
int is_allocated; /* 0 iff hash structure is on stack or is static */
int meta_alloc_type; /* argument to pass to meta_alloc and meta_free */
char* name; /* Table name (static string, for debugging) */
- int size; /* Number of slots */
+ int shift; /* How much to shift the hash value */
+ int max_shift; /* Never shift more than this value */
int shrink_threshold;
int grow_threshold;
- int size_ix; /* Size index in size table */
- int min_size_ix; /* Never shrink table smaller than this */
int nobjs; /* Number of objects in table */
HashBucket** bucket; /* Vector of bucket pointers (objects) */
};
@@ -96,6 +96,54 @@ void* hash_get(Hash*, void*);
void* hash_put(Hash*, void*);
void* hash_erase(Hash*, void*);
void* hash_remove(Hash*, void*);
-void hash_foreach(Hash*, void (*func)(void *, void *), void *);
+void hash_foreach(Hash*, HFOREACH_FUN, void *);
+
+ERTS_GLB_INLINE Uint hash_get_slot(Hash *h, HashValue hv);
+ERTS_GLB_INLINE void* hash_fetch(Hash *, void*, H_FUN, HCMP_FUN);
+
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+ERTS_GLB_INLINE Uint
+hash_get_slot(Hash *h, HashValue hv)
+{
+ /* This slot mapping function uses fibonacci hashing in order to
+ * protect itself against a very bad hash function. This is not
+ * a hash function, so the user of hash.h should still spend time
+ * to figure out a good hash function for its data.
+ *
+ * See https://probablydance.com/2018/06/16/fibonacci-hashing-the-optimization-that-the-world-forgot-or-a-better-alternative-to-integer-modulo/
+ * for some thoughts and ideas about fibonacci hashing.
+ */
+
+ /* This is not strictly part of the fibonacci hashing algorithm
+ * but it does help to spread the values of the mapping function better.
+ */
+ hv ^= hv >> h->shift;
+#ifdef ARCH_64
+ /* 2^64 / 1.61803398875 = 11400714819323198485.... */
+ return (UWORD_CONSTANT(11400714819323198485) * hv) >> h->shift;
+#else
+ /* 2^32 / 1.61803398875 = 2654435769.... */
+ return (UWORD_CONSTANT(2654435769) * hv) >> h->shift;
+#endif
+}
+
+ERTS_GLB_INLINE void* hash_fetch(Hash *h, void* tmpl, H_FUN hash, HCMP_FUN cmp)
+{
+ HashValue hval = hash(tmpl);
+ Uint ix = hash_get_slot(h, hval);
+ HashBucket* b = h->bucket[ix];
+ ASSERT(h->fun.hash == hash);
+ ASSERT(h->fun.cmp == cmp);
+
+ while(b != (HashBucket*) 0) {
+ if ((b->hvalue == hval) && (cmp(tmpl, (void*)b) == 0))
+ return (void*) b;
+ b = b->next;
+ }
+ return (void*) 0;
+}
+
+#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */
#endif
diff --git a/erts/emulator/beam/index.c b/erts/emulator/beam/index.c
index be1771b037..09d3c24424 100644
--- a/erts/emulator/beam/index.c
+++ b/erts/emulator/beam/index.c
@@ -114,35 +114,26 @@ int index_get(IndexTable* t, void* tmpl)
return -1;
}
-void erts_index_merge(Hash* src, IndexTable* dst)
+static void index_merge_foreach(IndexSlot *p, IndexTable *dst)
{
- int limit = src->size;
- HashBucket** bucket = src->bucket;
- int i;
-
- for (i = 0; i < limit; i++) {
- HashBucket* b = bucket[i];
- IndexSlot* p;
- int ix;
-
- while (b) {
- Uint sz;
- ix = dst->entries++;
- if (ix >= dst->size) {
- if (ix >= dst->limit) {
- erts_exit(ERTS_ERROR_EXIT, "no more index entries in %s (max=%d)\n",
- dst->htable.name, dst->limit);
- }
- sz = INDEX_PAGE_SIZE*sizeof(IndexSlot*);
- dst->seg_table[ix>>INDEX_PAGE_SHIFT] = erts_alloc(dst->type, sz);
- dst->size += INDEX_PAGE_SIZE;
- }
- p = (IndexSlot*) b;
- p->index = ix;
- dst->seg_table[ix>>INDEX_PAGE_SHIFT][ix&INDEX_PAGE_MASK] = p;
- b = b->next;
- }
+ Uint sz;
+ int ix = dst->entries++;
+ if (ix >= dst->size) {
+ if (ix >= dst->limit) {
+ erts_exit(ERTS_ERROR_EXIT, "no more index entries in %s (max=%d)\n",
+ dst->htable.name, dst->limit);
+ }
+ sz = INDEX_PAGE_SIZE*sizeof(IndexSlot*);
+ dst->seg_table[ix>>INDEX_PAGE_SHIFT] = erts_alloc(dst->type, sz);
+ dst->size += INDEX_PAGE_SIZE;
}
+ p->index = ix;
+ dst->seg_table[ix>>INDEX_PAGE_SHIFT][ix&INDEX_PAGE_MASK] = p;
+}
+
+void erts_index_merge(Hash* src, IndexTable* dst)
+{
+ hash_foreach(src, (HFOREACH_FUN)index_merge_foreach, dst);
}
void index_erase_latest_from(IndexTable* t, Uint from_ix)
diff --git a/erts/emulator/beam/instrs.tab b/erts/emulator/beam/instrs.tab
index bc8c1189a8..9396c09182 100644
--- a/erts/emulator/beam/instrs.tab
+++ b/erts/emulator/beam/instrs.tab
@@ -19,7 +19,12 @@
// %CopyrightEnd%
//
-// Stack manipulation instructions
+//
+// Stack manipulation instructions follow.
+//
+// See the comment for AH() in macros.tab for information about
+// the layout of stack frames.
+//
allocate(NeedStack, Live) {
$AH($NeedStack, 0, $Live);
@@ -58,105 +63,169 @@ allocate_heap_zero(NeedStack, NeedHeap, Live) {
deallocate(Deallocate) {
//| -no_prefetch
- SET_CP(c_p, (BeamInstr *) cp_val(*E));
E = ADD_BYTE_OFFSET(E, $Deallocate);
}
-deallocate_return(Deallocate) {
- //| -no_next
- int words_to_pop = $Deallocate;
- SET_I((BeamInstr *) cp_val(*E));
- E = ADD_BYTE_OFFSET(E, words_to_pop);
- CHECK_TERM(x(0));
- DispatchReturn;
+//
+// Micro-benchmarks showed that the deallocate_return instruction
+// became slower when the continuation pointer was moved from
+// the process struct to the stack. The reason seems to be read
+// dependencies, i.e. that the CPU cannot figure out beforehand
+// from which position on the stack the continuation pointer
+// should be fetched.
+//
+// Initializing num_bytes with a constant value seems to restore
+// the lost speed, so we've specialized the instruction for the
+// most common values.
+//
+
+deallocate_return0 := dealloc_ret.n0.execute;
+deallocate_return1 := dealloc_ret.n1.execute;
+deallocate_return2 := dealloc_ret.n2.execute;
+deallocate_return3 := dealloc_ret.n3.execute;
+deallocate_return4 := dealloc_ret.n4.execute;
+deallocate_return := dealloc_ret.var.execute;
+
+dealloc_ret.head() {
+ Uint num_bytes;
}
-move_deallocate_return(Src, Deallocate) {
- x(0) = $Src;
- $deallocate_return($Deallocate);
+dealloc_ret.n0() {
+ num_bytes = (0+1) * sizeof(Eterm);
}
-// Call instructions
+dealloc_ret.n1() {
+ num_bytes = (1+1) * sizeof(Eterm);
+}
+
+dealloc_ret.n2() {
+ num_bytes = (2+1) * sizeof(Eterm);
+}
+
+dealloc_ret.n3() {
+ num_bytes = (3+1) * sizeof(Eterm);
+}
+
+dealloc_ret.n4() {
+ num_bytes = (4+1) * sizeof(Eterm);
+}
+
+dealloc_ret.var(Deallocate) {
+ num_bytes = $Deallocate;
+}
-DISPATCH_REL(CallDest) {
+dealloc_ret.execute() {
//| -no_next
- $SET_I_REL($CallDest);
- DTRACE_LOCAL_CALL(c_p, erts_code_to_codemfa(I));
- Dispatch();
+
+ E = ADD_BYTE_OFFSET(E, num_bytes);
+ $RETURN();
+ CHECK_TERM(x(0));
+ $DISPATCH_RETURN();
}
-DISPATCH_ABS(CallDest) {
+move_deallocate_return(Src, Deallocate) {
//| -no_next
- SET_I((BeamInstr *) $CallDest);
- DTRACE_LOCAL_CALL(c_p, erts_code_to_codemfa(I));
- Dispatch();
+
+ /*
+ * Explicitly do reads first to mitigate the impact of read
+ * dependencies.
+ */
+
+ Uint bytes_to_pop = $Deallocate;
+ Eterm src = $Src;
+ E = ADD_BYTE_OFFSET(E, bytes_to_pop);
+ x(0) = src;
+ $RETURN();
+ CHECK_TERM(x(0));
+ $DISPATCH_RETURN();
}
+// Call instructions
+
i_call(CallDest) {
- SET_CP(c_p, $NEXT_INSTRUCTION);
+ //| -no_next
+ $SAVE_CONTINUATION_POINTER($NEXT_INSTRUCTION);
$DISPATCH_REL($CallDest);
}
move_call(Src, CallDest) {
- x(0) = $Src;
- SET_CP(c_p, $NEXT_INSTRUCTION);
- $DISPATCH_REL($CallDest);
+ //| -no_next
+ Eterm call_dest = $CallDest;
+ Eterm src = $Src;
+ $SAVE_CONTINUATION_POINTER($NEXT_INSTRUCTION);
+ x(0) = src;
+ $DISPATCH_REL(call_dest);
}
i_call_last(CallDest, Deallocate) {
+ //| -no_next
$deallocate($Deallocate);
$DISPATCH_REL($CallDest);
}
move_call_last(Src, CallDest, Deallocate) {
- x(0) = $Src;
- $i_call_last($CallDest, $Deallocate);
+ //| -no_next
+ Eterm call_dest = $CallDest;
+ Eterm src = $Src;
+ $deallocate($Deallocate);
+ x(0) = src;
+ $DISPATCH_REL(call_dest);
}
i_call_only(CallDest) {
+ //| -no_next
$DISPATCH_REL($CallDest);
}
move_call_only(Src, CallDest) {
- x(0) = $Src;
- $i_call_only($CallDest);
-}
-
-DISPATCHX(Dest) {
//| -no_next
- DTRACE_GLOBAL_CALL_FROM_EXPORT(c_p, $Dest);
- // Dispatchx assumes the Export* is in Arg(0)
- I = (&$Dest) - 1;
- Dispatchx();
+ Eterm call_dest = $CallDest;
+ Eterm src = $Src;
+ x(0) = src;
+ $DISPATCH_REL(call_dest);
}
i_call_ext(Dest) {
- SET_CP(c_p, $NEXT_INSTRUCTION);
- $DISPATCHX($Dest);
+ //| -no_next
+ $SAVE_CONTINUATION_POINTER($NEXT_INSTRUCTION);
+ $DISPATCH_EXPORT($Dest);
}
-i_move_call_ext(Src, Dest) {
- x(0) = $Src;
- $i_call_ext($Dest);
+i_move_call_ext(Src, CallDest) {
+ //| -no_next
+ Eterm call_dest = $CallDest;
+ Eterm src = $Src;
+ $SAVE_CONTINUATION_POINTER($NEXT_INSTRUCTION);
+ x(0) = src;
+ $DISPATCH_EXPORT(call_dest);
}
i_call_ext_only(Dest) {
- $DISPATCHX($Dest);
+ //| -no_next
+ $DISPATCH_EXPORT($Dest);
}
-i_move_call_ext_only(Dest, Src) {
- x(0) = $Src;
- $i_call_ext_only($Dest);
+i_move_call_ext_only(CallDest, Src) {
+ //| -no_next
+ Eterm call_dest = $CallDest;
+ Eterm src = $Src;
+ x(0) = src;
+ $DISPATCH_EXPORT(call_dest);
}
i_call_ext_last(Dest, Deallocate) {
+ //| -no_next
$deallocate($Deallocate);
- $DISPATCHX($Dest);
+ $DISPATCH_EXPORT($Dest);
}
-i_move_call_ext_last(Dest, StackOffset, Src) {
- x(0) = $Src;
- $i_call_ext_last($Dest, $StackOffset);
+i_move_call_ext_last(CallDest, Deallocate, Src) {
+ //| -no_next
+ Eterm call_dest = $CallDest;
+ Eterm src = $Src;
+ $deallocate($Deallocate);
+ x(0) = src;
+ $DISPATCH_EXPORT(call_dest);
}
APPLY(I, Deallocate, Next) {
@@ -172,16 +241,18 @@ HANDLE_APPLY_ERROR() {
}
i_apply() {
+ //| -no_next
BeamInstr *next;
$APPLY(NULL, 0, next);
if (ERTS_LIKELY(next != NULL)) {
- SET_CP(c_p, $NEXT_INSTRUCTION);
+ $SAVE_CONTINUATION_POINTER($NEXT_INSTRUCTION);
$DISPATCH_ABS(next);
}
$HANDLE_APPLY_ERROR();
}
i_apply_last(Deallocate) {
+ //| -no_next
BeamInstr *next;
$APPLY(I, $Deallocate, next);
if (ERTS_LIKELY(next != NULL)) {
@@ -192,6 +263,7 @@ i_apply_last(Deallocate) {
}
i_apply_only() {
+ //| -no_next
BeamInstr *next;
$APPLY(I, 0, next);
if (ERTS_LIKELY(next != NULL)) {
@@ -208,16 +280,18 @@ FIXED_APPLY(Arity, I, Deallocate, Next) {
}
apply(Arity) {
+ //| -no_next
BeamInstr *next;
$FIXED_APPLY($Arity, NULL, 0, next);
if (ERTS_LIKELY(next != NULL)) {
- SET_CP(c_p, $NEXT_INSTRUCTION);
+ $SAVE_CONTINUATION_POINTER($NEXT_INSTRUCTION);
$DISPATCH_ABS(next);
}
$HANDLE_APPLY_ERROR();
}
apply_last(Arity, Deallocate) {
+ //| -no_next
BeamInstr *next;
$FIXED_APPLY($Arity, I, $Deallocate, next);
if (ERTS_LIKELY(next != NULL)) {
@@ -237,23 +311,19 @@ HANDLE_APPLY_FUN_ERROR() {
goto find_func_info;
}
-DISPATCH_FUN(I) {
- //| -no_next
- SET_I($I);
- Dispatchfun();
-}
-
i_apply_fun() {
+ //| -no_next
BeamInstr *next;
$APPLY_FUN(next);
if (ERTS_LIKELY(next != NULL)) {
- SET_CP(c_p, $NEXT_INSTRUCTION);
+ $SAVE_CONTINUATION_POINTER($NEXT_INSTRUCTION);
$DISPATCH_FUN(next);
}
$HANDLE_APPLY_FUN_ERROR();
}
i_apply_fun_last(Deallocate) {
+ //| -no_next
BeamInstr *next;
$APPLY_FUN(next);
if (ERTS_LIKELY(next != NULL)) {
@@ -264,6 +334,7 @@ i_apply_fun_last(Deallocate) {
}
i_apply_fun_only() {
+ //| -no_next
BeamInstr *next;
$APPLY_FUN(next);
if (ERTS_LIKELY(next != NULL)) {
@@ -280,16 +351,18 @@ CALL_FUN(Fun, Next) {
}
i_call_fun(Fun) {
+ //| -no_next
BeamInstr *next;
$CALL_FUN($Fun, next);
if (ERTS_LIKELY(next != NULL)) {
- SET_CP(c_p, $NEXT_INSTRUCTION);
+ $SAVE_CONTINUATION_POINTER($NEXT_INSTRUCTION);
$DISPATCH_FUN(next);
}
$HANDLE_APPLY_FUN_ERROR();
}
i_call_fun_last(Fun, Deallocate) {
+ //| -no_next
BeamInstr *next;
$CALL_FUN($Fun, next);
if (ERTS_LIKELY(next != NULL)) {
@@ -301,18 +374,13 @@ i_call_fun_last(Fun, Deallocate) {
return() {
//| -no_next
- SET_I(c_p->cp);
- DTRACE_RETURN_FROM_PC(c_p);
+ $RETURN();
- /*
- * We must clear the CP to make sure that a stale value do not
- * create a false module dependcy preventing code upgrading.
- * It also means that we can use the CP in stack backtraces.
- */
- c_p->cp = 0;
+ DTRACE_RETURN_FROM_PC(c_p);
CHECK_TERM(r(0));
HEAP_SPACE_VERIFIED(0);
- DispatchReturn;
+
+ $DISPATCH_RETURN();
}
get_list(Src, Hd, Tl) {
@@ -478,16 +546,21 @@ i_make_fun(FunP, NumFree) {
}
move_trim(Src, Dst, Words) {
- Uint cp = E[0];
$Dst = $Src;
- E += $Words;
- E[0] = cp;
+ $i_trim($Words);
}
i_trim(Words) {
- Uint cp = E[0];
E += $Words;
- E[0] = cp;
+
+ /*
+ * Clear the reserved location for the continuation pointer at
+ * E[0]. This is not strictly necessary for correctness, but if a
+ * GC is triggered before E[0] is overwritten by another
+ * continuation pointer the now dead term at E[0] would be
+ * retained by the GC.
+ */
+ E[0] = NIL;
}
move(Src, Dst) {
@@ -599,9 +672,8 @@ move_window5(S1, S2, S3, S4, S5, D) {
move_return(Src) {
//| -no_next
x(0) = $Src;
- SET_I(c_p->cp);
- c_p->cp = 0;
- DispatchReturn;
+ $RETURN();
+ $DISPATCH_RETURN();
}
move_x1(Src) {
diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c
index 45fef0c0e5..1622330aab 100644
--- a/erts/emulator/beam/io.c
+++ b/erts/emulator/beam/io.c
@@ -3243,7 +3243,10 @@ static void deliver_read_message(Port* prt, erts_aint32_t state, Eterm to,
need += 3;
}
if ((state & ERTS_PORT_SFLG_BINARY_IO) && buf != NULL) {
- need += PROC_BIN_SIZE;
+ if (len <= ERL_ONHEAP_BIN_LIMIT)
+ need += heap_bin_size(len);
+ else
+ need += PROC_BIN_SIZE;
} else {
need += 2*len;
}
@@ -3261,11 +3264,21 @@ static void deliver_read_message(Port* prt, erts_aint32_t state, Eterm to,
if ((state & ERTS_PORT_SFLG_BINARY_IO) == 0) {
listp = buf_to_intlist(&hp, buf, len, listp);
} else if (buf != NULL) {
- Binary* bptr = erts_bin_nrml_alloc(len);
- sys_memcpy(bptr->orig_bytes, buf, len);
+ if (len <= ERL_ONHEAP_BIN_LIMIT) {
+ ErlHeapBin *hbin = (ErlHeapBin *) hp;
+ hbin->thing_word = header_heap_bin(len);
+ hbin->size = (Uint) len;
+ sys_memcpy(hbin->data, buf, len);
+ listp = make_binary(hp);
+ hp += heap_bin_size(len);
+ }
+ else {
+ Binary* bptr = erts_bin_nrml_alloc(len);
+ sys_memcpy(bptr->orig_bytes, buf, len);
- listp = erts_build_proc_bin(ohp, hp, bptr);
- hp += PROC_BIN_SIZE;
+ listp = erts_build_proc_bin(ohp, hp, bptr);
+ hp += PROC_BIN_SIZE;
+ }
}
/* Prepend the header */
@@ -3388,7 +3401,14 @@ deliver_vec_message(Port* prt, /* Port */
need = 3 + 3; /* Heap space for two tuples */
if (state & ERTS_PORT_SFLG_BINARY_IO) {
- need += (2+PROC_BIN_SIZE)*vsize - 2 + hlen*2;
+ Sint i;
+ for (i = 0; i < vsize; i++) {
+ if (iov[i].iov_len <= ERL_ONHEAP_BIN_LIMIT)
+ need += heap_bin_size(iov[i].iov_len);
+ else
+ need += PROC_BIN_SIZE;
+ }
+ need += (vsize - 1)*2 + hlen*2;
} else {
need += (hlen+csize)*2;
}
@@ -3408,36 +3428,52 @@ deliver_vec_message(Port* prt, /* Port */
} else {
binv += vsize;
while (vsize--) {
- ErlDrvBinary* b;
- ProcBin* pb = (ProcBin*) hp;
- byte* base;
-
- iov--;
- binv--;
- if ((b = *binv) == NULL) {
- b = driver_alloc_binary(iov->iov_len);
- sys_memcpy(b->orig_bytes, iov->iov_base, iov->iov_len);
- base = (byte*) b->orig_bytes;
- } else {
- /* Must increment reference count, caller calls free */
- driver_binary_inc_refc(b);
- base = iov->iov_base;
- }
- pb->thing_word = HEADER_PROC_BIN;
- pb->size = iov->iov_len;
- pb->next = ohp->first;
- ohp->first = (struct erl_off_heap_header*)pb;
- pb->val = ErlDrvBinary2Binary(b);
- pb->bytes = base;
- pb->flags = 0;
- hp += PROC_BIN_SIZE;
+ Eterm bin;
+ Uint bin_size;
+ iov--;
+ binv--;
+ bin_size = (Uint) iov->iov_len;
+
+ if (bin_size <= ERL_ONHEAP_BIN_LIMIT) {
+ ErlHeapBin *hbin = (ErlHeapBin *) hp;
+ hbin->thing_word = header_heap_bin(bin_size);
+ hbin->size = bin_size;
+ sys_memcpy(hbin->data, iov->iov_base, bin_size);
+ bin = make_binary(hp);
+ hp += heap_bin_size(bin_size);
+ }
+ else {
+ ErlDrvBinary* b;
+ ProcBin* pb = (ProcBin*) hp;
+ byte* base;
+
+ if ((b = *binv) == NULL) {
+ b = driver_alloc_binary(bin_size);
+ sys_memcpy(b->orig_bytes, iov->iov_base, bin_size);
+ base = (byte*) b->orig_bytes;
+ } else {
+ /* Must increment reference count, caller calls free */
+ driver_binary_inc_refc(b);
+ base = iov->iov_base;
+ }
+ pb->thing_word = HEADER_PROC_BIN;
+ pb->size = bin_size;
+ pb->next = ohp->first;
+ ohp->first = (struct erl_off_heap_header*)pb;
+ pb->val = ErlDrvBinary2Binary(b);
+ pb->bytes = base;
+ pb->flags = 0;
+ hp += PROC_BIN_SIZE;
- OH_OVERHEAD(ohp, iov->iov_len / sizeof(Eterm));
+ OH_OVERHEAD(ohp, bin_size / sizeof(Eterm));
+
+ bin = make_binary(pb);
+ }
if (listp == NIL) { /* compatible with deliver_bin_message */
- listp = make_binary(pb);
+ listp = bin;
} else {
- listp = CONS(hp, make_binary(pb), listp);
+ listp = CONS(hp, bin, listp);
hp += 2;
}
}
diff --git a/erts/emulator/beam/macros.tab b/erts/emulator/beam/macros.tab
index 1b5e5f66b0..848e35d45c 100644
--- a/erts/emulator/beam/macros.tab
+++ b/erts/emulator/beam/macros.tab
@@ -104,14 +104,136 @@ GC_TEST_PRESERVE(NeedHeap, Live, PreserveTerm) {
// Make sure that there are NeedStack + NeedHeap + 1 words available
-// on the combined heap/stack segment, then allocates NeedHeap + 1
-// words on the stack and saves CP.
+// on the combined heap/stack segment, then decrement the stack
+// pointer by (NeedStack + 1) words. Finally clear the word reserved
+// for the continuation pointer at the top of the stack.
+//
+// Stack frame layout:
+//
+// +-----------+
+// y(N) | Term |
+// +-----------+
+// .
+// .
+// .
+// +-----------+
+// y(0) | Term |
+// +-----------+
+// E ==> | NIL or CP |
+// +-----------+
+//
+// When the function owning the stack frame is the currently executing
+// function, the word at the top of the stack is NIL. When calling
+// another function, the continuation pointer will be stored in the
+// word at the top of the stack. When returning to the function
+// owning the stack frame, the word at the stack top will again be set
+// to NIL.
+
AH(NeedStack, NeedHeap, Live) {
unsigned needed = $NeedStack + 1;
$GC_TEST(needed, $NeedHeap, $Live);
E -= needed;
- *E = make_cp(c_p->cp);
- c_p->cp = 0;
+ *E = NIL;
+}
+
+
+//
+// Helpers for call instructions
+//
+
+DISPATCH() {
+ BeamInstr dis_next;
+
+ dis_next = *I;
+ CHECK_ARGS(I);
+
+ if (FCALLS > 0 || FCALLS > neg_o_reds) {
+ FCALLS--;
+ Goto(dis_next);
+ } else {
+ goto context_switch;
+ }
+}
+
+DISPATCH_ABS(CallDest) {
+ SET_I((BeamInstr *) $CallDest);
+ DTRACE_LOCAL_CALL(c_p, erts_code_to_codemfa(I));
+
+ $DISPATCH();
+}
+
+DISPATCH_EXPORT(Export) {
+ BeamInstr dis_next;
+ Export *ep;
+
+ ep = (Export*)($Export);
+
+ DTRACE_GLOBAL_CALL_FROM_EXPORT(c_p, ep);
+
+ SET_I(ep->addressv[erts_active_code_ix()]);
+ CHECK_ARGS(I);
+ dis_next = *I;
+
+ if (ERTS_UNLIKELY(FCALLS <= 0)) {
+ if (ERTS_PROC_GET_SAVED_CALLS_BUF(c_p) && FCALLS > neg_o_reds) {
+ save_calls(c_p, ep);
+ } else {
+ goto context_switch;
+ }
+ }
+
+ FCALLS--;
+ Goto(dis_next);
+}
+
+DISPATCH_FUN(I) {
+ BeamInstr dis_next;
+
+ SET_I($I);
+
+ dis_next = *I;
+ CHECK_ARGS(I);
+
+ if (FCALLS > 0 || FCALLS > neg_o_reds) {
+ FCALLS--;
+ Goto(dis_next);
+ } else {
+ goto context_switch_fun;
+ }
+}
+
+DISPATCH_REL(CallDest) {
+ $SET_I_REL($CallDest);
+ DTRACE_LOCAL_CALL(c_p, erts_code_to_codemfa(I));
+
+ $DISPATCH();
+}
+
+DISPATCH_RETURN() {
+ if (FCALLS > 0 || FCALLS > neg_o_reds) {
+ FCALLS--;
+ Goto(*I);
+ } else {
+ c_p->current = NULL;
+ c_p->arity = 1;
+ goto context_switch3;
+ }
+}
+
+// Save the continuation pointer in the reserved slot at the
+// top of the stack as preparation for doing a function call.
+
+SAVE_CONTINUATION_POINTER(IP) {
+ ASSERT(VALID_INSTR(*($IP)));
+ *E = (BeamInstr) ($IP);
+}
+
+// Return to the function whose continuation pointer is stored
+// at the top of the stack and set that word to NIL.
+
+RETURN() {
+ SET_I(cp_val(*E));
+ *E = NIL;
}
NEXT0() {
diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab
index f525d126e7..1d336e4b7b 100644
--- a/erts/emulator/beam/ops.tab
+++ b/erts/emulator/beam/ops.tab
@@ -77,19 +77,32 @@ return
# To ensure that a "move Src x(0)" instruction can be combined with
# the following call instruction, we need to make sure that there is
# no line/1 instruction between the move and the call.
-#
-# A tail-recursive call to an external function (BIF or non-BIF) will
-# never be saved on the stack, so there is no reason to keep the line
-# instruction.
+
+move S X0=x==0 | line Loc | call Ar Func => \
+ line Loc | move S X0 | call Ar Func
move S X0=x==0 | line Loc | call_ext Ar Func => \
line Loc | move S X0 | call_ext Ar Func
+
+#
+# A tail call will not refer to the current function on error unless it's a
+# BIF, so we can omit the line instruction for non-BIFs.
+#
+
+move S X0=x==0 | line Loc | call_ext_last Ar Func=u$is_bif D => \
+ line Loc | move S X0 | call_ext_last Ar Func D
+move S X0=x==0 | line Loc | call_ext_only Ar Func=u$is_bif => \
+ line Loc | move S X0 | call_ext_only Ar Func
+
move S X0=x==0 | line Loc | call_ext_last Ar Func D => \
move S X0 | call_ext_last Ar Func D
move S X0=x==0 | line Loc | call_ext_only Ar Func => \
move S X0 | call_ext_only Ar Func
-move S X0=x==0 | line Loc | call Ar Func => \
- line Loc | move S X0 | call Ar Func
+
+move S X0=x==0 | line Loc | call_last Ar Func D => \
+ move S X0 | call_last Ar Func D
+move S X0=x==0 | line Loc | call_only Ar Func => \
+ move S X0 | call_only Ar Func
line Loc | func_info M F A => func_info M F A | line Loc
@@ -574,8 +587,8 @@ put_list s s d
%cold
normal_exit
continue_exit
-apply_bif
-call_nif
+call_bif W
+call_nif W W W
call_error_handler
error_action_code
return_trace
@@ -596,8 +609,20 @@ move S x==0 | deallocate D | return => move_deallocate_return S D
move_deallocate_return xycn Q
+deallocate u==0 | return => deallocate_return0
+deallocate u==1 | return => deallocate_return1
+deallocate u==2 | return => deallocate_return2
+deallocate u==3 | return => deallocate_return3
+deallocate u==4 | return => deallocate_return4
+
deallocate D | return => deallocate_return D
+deallocate_return0
+deallocate_return1
+deallocate_return2
+deallocate_return3
+deallocate_return4
+
deallocate_return Q
test_heap Need u==1 | put_list Y=y x==0 x==0 => test_heap_1_put_list Need Y
@@ -775,62 +800,22 @@ allocate_init t t? y
# External function and bif calls.
#################################################################
-#
-# The BIFs erts_internal:check_process_code/1 must be called like a function,
-# to ensure that c_p->i (program counter) is set correctly (an ordinary
-# BIF call doesn't set it).
-#
-
-call_ext u==1 Bif=u$bif:erts_internal:check_process_code/1 => i_call_ext Bif
-call_ext_last u==1 Bif=u$bif:erts_internal:check_process_code/1 D => i_call_ext_last Bif D
-call_ext_only u==1 Bif=u$bif:erts_internal:check_process_code/1 => i_call_ext_only Bif
-
-#
-# The BIFs erts_internal:garbage_collect/1 must be called like a function,
-# to allow them to invoke the garbage collector. (The stack pointer must
-# be saved and p->arity must be zeroed, which is not done on ordinary BIF calls.)
-#
-call_ext u==1 Bif=u$bif:erts_internal:garbage_collect/1 => i_call_ext Bif
-call_ext_last u==1 Bif=u$bif:erts_internal:garbage_collect/1 D => i_call_ext_last Bif D
-call_ext_only u==1 Bif=u$bif:erts_internal:garbage_collect/1 => i_call_ext_only Bif
-
-#
-# put/2 and erase/1 must be able to do garbage collection, so we must call
-# them like functions.
-#
-
-call_ext u==2 Bif=u$bif:erlang:put/2 => i_call_ext Bif
-call_ext_last u==2 Bif=u$bif:erlang:put/2 D => i_call_ext_last Bif D
-call_ext_only u==2 Bif=u$bif:erlang:put/2 => i_call_ext_only Bif
-
-call_ext u==1 Bif=u$bif:erlang:erase/1 => i_call_ext Bif
-call_ext_last u==1 Bif=u$bif:erlang:erase/1 D => i_call_ext_last Bif D
-call_ext_only u==1 Bif=u$bif:erlang:erase/1 => i_call_ext_only Bif
+# Expands into call_light_bif(_only)/2
+call_light_bif/1
+call_light_bif_only/1
+call_light_bif_last/2
#
-# The process_info/1,2 BIF should be called like a function, to force
-# the emulator to set c_p->current before calling it (a BIF call doesn't
-# set it).
+# The load_nif/2 BIF is an instruction.
#
-# In addition, we force the use of a non-tail-recursive call. This will ensure
-# that c_p->cp points into the function making the call.
-#
-
-call_ext u==1 Bif=u$bif:erlang:process_info/1 => i_call_ext Bif
-call_ext_last u==1 Bif=u$bif:erlang:process_info/1 D => i_call_ext Bif | deallocate_return D
-call_ext_only Ar=u==1 Bif=u$bif:erlang:process_info/1 => allocate u Ar | i_call_ext Bif | deallocate_return u
-call_ext u==2 Bif=u$bif:erlang:process_info/2 => i_call_ext Bif
-call_ext_last u==2 Bif=u$bif:erlang:process_info/2 D => i_call_ext Bif | deallocate_return D
-call_ext_only Ar=u==2 Bif=u$bif:erlang:process_info/2 => allocate u Ar | i_call_ext Bif | deallocate_return u
-
-#
-# load_nif/2 also needs to know calling function like process_info
-#
-call_ext u==2 Bif=u$bif:erlang:load_nif/2 => i_call_ext Bif
-call_ext_last u==2 Bif=u$bif:erlang:load_nif/2 D => i_call_ext Bif | deallocate_return D
-call_ext_only Ar=u==2 Bif=u$bif:erlang:load_nif/2 => allocate u Ar | i_call_ext Bif | deallocate_return u
+call_ext u==2 u$func:erlang:load_nif/2 => i_load_nif
+call_ext_last u==2 u$func:erlang:load_nif/2 D => i_load_nif | deallocate_return D
+call_ext_only u==2 u$func:erlang:load_nif/2 => i_load_nif | return
+%cold
+i_load_nif
+%hot
#
# apply/2 is an instruction, not a BIF.
@@ -849,33 +834,6 @@ call_ext_last u==3 u$bif:erlang:apply/3 D => i_apply_last D
call_ext_only u==3 u$bif:erlang:apply/3 => i_apply_only
#
-# The exit/1 and throw/1 BIFs never execute the instruction following them;
-# thus there is no need to generate any return instruction.
-#
-
-call_ext_last u==1 Bif=u$bif:erlang:exit/1 D => call_bif Bif
-call_ext_last u==1 Bif=u$bif:erlang:throw/1 D => call_bif Bif
-
-call_ext_only u==1 Bif=u$bif:erlang:exit/1 => call_bif Bif
-call_ext_only u==1 Bif=u$bif:erlang:throw/1 => call_bif Bif
-
-#
-# The error/1 and error/2 BIFs never execute the instruction following them;
-# thus there is no need to generate any return instruction.
-# However, they generate stack backtraces, so if the call instruction
-# is call_ext_only/2 instruction, we explicitly do an allocate/2 to store
-# the continuation pointer on the stack.
-#
-
-call_ext_last u==1 Bif=u$bif:erlang:error/1 D => call_bif Bif
-call_ext_last u==2 Bif=u$bif:erlang:error/2 D => call_bif Bif
-
-call_ext_only Ar=u==1 Bif=u$bif:erlang:error/1 => \
- allocate u Ar | call_bif Bif
-call_ext_only Ar=u==2 Bif=u$bif:erlang:error/2 => \
- allocate u Ar | call_bif Bif
-
-#
# The yield/0 BIF is an instruction
#
@@ -987,17 +945,24 @@ call_ext_only u==0 u$func:os:perf_counter/0 => \
i_perf_counter | return
#
-# The general case for BIFs that have no special instructions.
-# A BIF used in the tail must be followed by a return instruction.
+# BIFs like process_info/1,2 require up-to-date information about the current
+# emulator state, which the ordinary call_light_bif instruction doesn't save.
#
-# To make trapping and stack backtraces work correctly, we make sure that
-# the continuation pointer is always stored on the stack.
-call_ext u Bif=u$is_bif => call_bif Bif
+call_ext u Bif=u$is_bif | is_heavy_bif(Bif) => \
+ i_call_ext Bif
+call_ext_last u Bif=u$is_bif D | is_heavy_bif(Bif) => \
+ i_call_ext Bif | deallocate_return D
+call_ext_only Ar=u Bif=u$is_bif | is_heavy_bif(Bif) => \
+ allocate u Ar | i_call_ext Bif | deallocate_return u
-call_ext_last u Bif=u$is_bif D => deallocate D | call_bif_only Bif
+#
+# The general case for BIFs that have no special requirements.
+#
-call_ext_only Ar=u Bif=u$is_bif => call_bif_only Bif
+call_ext u Bif=u$is_bif => call_light_bif Bif
+call_ext_last u Bif=u$is_bif D => call_light_bif_last Bif D
+call_ext_only Ar=u Bif=u$is_bif => call_light_bif_only Bif
#
# Any remaining calls are calls to Erlang functions, not BIFs.
@@ -1022,14 +987,32 @@ i_apply_fun
i_apply_fun_last Q
i_apply_fun_only
+#
+# When a BIF is traced, these instructions make a body call through the export
+# entry instead of calling the BIF directly (setting up a temporary stack frame
+# if needed). We therefore retain the stack frame in call_light_bif_last, and
+# add a deallocate_return after call_light_bif_only to remove the temporary
+# stack frame before returning.
+#
+
+call_light_bif Bif=u$is_bif => \
+ call_light_bif Bif Bif
+
+call_light_bif_last Bif=u$is_bif D => \
+ call_light_bif Bif Bif | deallocate_return D
+
+call_light_bif_only Bif=u$is_bif => \
+ call_light_bif_only Bif Bif | deallocate_return u
+
+call_light_bif b e
+call_light_bif_only b e
+
%cold
-i_hibernate
+i_hibernate
i_perf_counter
-%hot
-call_bif e
-call_bif_only e
+%hot
#
# Calls to non-building and guard BIFs.
diff --git a/erts/emulator/beam/register.c b/erts/emulator/beam/register.c
index c7e02c6d48..8e44b527a2 100644
--- a/erts/emulator/beam/register.c
+++ b/erts/emulator/beam/register.c
@@ -265,10 +265,8 @@ Eterm
erts_whereis_name_to_id(Process *c_p, Eterm name)
{
Eterm res = am_undefined;
- HashValue hval;
- int ix;
- HashBucket* b;
ErtsProcLocks c_p_locks = 0;
+ RegProc *rp, tmpl;
if (c_p) {
c_p_locks = ERTS_PROC_LOCK_MAIN;
ERTS_CHK_HAVE_ONLY_MAIN_PROC_LOCK(c_p);
@@ -278,29 +276,14 @@ erts_whereis_name_to_id(Process *c_p, Eterm name)
if (c_p && !c_p_locks)
erts_proc_lock(c_p, ERTS_PROC_LOCK_MAIN);
- hval = REG_HASH(name);
- ix = hval % process_reg.size;
- b = process_reg.bucket[ix];
+ tmpl.name = name;
+ rp = hash_fetch(&process_reg, &tmpl, (H_FUN)reg_hash, (HCMP_FUN)reg_cmp);
- /*
- * Note: We have inlined the code from hash.c for speed.
- */
-
- while (b) {
- RegProc* rp = (RegProc *) b;
- if (rp->name == name) {
- /*
- * SMP NOTE: No need to lock registered entity since it cannot
- * be removed without acquiring write reg lock and id on entity
- * is read only.
- */
- if (rp->p)
- res = rp->p->common.id;
- else if (rp->pt)
- res = rp->pt->common.id;
- break;
- }
- b = b->next;
+ if (rp) {
+ if (rp->p)
+ res = rp->p->common.id;
+ else if (rp->pt)
+ res = rp->pt->common.id;
}
reg_read_unlock();
@@ -321,10 +304,7 @@ erts_whereis_name(Process *c_p,
Port** port,
int lock_port)
{
- RegProc* rp = NULL;
- HashValue hval;
- int ix;
- HashBucket* b;
+ RegProc* rp = NULL, tmpl;
ErtsProcLocks current_c_p_locks;
Port *pending_port = NULL;
@@ -342,21 +322,8 @@ erts_whereis_name(Process *c_p,
* - current_c_p_locks (either c_p_locks or 0) on c_p
*/
- hval = REG_HASH(name);
- ix = hval % process_reg.size;
- b = process_reg.bucket[ix];
-
- /*
- * Note: We have inlined the code from hash.c for speed.
- */
-
- while (b) {
- if (((RegProc *) b)->name == name) {
- rp = (RegProc *) b;
- break;
- }
- b = b->next;
- }
+ tmpl.name = name;
+ rp = hash_fetch(&process_reg, &tmpl, (H_FUN)reg_hash, (HCMP_FUN)reg_cmp);
if (proc) {
if (!rp)
@@ -564,18 +531,6 @@ int erts_unregister_name(Process *c_p,
return res;
}
-int process_reg_size(void)
-{
- int size;
- int lock = !ERTS_IS_CRASH_DUMPING;
- if (lock)
- reg_read_lock();
- size = process_reg.size;
- if (lock)
- reg_read_unlock();
- return size;
-}
-
int process_reg_sz(void)
{
int sz;
@@ -592,15 +547,24 @@ int process_reg_sz(void)
#include "bif.h"
+struct registered_foreach_arg {
+ Eterm res;
+ Eterm *hp;
+};
+
+static void
+registered_foreach(RegProc *reg, struct registered_foreach_arg *arg)
+{
+ arg->res = CONS(arg->hp, reg->name, arg->res);
+ arg->hp += 2;
+}
+
/* return a list of the registered processes */
BIF_RETTYPE registered_0(BIF_ALIST_0)
{
- int i;
- Eterm res;
+ struct registered_foreach_arg arg;
Uint need;
- Eterm* hp;
- HashBucket **bucket;
ErtsProcLocks proc_locks = ERTS_PROC_LOCK_MAIN;
ERTS_CHK_HAVE_ONLY_MAIN_PROC_LOCK(BIF_P);
@@ -608,41 +572,21 @@ BIF_RETTYPE registered_0(BIF_ALIST_0)
if (!proc_locks)
erts_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
- bucket = process_reg.bucket;
-
- /* work out how much heap we need & maybe garb, by scanning through
- the registered process table */
- need = 0;
- for (i = 0; i < process_reg.size; i++) {
- HashBucket *b = bucket[i];
- while (b != NULL) {
- need += 2;
- b = b->next;
- }
- }
+ /* work out how much heap we need */
+ need = process_reg.nobjs * 2;
if (need == 0) {
reg_read_unlock();
BIF_RET(NIL);
}
- hp = HAlloc(BIF_P, need);
-
- /* scan through again and make the list */
- res = NIL;
+ /* scan through again and make the list */
+ arg.hp = HAlloc(BIF_P, need);
+ arg.res = NIL;
- for (i = 0; i < process_reg.size; i++) {
- HashBucket *b = bucket[i];
- while (b != NULL) {
- RegProc *reg = (RegProc *) b;
-
- res = CONS(hp, reg->name, res);
- hp += 2;
- b = b->next;
- }
- }
+ hash_foreach(&process_reg, (HFOREACH_FUN)registered_foreach, &arg);
reg_read_unlock();
- BIF_RET(res);
+ BIF_RET(arg.res);
}
diff --git a/erts/emulator/beam/register.h b/erts/emulator/beam/register.h
index 27a314ca78..c77bd03653 100644
--- a/erts/emulator/beam/register.h
+++ b/erts/emulator/beam/register.h
@@ -41,7 +41,6 @@ typedef struct reg_proc
Eterm name; /* Atom name */
} RegProc;
-int process_reg_size(void);
int process_reg_sz(void);
void init_register_table(void);
void register_info(fmtfn_t, void *);
diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h
index db07512cf7..8a59b61b63 100644
--- a/erts/emulator/beam/sys.h
+++ b/erts/emulator/beam/sys.h
@@ -673,7 +673,16 @@ typedef struct preload {
*/
typedef Eterm ErtsTracer;
-#include "erl_osenv.h"
+
+/*
+ * This structure contains the rb tree for the erlang osenv copy
+ * see erl_osenv.h for more details.
+ */
+typedef struct __erts_osenv_t {
+ struct __env_rbtnode_t *tree;
+ int variable_count;
+ int content_size;
+} erts_osenv_t;
/*
* This structure contains options to all built in drivers.
diff --git a/erts/emulator/beam/trace_instrs.tab b/erts/emulator/beam/trace_instrs.tab
index 3eee81c053..9f22587f96 100644
--- a/erts/emulator/beam/trace_instrs.tab
+++ b/erts/emulator/beam/trace_instrs.tab
@@ -20,16 +20,15 @@
//
return_trace() {
- ErtsCodeMFA* mfa = (ErtsCodeMFA *)(E[0]);
+ ErtsCodeMFA* mfa = (ErtsCodeMFA *)(E[1]);
SWAPOUT; /* Needed for shared heap */
ERTS_UNREQ_PROC_MAIN_LOCK(c_p);
- erts_trace_return(c_p, mfa, r(0), ERTS_TRACER_FROM_ETERM(E+1)/* tracer */);
+ erts_trace_return(c_p, mfa, r(0), ERTS_TRACER_FROM_ETERM(E+2)/* tracer */);
ERTS_REQ_PROC_MAIN_LOCK(c_p);
SWAPIN;
- c_p->cp = NULL;
- SET_I((BeamInstr *) cp_val(E[2]));
E += 3;
+ $RETURN();
Goto(*I);
//| -no_next
}
@@ -45,13 +44,12 @@ i_generic_breakpoint() {
}
i_return_time_trace() {
- BeamInstr *pc = (BeamInstr *) (UWord) E[0];
+ BeamInstr *pc = (BeamInstr *) (UWord) E[1];
SWAPOUT;
erts_trace_time_return(c_p, erts_code_to_codeinfo(pc));
SWAPIN;
- c_p->cp = NULL;
- SET_I((BeamInstr *) cp_val(E[1]));
E += 2;
+ $RETURN();
Goto(*I);
//| -no_next
}
@@ -59,8 +57,10 @@ i_return_time_trace() {
i_return_to_trace() {
if (IS_TRACED_FL(c_p, F_TRACE_RETURN_TO)) {
Uint *cpp = (Uint*) E;
+ while (is_not_CP(*cpp)) {
+ cpp++;
+ }
for(;;) {
- ASSERT(is_CP(*cpp));
if (IsOpCode(*cp_val(*cpp), return_trace)) {
do
++cpp;
@@ -80,9 +80,8 @@ i_return_to_trace() {
ERTS_REQ_PROC_MAIN_LOCK(c_p);
SWAPIN;
}
- c_p->cp = NULL;
- SET_I((BeamInstr *) cp_val(E[0]));
E += 1;
+ $RETURN();
Goto(*I);
//| -no_next
}
diff --git a/erts/emulator/hipe/hipe_debug.c b/erts/emulator/hipe/hipe_debug.c
index 138e4f7da3..2e34cfac59 100644
--- a/erts/emulator/hipe/hipe_debug.c
+++ b/erts/emulator/hipe/hipe_debug.c
@@ -232,7 +232,6 @@ void hipe_print_pcb(Process *p)
U("intial.fun ", u.initial.function);
U("intial.ari ", u.initial.arity);
U("current ", current);
- P("cp ", cp);
P("i ", i);
U("catches ", catches);
U("arity ", arity);
diff --git a/erts/emulator/hipe/hipe_instrs.tab b/erts/emulator/hipe/hipe_instrs.tab
index a01baebddf..8aa8544b2a 100644
--- a/erts/emulator/hipe/hipe_instrs.tab
+++ b/erts/emulator/hipe/hipe_instrs.tab
@@ -86,15 +86,14 @@ hipe_trap.post() {
switch( c_p->def_arg_reg[3] ) {
case HIPE_MODE_SWITCH_RES_RETURN:
ASSERT(is_value(reg[0]));
- SET_I(c_p->cp);
- c_p->cp = 0;
+ $RETURN();
Goto(*I);
case HIPE_MODE_SWITCH_RES_CALL_EXPORTED:
c_p->i = c_p->hipe.u.callee_exp->addressv[erts_active_code_ix()];
/*fall through*/
case HIPE_MODE_SWITCH_RES_CALL_BEAM:
SET_I(c_p->i);
- Dispatch();
+ $DISPATCH();
case HIPE_MODE_SWITCH_RES_CALL_CLOSURE:
/* This can be used to call any function value, but currently
it's only used to call closures referring to unloaded
@@ -105,13 +104,11 @@ hipe_trap.post() {
next = call_fun(c_p, c_p->arity - 1, reg, THE_NON_VALUE);
HEAVY_SWAPIN;
if (next != NULL) {
- SET_I(next);
- Dispatchfun();
+ $DISPATCH_FUN(next);
}
goto find_func_info;
}
case HIPE_MODE_SWITCH_RES_THROW:
- c_p->cp = NULL;
I = handle_error(c_p, I, reg, NULL);
goto post_error_handling;
default:
diff --git a/erts/emulator/hipe/hipe_mode_switch.c b/erts/emulator/hipe/hipe_mode_switch.c
index 052cf9c263..863c5e6d44 100644
--- a/erts/emulator/hipe/hipe_mode_switch.c
+++ b/erts/emulator/hipe/hipe_mode_switch.c
@@ -202,15 +202,13 @@ hipe_push_beam_trap_frame(Process *p, Eterm reg[], unsigned arity)
p->stop -= 2;
p->stop[1] = hipe_beam_catch_throw;
}
- p->stop[0] = make_cp(p->cp);
+ p->stop[0] = (BeamInstr) hipe_beam_pc_return;
++p->catches;
- p->cp = hipe_beam_pc_return;
}
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;
}
@@ -263,7 +261,7 @@ Process *hipe_mode_switch(Process *p, unsigned cmd, Eterm reg[])
unsigned arity = cmd >> 8;
/* p->hipe.u.ncallee set in beam_emu */
- if (p->cp == hipe_beam_pc_return) {
+ if (cp_val(p->stop[0]) == hipe_beam_pc_return) {
/* Native called BEAM, which now tailcalls native. */
hipe_pop_beam_trap_frame(p);
result = hipe_tailcall_to_native(p, arity, reg);
@@ -292,7 +290,7 @@ Process *hipe_mode_switch(Process *p, unsigned cmd, Eterm reg[])
/* just like a normal call from now on */
/* p->hipe.u.ncallee set in beam_emu */
- if (p->cp == hipe_beam_pc_return) {
+ if (cp_val(p->stop[0]) == hipe_beam_pc_return) {
/* Native called BEAM, which now tailcalls native. */
hipe_pop_beam_trap_frame(p);
result = hipe_tailcall_to_native(p, arity, reg);
diff --git a/erts/emulator/hipe/hipe_x86_signal.c b/erts/emulator/hipe/hipe_x86_signal.c
index d3b6933155..df8e1825bc 100644
--- a/erts/emulator/hipe/hipe_x86_signal.c
+++ b/erts/emulator/hipe/hipe_x86_signal.c
@@ -159,19 +159,9 @@
#if !(defined(__GLIBC__) || defined(__DARWIN__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined(__sun__))
/*
- * Unknown libc -- assume musl. Note: musl deliberately does not provide a musl-specific
- * feature test macro, so we cannot check for it.
- *
- * sigaction is a weak alias for __sigaction, which is a wrapper for __libc_sigaction.
- * There are libc-internal calls to __libc_sigaction which install handlers, so we must
- * override __libc_sigaction rather than __sigaction.
+ * Unknown libc -- assume musl, which does not allow safe signals
*/
-#define NEXT_SIGACTION "__libc_sigaction"
-#define LIBC_SIGACTION __libc_sigaction
-#define OVERRIDE_SIGACTION
-#ifndef _NSIG
-#define _NSIG NSIG
-#endif
+#error "HiPE does not work without a libc that can guarantee that sigaltstack works"
#endif /* !(__GLIBC__ || __DARWIN__ || __NetBSD__ || __FreeBSD__ || __sun__) */
#if defined(NEXT_SIGACTION)
diff --git a/erts/emulator/internal_doc/beam_makeops.md b/erts/emulator/internal_doc/beam_makeops.md
index 2880099b70..267af78412 100644
--- a/erts/emulator/internal_doc/beam_makeops.md
+++ b/erts/emulator/internal_doc/beam_makeops.md
@@ -1457,26 +1457,26 @@ all instructions. It expands to the address of the next instruction.
Here is an example:
i_call(CallDest) {
- SET_CP(c_p, $NEXT_INSTRUCTION);
+ //| -no_next
+ $SAVE_CONTINUATION_POINTER($NEXT_INSTRUCTION);
$DISPATCH_REL($CallDest);
}
-When calling a function, the return address is first stored in `c_p->cp`
-(using the `SET_CP()` macro defined in `beam_emu.c`), and then control is
+When calling a function, the return address is first stored in `E[0]`
+(using the `$SAVE_CONTINUATION_POINTER()` macro), and then control is
transferred to the callee. Here is the generated code:
OpCase(i_call_f):
{
- SET_CP(c_p, I+1);
- ASSERT(VALID_INSTR(*(I + (fb(BeamExtraData(I[0]))) + 0)));
- I += fb(BeamExtraData(I[0])) + 0;;
- DTRACE_LOCAL_CALL(c_p, erts_code_to_codemfa(I));
- Dispatch();;
+ ASSERT(VALID_INSTR(*(I+2)));
+ *E = (BeamInstr) (I+2);;
+
+ /* ... dispatch code intentionally left out ... */
}
-We can see that that `$NEXT_INSTRUCTION` has been expanded to `I+1`.
+We can see that that `$NEXT_INSTRUCTION` has been expanded to `I+2`.
That makes sense since the size of the `i_call_f/1` instruction is
-one word.
+two words.
##### The IP_ADJUSTMENT pre-bound variable #####
diff --git a/erts/emulator/nifs/common/prim_file_nif.c b/erts/emulator/nifs/common/prim_file_nif.c
index 9e9a14844e..5c5e9a2d30 100644
--- a/erts/emulator/nifs/common/prim_file_nif.c
+++ b/erts/emulator/nifs/common/prim_file_nif.c
@@ -162,6 +162,7 @@ WRAP_FILE_HANDLE_EXPORT(allocate_nif)
WRAP_FILE_HANDLE_EXPORT(advise_nif)
WRAP_FILE_HANDLE_EXPORT(get_handle_nif)
WRAP_FILE_HANDLE_EXPORT(ipread_s32bu_p32bu_nif)
+WRAP_FILE_HANDLE_EXPORT(read_handle_info_nif)
static ErlNifFunc nif_funcs[] = {
/* File handle ops */
@@ -176,6 +177,7 @@ static ErlNifFunc nif_funcs[] = {
{"truncate_nif", 1, truncate_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
{"allocate_nif", 3, allocate_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
{"advise_nif", 4, advise_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
+ {"read_handle_info_nif", 1, read_handle_info_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
/* Filesystem ops */
{"make_hard_link_nif", 2, make_hard_link_nif, ERL_NIF_DIRTY_JOB_IO_BOUND},
@@ -896,6 +898,26 @@ static ERL_NIF_TERM get_handle_nif_impl(efile_data_t *d, ErlNifEnv *env, int arg
return efile_get_handle(env, d);
}
+static ERL_NIF_TERM build_file_info(ErlNifEnv *env, efile_fileinfo_t *info) {
+ /* #file_info as declared in file.hrl */
+ return enif_make_tuple(env, 14,
+ am_file_info,
+ enif_make_uint64(env, info->size),
+ efile_filetype_to_atom(info->type),
+ efile_access_to_atom(info->access),
+ enif_make_int64(env, MAX(EFILE_MIN_FILETIME, info->a_time)),
+ enif_make_int64(env, MAX(EFILE_MIN_FILETIME, info->m_time)),
+ enif_make_int64(env, MAX(EFILE_MIN_FILETIME, info->c_time)),
+ enif_make_uint(env, info->mode),
+ enif_make_uint(env, info->links),
+ enif_make_uint(env, info->major_device),
+ enif_make_uint(env, info->minor_device),
+ enif_make_uint(env, info->inode),
+ enif_make_uint(env, info->uid),
+ enif_make_uint(env, info->gid)
+ );
+}
+
static ERL_NIF_TERM read_info_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
posix_errno_t posix_errno;
@@ -914,23 +936,20 @@ static ERL_NIF_TERM read_info_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM a
return posix_error_to_tuple(env, posix_errno);
}
- /* #file_info as declared in file.hrl */
- return enif_make_tuple(env, 14,
- am_file_info,
- enif_make_uint64(env, info.size),
- efile_filetype_to_atom(info.type),
- efile_access_to_atom(info.access),
- enif_make_int64(env, MAX(EFILE_MIN_FILETIME, info.a_time)),
- enif_make_int64(env, MAX(EFILE_MIN_FILETIME, info.m_time)),
- enif_make_int64(env, MAX(EFILE_MIN_FILETIME, info.c_time)),
- enif_make_uint(env, info.mode),
- enif_make_uint(env, info.links),
- enif_make_uint(env, info.major_device),
- enif_make_uint(env, info.minor_device),
- enif_make_uint(env, info.inode),
- enif_make_uint(env, info.uid),
- enif_make_uint(env, info.gid)
- );
+ return build_file_info(env, &info);
+}
+
+static ERL_NIF_TERM read_handle_info_nif_impl(efile_data_t *d, ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
+ posix_errno_t posix_errno;
+ efile_fileinfo_t info = {0};
+
+ ASSERT(argc == 0);
+
+ if((posix_errno = efile_read_handle_info(d, &info))) {
+ return posix_error_to_tuple(env, posix_errno);
+ }
+
+ return build_file_info(env, &info);
}
static ERL_NIF_TERM set_permissions_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {
diff --git a/erts/emulator/nifs/common/prim_file_nif.h b/erts/emulator/nifs/common/prim_file_nif.h
index 020714a03b..28c1ea9d00 100644
--- a/erts/emulator/nifs/common/prim_file_nif.h
+++ b/erts/emulator/nifs/common/prim_file_nif.h
@@ -170,6 +170,7 @@ int efile_close(efile_data_t *d, posix_errno_t *error);
/* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
posix_errno_t efile_read_info(const efile_path_t *path, int follow_link, efile_fileinfo_t *result);
+posix_errno_t efile_read_handle_info(efile_data_t *d, efile_fileinfo_t *result);
/** @brief Sets the file times to the given values. Refer to efile_fileinfo_t
* for a description of each. */
diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c
index 7ab712530c..881a9c7ccd 100644
--- a/erts/emulator/nifs/common/socket_nif.c
+++ b/erts/emulator/nifs/common/socket_nif.c
@@ -24,7 +24,7 @@
* The first function is called 'nif_<something>', e.g. nif_open.
* This does the initial validation and argument processing and then
* calls the function that does the actual work. This is called
- * 'n<something>'.
+ * 'esock_<something>'.
* ----------------------------------------------------------------------
*
*
@@ -354,11 +354,11 @@ static void (*esock_sctp_freepaddrs)(struct sockaddr *addrs) = NULL;
/* Debug stuff... */
-#define SOCKET_GLOBAL_DEBUG_DEFAULT FALSE
-#define SOCKET_DEBUG_DEFAULT FALSE
+#define ESOCK_GLOBAL_DEBUG_DEFAULT FALSE
+#define ESOCK_DEBUG_DEFAULT FALSE
/* Counters and stuff (Don't know where to sent this stuff anyway) */
-#define SOCKET_NIF_IOW_DEFAULT FALSE
+#define ESOCK_NIF_IOW_DEFAULT FALSE
@@ -398,10 +398,10 @@ static void (*esock_sctp_freepaddrs)(struct sockaddr *addrs) = NULL;
#if defined(TCP_CA_NAME_MAX)
-#define SOCKET_OPT_TCP_CONGESTION_NAME_MAX TCP_CA_NAME_MAX
+#define ESOCK_OPT_TCP_CONGESTION_NAME_MAX TCP_CA_NAME_MAX
#else
/* This is really excessive, but just in case... */
-#define SOCKET_OPT_TCP_CONGESTION_NAME_MAX 256
+#define ESOCK_OPT_TCP_CONGESTION_NAME_MAX 256
#endif
@@ -414,25 +414,25 @@ static void (*esock_sctp_freepaddrs)(struct sockaddr *addrs) = NULL;
/* *** Socket state defs *** */
-#define SOCKET_FLAG_OPEN 0x0001
-#define SOCKET_FLAG_ACTIVE 0x0004
-#define SOCKET_FLAG_LISTEN 0x0008
-#define SOCKET_FLAG_CON 0x0010
-#define SOCKET_FLAG_ACC 0x0020
-#define SOCKET_FLAG_BUSY 0x0040
-#define SOCKET_FLAG_CLOSE 0x0080
-
-#define SOCKET_STATE_CLOSED (0)
-#define SOCKET_STATE_OPEN (SOCKET_FLAG_OPEN)
-#define SOCKET_STATE_CONNECTED (SOCKET_STATE_OPEN | SOCKET_FLAG_ACTIVE)
-#define SOCKET_STATE_LISTENING (SOCKET_STATE_OPEN | SOCKET_FLAG_LISTEN)
-#define SOCKET_STATE_CONNECTING (SOCKET_STATE_OPEN | SOCKET_FLAG_CON)
-#define SOCKET_STATE_ACCEPTING (SOCKET_STATE_LISTENING | SOCKET_FLAG_ACC)
-#define SOCKET_STATE_CLOSING (SOCKET_FLAG_CLOSE)
-#define SOCKET_STATE_DTOR (0xFFFF)
+#define ESOCK_FLAG_OPEN 0x0001
+#define ESOCK_FLAG_ACTIVE 0x0004
+#define ESOCK_FLAG_LISTEN 0x0008
+#define ESOCK_FLAG_CON 0x0010
+#define ESOCK_FLAG_ACC 0x0020
+#define ESOCK_FLAG_BUSY 0x0040
+#define ESOCK_FLAG_CLOSE 0x0080
+
+#define ESOCK_STATE_CLOSED (0)
+#define ESOCK_STATE_OPEN (ESOCK_FLAG_OPEN)
+#define ESOCK_STATE_CONNECTED (ESOCK_STATE_OPEN | ESOCK_FLAG_ACTIVE)
+#define ESOCK_STATE_LISTENING (ESOCK_STATE_OPEN | ESOCK_FLAG_LISTEN)
+#define ESOCK_STATE_CONNECTING (ESOCK_STATE_OPEN | ESOCK_FLAG_CON)
+#define ESOCK_STATE_ACCEPTING (ESOCK_STATE_LISTENING | ESOCK_FLAG_ACC)
+#define ESOCK_STATE_CLOSING (ESOCK_FLAG_CLOSE)
+#define ESOCK_STATE_DTOR (0xFFFF)
#define IS_CLOSED(d) \
- ((d)->state == SOCKET_STATE_CLOSED)
+ ((d)->state == ESOCK_STATE_CLOSED)
/*
#define IS_STATE(d, f) \
@@ -440,55 +440,59 @@ static void (*esock_sctp_freepaddrs)(struct sockaddr *addrs) = NULL;
*/
#define IS_CLOSING(d) \
- (((d)->state & SOCKET_STATE_CLOSING) == SOCKET_STATE_CLOSING)
+ (((d)->state & ESOCK_STATE_CLOSING) == ESOCK_STATE_CLOSING)
#define IS_OPEN(d) \
- (((d)->state & SOCKET_FLAG_OPEN) == SOCKET_FLAG_OPEN)
+ (((d)->state & ESOCK_FLAG_OPEN) == ESOCK_FLAG_OPEN)
#define IS_CONNECTED(d) \
- (((d)->state & SOCKET_STATE_CONNECTED) == SOCKET_STATE_CONNECTED)
+ (((d)->state & ESOCK_STATE_CONNECTED) == ESOCK_STATE_CONNECTED)
#define IS_CONNECTING(d) \
- (((d)->state & SOCKET_FLAG_CON) == SOCKET_FLAG_CON)
+ (((d)->state & ESOCK_FLAG_CON) == ESOCK_FLAG_CON)
/*
#define IS_BUSY(d) \
- (((d)->state & SOCKET_FLAG_BUSY) == SOCKET_FLAG_BUSY)
+ (((d)->state & ESOCK_FLAG_BUSY) == ESOCK_FLAG_BUSY)
*/
-#define SOCKET_SEND_FLAG_CONFIRM 0
-#define SOCKET_SEND_FLAG_DONTROUTE 1
-#define SOCKET_SEND_FLAG_EOR 2
-#define SOCKET_SEND_FLAG_MORE 3
-#define SOCKET_SEND_FLAG_NOSIGNAL 4
-#define SOCKET_SEND_FLAG_OOB 5
-#define SOCKET_SEND_FLAG_LOW SOCKET_SEND_FLAG_CONFIRM
-#define SOCKET_SEND_FLAG_HIGH SOCKET_SEND_FLAG_OOB
-
-#define SOCKET_RECV_FLAG_CMSG_CLOEXEC 0
-#define SOCKET_RECV_FLAG_ERRQUEUE 1
-#define SOCKET_RECV_FLAG_OOB 2
-#define SOCKET_RECV_FLAG_PEEK 3
-#define SOCKET_RECV_FLAG_TRUNC 4
-#define SOCKET_RECV_FLAG_LOW SOCKET_RECV_FLAG_CMSG_CLOEXEC
-#define SOCKET_RECV_FLAG_HIGH SOCKET_RECV_FLAG_TRUNC
-
-#define SOCKET_RECV_BUFFER_SIZE_DEFAULT 8192
-#define SOCKET_RECV_CTRL_BUFFER_SIZE_DEFAULT 1024
-#define SOCKET_SEND_CTRL_BUFFER_SIZE_DEFAULT 1024
-
-#define VT2S(__VT__) (((__VT__) == SOCKET_OPT_VALUE_TYPE_UNSPEC) ? "unspec" : \
- (((__VT__) == SOCKET_OPT_VALUE_TYPE_INT) ? "int" : \
- ((__VT__) == SOCKET_OPT_VALUE_TYPE_BOOL) ? "bool" : \
+#define ESOCK_GET_RESOURCE(ENV, REF, RES) \
+ enif_get_resource((ENV), (REF), esocks, (RES))
+
+#define ESOCK_SEND_FLAG_CONFIRM 0
+#define ESOCK_SEND_FLAG_DONTROUTE 1
+#define ESOCK_SEND_FLAG_EOR 2
+#define ESOCK_SEND_FLAG_MORE 3
+#define ESOCK_SEND_FLAG_NOSIGNAL 4
+#define ESOCK_SEND_FLAG_OOB 5
+#define ESOCK_SEND_FLAG_LOW ESOCK_SEND_FLAG_CONFIRM
+#define ESOCK_SEND_FLAG_HIGH ESOCK_SEND_FLAG_OOB
+
+#define ESOCK_RECV_FLAG_CMSG_CLOEXEC 0
+#define ESOCK_RECV_FLAG_ERRQUEUE 1
+#define ESOCK_RECV_FLAG_OOB 2
+#define ESOCK_RECV_FLAG_PEEK 3
+#define ESOCK_RECV_FLAG_TRUNC 4
+#define ESOCK_RECV_FLAG_LOW ESOCK_RECV_FLAG_CMSG_CLOEXEC
+#define ESOCK_RECV_FLAG_HIGH ESOCK_RECV_FLAG_TRUNC
+
+#define ESOCK_RECV_BUFFER_SIZE_DEFAULT 8192
+#define ESOCK_RECV_CTRL_BUFFER_SIZE_DEFAULT 1024
+#define ESOCK_SEND_CTRL_BUFFER_SIZE_DEFAULT 1024
+
+#define VT2S(__VT__) (((__VT__) == ESOCK_OPT_VALUE_TYPE_UNSPEC) ? "unspec" : \
+ (((__VT__) == ESOCK_OPT_VALUE_TYPE_INT) ? "int" : \
+ ((__VT__) == ESOCK_OPT_VALUE_TYPE_BOOL) ? "bool" : \
"undef"))
-#define SOCKET_OPT_VALUE_TYPE_UNSPEC 0
-#define SOCKET_OPT_VALUE_TYPE_INT 1
-#define SOCKET_OPT_VALUE_TYPE_BOOL 2
+#define ESOCK_OPT_VALUE_TYPE_UNSPEC 0
+#define ESOCK_OPT_VALUE_TYPE_INT 1
+#define ESOCK_OPT_VALUE_TYPE_BOOL 2
#define ESOCK_DESC_PATTERN_CREATED 0x03030303
#define ESOCK_DESC_PATTERN_DTOR 0xC0C0C0C0
+/*
typedef union {
struct {
// 0 = not open, 1 = open
@@ -501,15 +505,16 @@ typedef union {
unsigned int listen:2;
// unsigned int listening:1;
// unsigned int accepting:1;
- /* Room for more... */
+ / * Room for more... * /
} flags;
unsigned int field; // Make it easy to reset all flags...
} SocketState;
+*/
/*
#define IS_OPEN(d) ((d)->state.flags.open)
-#define IS_CONNECTED(d) ((d)->state.flags.connect == SOCKET_STATE_CONNECTED)
-#define IS_CONNECTING(d) ((d)->state.flags.connect == SOCKET_STATE_CONNECTING)
+#define IS_CONNECTED(d) ((d)->state.flags.connect == ESOCK_STATE_CONNECTED)
+#define IS_CONNECTING(d) ((d)->state.flags.connect == ESOCK_STATE_CONNECTING)
*/
@@ -520,151 +525,151 @@ typedef union {
*/
/* domain */
-#define SOCKET_DOMAIN_LOCAL 1
-#define SOCKET_DOMAIN_INET 2
-#define SOCKET_DOMAIN_INET6 3
+#define ESOCK_DOMAIN_LOCAL 1
+#define ESOCK_DOMAIN_INET 2
+#define ESOCK_DOMAIN_INET6 3
/* type */
-#define SOCKET_TYPE_STREAM 1
-#define SOCKET_TYPE_DGRAM 2
-#define SOCKET_TYPE_RAW 3
-// #define SOCKET_TYPE_RDM 4
-#define SOCKET_TYPE_SEQPACKET 5
+#define ESOCK_TYPE_STREAM 1
+#define ESOCK_TYPE_DGRAM 2
+#define ESOCK_TYPE_RAW 3
+// #define ESOCK_TYPE_RDM 4
+#define ESOCK_TYPE_SEQPACKET 5
/* protocol */
-#define SOCKET_PROTOCOL_DEFAULT 0
-#define SOCKET_PROTOCOL_IP 1
-#define SOCKET_PROTOCOL_TCP 2
-#define SOCKET_PROTOCOL_UDP 3
-#define SOCKET_PROTOCOL_SCTP 4
-#define SOCKET_PROTOCOL_ICMP 5
-#define SOCKET_PROTOCOL_IGMP 6
+#define ESOCK_PROTOCOL_DEFAULT 0
+#define ESOCK_PROTOCOL_IP 1
+#define ESOCK_PROTOCOL_TCP 2
+#define ESOCK_PROTOCOL_UDP 3
+#define ESOCK_PROTOCOL_SCTP 4
+#define ESOCK_PROTOCOL_ICMP 5
+#define ESOCK_PROTOCOL_IGMP 6
/* shutdown how */
-#define SOCKET_SHUTDOWN_HOW_RD 0
-#define SOCKET_SHUTDOWN_HOW_WR 1
-#define SOCKET_SHUTDOWN_HOW_RDWR 2
-
-
-#define SOCKET_OPT_LEVEL_OTP 0
-#define SOCKET_OPT_LEVEL_SOCKET 1
-#define SOCKET_OPT_LEVEL_IP 2
-#define SOCKET_OPT_LEVEL_IPV6 3
-#define SOCKET_OPT_LEVEL_TCP 4
-#define SOCKET_OPT_LEVEL_UDP 5
-#define SOCKET_OPT_LEVEL_SCTP 6
-
-#define SOCKET_OPT_OTP_DEBUG 1
-#define SOCKET_OPT_OTP_IOW 2
-#define SOCKET_OPT_OTP_CTRL_PROC 3
-#define SOCKET_OPT_OTP_RCVBUF 4
-#define SOCKET_OPT_OTP_RCVCTRLBUF 6
-#define SOCKET_OPT_OTP_SNDCTRLBUF 7
-#define SOCKET_OPT_OTP_FD 8
-#define SOCKET_OPT_OTP_DOMAIN 0xFF01 // INTERNAL AND ONLY GET
-#define SOCKET_OPT_OTP_TYPE 0xFF02 // INTERNAL AND ONLY GET
-#define SOCKET_OPT_OTP_PROTOCOL 0xFF03 // INTERNAL AND ONLY GET
-
-#define SOCKET_OPT_SOCK_ACCEPTCONN 1
-#define SOCKET_OPT_SOCK_BINDTODEVICE 3
-#define SOCKET_OPT_SOCK_BROADCAST 4
-#define SOCKET_OPT_SOCK_DEBUG 6
-#define SOCKET_OPT_SOCK_DOMAIN 7
-#define SOCKET_OPT_SOCK_DONTROUTE 8
-#define SOCKET_OPT_SOCK_KEEPALIVE 10
-#define SOCKET_OPT_SOCK_LINGER 11
-#define SOCKET_OPT_SOCK_OOBINLINE 13
-#define SOCKET_OPT_SOCK_PEEK_OFF 15
-#define SOCKET_OPT_SOCK_PRIORITY 17
-#define SOCKET_OPT_SOCK_PROTOCOL 18
-#define SOCKET_OPT_SOCK_RCVBUF 19
-#define SOCKET_OPT_SOCK_RCVLOWAT 21
-#define SOCKET_OPT_SOCK_RCVTIMEO 22
-#define SOCKET_OPT_SOCK_REUSEADDR 23
-#define SOCKET_OPT_SOCK_REUSEPORT 24
-#define SOCKET_OPT_SOCK_SNDBUF 27
-#define SOCKET_OPT_SOCK_SNDLOWAT 29
-#define SOCKET_OPT_SOCK_SNDTIMEO 30
-#define SOCKET_OPT_SOCK_TIMESTAMP 31
-#define SOCKET_OPT_SOCK_TYPE 32
-
-#define SOCKET_OPT_IP_ADD_MEMBERSHIP 1
-#define SOCKET_OPT_IP_ADD_SOURCE_MEMBERSHIP 2
-#define SOCKET_OPT_IP_BLOCK_SOURCE 3
-#define SOCKET_OPT_IP_DROP_MEMBERSHIP 5
-#define SOCKET_OPT_IP_DROP_SOURCE_MEMBERSHIP 6
-#define SOCKET_OPT_IP_FREEBIND 7
-#define SOCKET_OPT_IP_HDRINCL 8
-#define SOCKET_OPT_IP_MINTTL 9
-#define SOCKET_OPT_IP_MSFILTER 10
-#define SOCKET_OPT_IP_MTU 11
-#define SOCKET_OPT_IP_MTU_DISCOVER 12
-#define SOCKET_OPT_IP_MULTICAST_ALL 13
-#define SOCKET_OPT_IP_MULTICAST_IF 14
-#define SOCKET_OPT_IP_MULTICAST_LOOP 15
-#define SOCKET_OPT_IP_MULTICAST_TTL 16
-#define SOCKET_OPT_IP_NODEFRAG 17
-#define SOCKET_OPT_IP_PKTINFO 19
-#define SOCKET_OPT_IP_RECVDSTADDR 20
-#define SOCKET_OPT_IP_RECVERR 21
-#define SOCKET_OPT_IP_RECVIF 22
-#define SOCKET_OPT_IP_RECVOPTS 23
-#define SOCKET_OPT_IP_RECVORIGDSTADDR 24
-#define SOCKET_OPT_IP_RECVTOS 25
-#define SOCKET_OPT_IP_RECVTTL 26
-#define SOCKET_OPT_IP_RETOPTS 27
-#define SOCKET_OPT_IP_ROUTER_ALERT 28
-#define SOCKET_OPT_IP_SENDSRCADDR 29 // Same as IP_RECVDSTADDR?
-#define SOCKET_OPT_IP_TOS 30
-#define SOCKET_OPT_IP_TRANSPARENT 31
-#define SOCKET_OPT_IP_TTL 32
-#define SOCKET_OPT_IP_UNBLOCK_SOURCE 33
-
-#define SOCKET_OPT_IPV6_ADDRFORM 1
-#define SOCKET_OPT_IPV6_ADD_MEMBERSHIP 2
-#define SOCKET_OPT_IPV6_AUTHHDR 3
-#define SOCKET_OPT_IPV6_DROP_MEMBERSHIP 6
-#define SOCKET_OPT_IPV6_DSTOPTS 7
-#define SOCKET_OPT_IPV6_FLOWINFO 11
-#define SOCKET_OPT_IPV6_HOPLIMIT 12
-#define SOCKET_OPT_IPV6_HOPOPTS 13
-#define SOCKET_OPT_IPV6_MTU 17
-#define SOCKET_OPT_IPV6_MTU_DISCOVER 18
-#define SOCKET_OPT_IPV6_MULTICAST_HOPS 19
-#define SOCKET_OPT_IPV6_MULTICAST_IF 20
-#define SOCKET_OPT_IPV6_MULTICAST_LOOP 21
-#define SOCKET_OPT_IPV6_RECVERR 24
-#define SOCKET_OPT_IPV6_RECVPKTINFO 25 // PKTINFO on FreeBSD
-#define SOCKET_OPT_IPV6_ROUTER_ALERT 27
-#define SOCKET_OPT_IPV6_RTHDR 28
-#define SOCKET_OPT_IPV6_UNICAST_HOPS 30
-#define SOCKET_OPT_IPV6_V6ONLY 32
-
-#define SOCKET_OPT_TCP_CONGESTION 1
-#define SOCKET_OPT_TCP_CORK 2
-#define SOCKET_OPT_TCP_MAXSEG 7
-#define SOCKET_OPT_TCP_NODELAY 9
-
-#define SOCKET_OPT_UDP_CORK 1
-
-#define SOCKET_OPT_SCTP_ASSOCINFO 2
-#define SOCKET_OPT_SCTP_AUTOCLOSE 8
-#define SOCKET_OPT_SCTP_DISABLE_FRAGMENTS 12
-#define SOCKET_OPT_SCTP_EVENTS 14
-#define SOCKET_OPT_SCTP_INITMSG 18
-#define SOCKET_OPT_SCTP_MAXSEG 21
-#define SOCKET_OPT_SCTP_NODELAY 23
-#define SOCKET_OPT_SCTP_RTOINFO 29
+#define ESOCK_SHUTDOWN_HOW_RD 0
+#define ESOCK_SHUTDOWN_HOW_WR 1
+#define ESOCK_SHUTDOWN_HOW_RDWR 2
+
+
+#define ESOCK_OPT_LEVEL_OTP 0
+#define ESOCK_OPT_LEVEL_SOCKET 1
+#define ESOCK_OPT_LEVEL_IP 2
+#define ESOCK_OPT_LEVEL_IPV6 3
+#define ESOCK_OPT_LEVEL_TCP 4
+#define ESOCK_OPT_LEVEL_UDP 5
+#define ESOCK_OPT_LEVEL_SCTP 6
+
+#define ESOCK_OPT_OTP_DEBUG 1
+#define ESOCK_OPT_OTP_IOW 2
+#define ESOCK_OPT_OTP_CTRL_PROC 3
+#define ESOCK_OPT_OTP_RCVBUF 4
+#define ESOCK_OPT_OTP_RCVCTRLBUF 6
+#define ESOCK_OPT_OTP_SNDCTRLBUF 7
+#define ESOCK_OPT_OTP_FD 8
+#define ESOCK_OPT_OTP_DOMAIN 0xFF01 // INTERNAL AND ONLY GET
+#define ESOCK_OPT_OTP_TYPE 0xFF02 // INTERNAL AND ONLY GET
+#define ESOCK_OPT_OTP_PROTOCOL 0xFF03 // INTERNAL AND ONLY GET
+
+#define ESOCK_OPT_SOCK_ACCEPTCONN 1
+#define ESOCK_OPT_SOCK_BINDTODEVICE 3
+#define ESOCK_OPT_SOCK_BROADCAST 4
+#define ESOCK_OPT_SOCK_DEBUG 6
+#define ESOCK_OPT_SOCK_DOMAIN 7
+#define ESOCK_OPT_SOCK_DONTROUTE 8
+#define ESOCK_OPT_SOCK_KEEPALIVE 10
+#define ESOCK_OPT_SOCK_LINGER 11
+#define ESOCK_OPT_SOCK_OOBINLINE 13
+#define ESOCK_OPT_SOCK_PEEK_OFF 15
+#define ESOCK_OPT_SOCK_PRIORITY 17
+#define ESOCK_OPT_SOCK_PROTOCOL 18
+#define ESOCK_OPT_SOCK_RCVBUF 19
+#define ESOCK_OPT_SOCK_RCVLOWAT 21
+#define ESOCK_OPT_SOCK_RCVTIMEO 22
+#define ESOCK_OPT_SOCK_REUSEADDR 23
+#define ESOCK_OPT_SOCK_REUSEPORT 24
+#define ESOCK_OPT_SOCK_SNDBUF 27
+#define ESOCK_OPT_SOCK_SNDLOWAT 29
+#define ESOCK_OPT_SOCK_SNDTIMEO 30
+#define ESOCK_OPT_SOCK_TIMESTAMP 31
+#define ESOCK_OPT_SOCK_TYPE 32
+
+#define ESOCK_OPT_IP_ADD_MEMBERSHIP 1
+#define ESOCK_OPT_IP_ADD_SOURCE_MEMBERSHIP 2
+#define ESOCK_OPT_IP_BLOCK_SOURCE 3
+#define ESOCK_OPT_IP_DROP_MEMBERSHIP 5
+#define ESOCK_OPT_IP_DROP_SOURCE_MEMBERSHIP 6
+#define ESOCK_OPT_IP_FREEBIND 7
+#define ESOCK_OPT_IP_HDRINCL 8
+#define ESOCK_OPT_IP_MINTTL 9
+#define ESOCK_OPT_IP_MSFILTER 10
+#define ESOCK_OPT_IP_MTU 11
+#define ESOCK_OPT_IP_MTU_DISCOVER 12
+#define ESOCK_OPT_IP_MULTICAST_ALL 13
+#define ESOCK_OPT_IP_MULTICAST_IF 14
+#define ESOCK_OPT_IP_MULTICAST_LOOP 15
+#define ESOCK_OPT_IP_MULTICAST_TTL 16
+#define ESOCK_OPT_IP_NODEFRAG 17
+#define ESOCK_OPT_IP_PKTINFO 19
+#define ESOCK_OPT_IP_RECVDSTADDR 20
+#define ESOCK_OPT_IP_RECVERR 21
+#define ESOCK_OPT_IP_RECVIF 22
+#define ESOCK_OPT_IP_RECVOPTS 23
+#define ESOCK_OPT_IP_RECVORIGDSTADDR 24
+#define ESOCK_OPT_IP_RECVTOS 25
+#define ESOCK_OPT_IP_RECVTTL 26
+#define ESOCK_OPT_IP_RETOPTS 27
+#define ESOCK_OPT_IP_ROUTER_ALERT 28
+#define ESOCK_OPT_IP_SENDSRCADDR 29 // Same as IP_RECVDSTADDR?
+#define ESOCK_OPT_IP_TOS 30
+#define ESOCK_OPT_IP_TRANSPARENT 31
+#define ESOCK_OPT_IP_TTL 32
+#define ESOCK_OPT_IP_UNBLOCK_SOURCE 33
+
+#define ESOCK_OPT_IPV6_ADDRFORM 1
+#define ESOCK_OPT_IPV6_ADD_MEMBERSHIP 2
+#define ESOCK_OPT_IPV6_AUTHHDR 3
+#define ESOCK_OPT_IPV6_DROP_MEMBERSHIP 6
+#define ESOCK_OPT_IPV6_DSTOPTS 7
+#define ESOCK_OPT_IPV6_FLOWINFO 11
+#define ESOCK_OPT_IPV6_HOPLIMIT 12
+#define ESOCK_OPT_IPV6_HOPOPTS 13
+#define ESOCK_OPT_IPV6_MTU 17
+#define ESOCK_OPT_IPV6_MTU_DISCOVER 18
+#define ESOCK_OPT_IPV6_MULTICAST_HOPS 19
+#define ESOCK_OPT_IPV6_MULTICAST_IF 20
+#define ESOCK_OPT_IPV6_MULTICAST_LOOP 21
+#define ESOCK_OPT_IPV6_RECVERR 24
+#define ESOCK_OPT_IPV6_RECVPKTINFO 25 // PKTINFO on FreeBSD
+#define ESOCK_OPT_IPV6_ROUTER_ALERT 27
+#define ESOCK_OPT_IPV6_RTHDR 28
+#define ESOCK_OPT_IPV6_UNICAST_HOPS 30
+#define ESOCK_OPT_IPV6_V6ONLY 32
+
+#define ESOCK_OPT_TCP_CONGESTION 1
+#define ESOCK_OPT_TCP_CORK 2
+#define ESOCK_OPT_TCP_MAXSEG 7
+#define ESOCK_OPT_TCP_NODELAY 9
+
+#define ESOCK_OPT_UDP_CORK 1
+
+#define ESOCK_OPT_SCTP_ASSOCINFO 2
+#define ESOCK_OPT_SCTP_AUTOCLOSE 8
+#define ESOCK_OPT_SCTP_DISABLE_FRAGMENTS 12
+#define ESOCK_OPT_SCTP_EVENTS 14
+#define ESOCK_OPT_SCTP_INITMSG 18
+#define ESOCK_OPT_SCTP_MAXSEG 21
+#define ESOCK_OPT_SCTP_NODELAY 23
+#define ESOCK_OPT_SCTP_RTOINFO 29
/* We should *eventually* use this instead of hard-coding the size (to 1) */
#define ESOCK_RECVMSG_IOVEC_SZ 1
-#define SOCKET_CMD_DEBUG 0x0001
+#define ESOCK_CMD_DEBUG 0x0001
-#define SOCKET_SUPPORTS_OPTIONS 0x0001
-#define SOCKET_SUPPORTS_SCTP 0x0002
-#define SOCKET_SUPPORTS_IPV6 0x0003
-#define SOCKET_SUPPORTS_LOCAL 0x0004
+#define ESOCK_SUPPORTS_OPTIONS 0x0001
+#define ESOCK_SUPPORTS_SCTP 0x0002
+#define ESOCK_SUPPORTS_IPV6 0x0003
+#define ESOCK_SUPPORTS_LOCAL 0x0004
#define ESOCK_WHICH_PROTO_ERROR -1
#define ESOCK_WHICH_PROTO_UNSUP -2
@@ -1021,10 +1026,10 @@ static BOOLEAN_T ecommand2command(ErlNifEnv* env,
ERL_NIF_TERM ecommand,
Uint16* command,
ERL_NIF_TERM* edata);
-static ERL_NIF_TERM ncommand(ErlNifEnv* env,
- Uint16 cmd,
- ERL_NIF_TERM ecdata);
-static ERL_NIF_TERM ncommand_debug(ErlNifEnv* env, ERL_NIF_TERM ecdata);
+static ERL_NIF_TERM esock_command(ErlNifEnv* env,
+ Uint16 cmd,
+ ERL_NIF_TERM ecdata);
+static ERL_NIF_TERM esock_command_debug(ErlNifEnv* env, ERL_NIF_TERM ecdata);
static ERL_NIF_TERM esock_global_info(ErlNifEnv* env);
static ERL_NIF_TERM esock_socket_info(ErlNifEnv* env,
@@ -1048,1109 +1053,1109 @@ static ERL_NIF_TERM socket_info_reqs(ErlNifEnv* env,
ESockRequestor* crp,
ESockRequestQueue* q);
-static ERL_NIF_TERM nsupports(ErlNifEnv* env, int key);
-static ERL_NIF_TERM nsupports_options(ErlNifEnv* env);
-static ERL_NIF_TERM nsupports_options_socket(ErlNifEnv* env);
-static ERL_NIF_TERM nsupports_options_ip(ErlNifEnv* env);
-static ERL_NIF_TERM nsupports_options_ipv6(ErlNifEnv* env);
-static ERL_NIF_TERM nsupports_options_tcp(ErlNifEnv* env);
-static ERL_NIF_TERM nsupports_options_udp(ErlNifEnv* env);
-static ERL_NIF_TERM nsupports_options_sctp(ErlNifEnv* env);
-static ERL_NIF_TERM nsupports_sctp(ErlNifEnv* env);
-static ERL_NIF_TERM nsupports_ipv6(ErlNifEnv* env);
-static ERL_NIF_TERM nsupports_local(ErlNifEnv* env);
-
-static ERL_NIF_TERM nopen(ErlNifEnv* env,
+static ERL_NIF_TERM esock_supports(ErlNifEnv* env, int key);
+static ERL_NIF_TERM esock_supports_options(ErlNifEnv* env);
+static ERL_NIF_TERM esock_supports_options_socket(ErlNifEnv* env);
+static ERL_NIF_TERM esock_supports_options_ip(ErlNifEnv* env);
+static ERL_NIF_TERM esock_supports_options_ipv6(ErlNifEnv* env);
+static ERL_NIF_TERM esock_supports_options_tcp(ErlNifEnv* env);
+static ERL_NIF_TERM esock_supports_options_udp(ErlNifEnv* env);
+static ERL_NIF_TERM esock_supports_options_sctp(ErlNifEnv* env);
+static ERL_NIF_TERM esock_supports_sctp(ErlNifEnv* env);
+static ERL_NIF_TERM esock_supports_ipv6(ErlNifEnv* env);
+static ERL_NIF_TERM esock_supports_local(ErlNifEnv* env);
+
+static ERL_NIF_TERM esock_open(ErlNifEnv* env,
int domain,
int type,
int protocol,
char* netns);
-static BOOLEAN_T nopen_which_protocol(SOCKET sock, int* proto);
+static BOOLEAN_T esock_open_which_protocol(SOCKET sock, int* proto);
-static ERL_NIF_TERM nbind(ErlNifEnv* env,
- ESockDescriptor* descP,
- ESockAddress* sockAddrP,
- unsigned int addrLen);
-static ERL_NIF_TERM nconnect(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef);
-static ERL_NIF_TERM nlisten(ErlNifEnv* env,
- ESockDescriptor* descP,
- int backlog);
-static ERL_NIF_TERM naccept_erts(ErlNifEnv* env,
+static ERL_NIF_TERM esock_bind(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ESockAddress* sockAddrP,
+ unsigned int addrLen);
+static ERL_NIF_TERM esock_connect(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef);
+static ERL_NIF_TERM esock_listen(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int backlog);
+static ERL_NIF_TERM esock_accept(ErlNifEnv* env,
ESockDescriptor* descP,
ERL_NIF_TERM sockRef,
ERL_NIF_TERM ref);
-static ERL_NIF_TERM naccept_listening(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef,
- ERL_NIF_TERM ref);
-static ERL_NIF_TERM naccept_listening_error(ErlNifEnv* env,
+static ERL_NIF_TERM esock_accept_listening(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM ref);
+static ERL_NIF_TERM esock_accept_listening_error(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM accRef,
+ ErlNifPid caller,
+ int save_errno);
+static ERL_NIF_TERM esock_accept_listening_accept(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ SOCKET accSock,
+ ErlNifPid caller,
+ ESockAddress* remote);
+static ERL_NIF_TERM esock_accept_accepting(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM ref);
+static ERL_NIF_TERM esock_accept_accepting_current(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM ref);
+static ERL_NIF_TERM esock_accept_accepting_current_accept(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ SOCKET accSock,
+ ESockAddress* remote);
+static ERL_NIF_TERM esock_accept_accepting_current_error(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM opRef,
+ int save_errno);
+static ERL_NIF_TERM esock_accept_accepting_other(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM ref,
+ ErlNifPid caller);
+static ERL_NIF_TERM esock_accept_busy_retry(ErlNifEnv* env,
ESockDescriptor* descP,
ERL_NIF_TERM sockRef,
ERL_NIF_TERM accRef,
- ErlNifPid caller,
- int save_errno);
-static ERL_NIF_TERM naccept_listening_accept(ErlNifEnv* env,
- ESockDescriptor* descP,
- SOCKET accSock,
- ErlNifPid caller,
- ESockAddress* remote);
-static ERL_NIF_TERM naccept_accepting(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef,
- ERL_NIF_TERM ref);
-static ERL_NIF_TERM naccept_accepting_current(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef,
- ERL_NIF_TERM ref);
-static ERL_NIF_TERM naccept_accepting_current_accept(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef,
- SOCKET accSock,
- ESockAddress* remote);
-static ERL_NIF_TERM naccept_accepting_current_error(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef,
- ERL_NIF_TERM opRef,
- int save_errno);
-static ERL_NIF_TERM naccept_accepting_other(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM ref,
- ErlNifPid caller);
-static ERL_NIF_TERM naccept_busy_retry(ErlNifEnv* env,
+ ErlNifPid* pid,
+ unsigned int nextState);
+static BOOLEAN_T esock_accept_accepted(ErlNifEnv* env,
ESockDescriptor* descP,
- ERL_NIF_TERM sockRef,
- ERL_NIF_TERM accRef,
- ErlNifPid* pid,
- unsigned int nextState);
-static BOOLEAN_T naccept_accepted(ErlNifEnv* env,
- ESockDescriptor* descP,
- SOCKET accSock,
- ErlNifPid pid,
- ESockAddress* remote,
- ERL_NIF_TERM* result);
-static ERL_NIF_TERM nsend(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef,
- ERL_NIF_TERM sendRef,
- ErlNifBinary* dataP,
- int flags);
-static ERL_NIF_TERM nsendto(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef,
- ERL_NIF_TERM sendRef,
- ErlNifBinary* dataP,
- int flags,
- ESockAddress* toAddrP,
- unsigned int toAddrLen);
-static ERL_NIF_TERM nsendmsg_erts(ErlNifEnv* env,
+ SOCKET accSock,
+ ErlNifPid pid,
+ ESockAddress* remote,
+ ERL_NIF_TERM* result);
+static ERL_NIF_TERM esock_send(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM sendRef,
+ ErlNifBinary* dataP,
+ int flags);
+static ERL_NIF_TERM esock_sendto(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM sendRef,
+ ErlNifBinary* dataP,
+ int flags,
+ ESockAddress* toAddrP,
+ unsigned int toAddrLen);
+static ERL_NIF_TERM esock_sendmsg(ErlNifEnv* env,
ESockDescriptor* descP,
ERL_NIF_TERM sockRef,
ERL_NIF_TERM sendRef,
ERL_NIF_TERM eMsgHdr,
int flags);
-static ERL_NIF_TERM nrecv(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sendRef,
- ERL_NIF_TERM recvRef,
- int len,
- int flags);
-static ERL_NIF_TERM nrecvfrom_erts(ErlNifEnv* env,
+static ERL_NIF_TERM esock_recv(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sendRef,
+ ERL_NIF_TERM recvRef,
+ int len,
+ int flags);
+static ERL_NIF_TERM esock_recvfrom(ErlNifEnv* env,
ESockDescriptor* descP,
ERL_NIF_TERM sockRef,
ERL_NIF_TERM recvRef,
Uint16 bufSz,
int flags);
-static ERL_NIF_TERM nrecvmsg_erts(ErlNifEnv* env,
+static ERL_NIF_TERM esock_recvmsg(ErlNifEnv* env,
ESockDescriptor* descP,
ERL_NIF_TERM sockRef,
ERL_NIF_TERM recvRef,
Uint16 bufLen,
Uint16 ctrlLen,
int flags);
-static ERL_NIF_TERM nclose(ErlNifEnv* env,
- ESockDescriptor* descP);
-static BOOLEAN_T nclose_check(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM* reason);
-static ERL_NIF_TERM nclose_do(ErlNifEnv* env,
- ESockDescriptor* descP);
-static ERL_NIF_TERM nshutdown(ErlNifEnv* env,
- ESockDescriptor* descP,
- int how);
-static ERL_NIF_TERM nsetopt(ErlNifEnv* env,
- ESockDescriptor* descP,
- BOOLEAN_T isEncoded,
- BOOLEAN_T isOTP,
- int level,
- int eOpt,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_close(ErlNifEnv* env,
+ ESockDescriptor* descP);
+static BOOLEAN_T esock_close_check(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM* reason);
+static ERL_NIF_TERM esock_close_do(ErlNifEnv* env,
+ ESockDescriptor* descP);
+static ERL_NIF_TERM esock_shutdown(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int how);
+static ERL_NIF_TERM esock_setopt(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ BOOLEAN_T isEncoded,
+ BOOLEAN_T isOTP,
+ int level,
+ int eOpt,
+ ERL_NIF_TERM eVal);
/* Set OTP level options */
-static ERL_NIF_TERM nsetopt_otp(ErlNifEnv* env,
- ESockDescriptor* descP,
- int eOpt,
- ERL_NIF_TERM eVal);
-/* *** nsetopt_otp_debug ***
- * *** nsetopt_otp_iow ***
- * *** nsetopt_otp_ctrl_proc ***
- * *** nsetopt_otp_rcvbuf ***
- * *** nsetopt_otp_rcvctrlbuf ***
- * *** nsetopt_otp_sndctrlbuf ***
- */
-#define NSETOPT_OTP_FUNCS \
- NSETOPT_OTP_FUNC_DEF(debug); \
- NSETOPT_OTP_FUNC_DEF(iow); \
- NSETOPT_OTP_FUNC_DEF(ctrl_proc); \
- NSETOPT_OTP_FUNC_DEF(rcvbuf); \
- NSETOPT_OTP_FUNC_DEF(rcvctrlbuf); \
- NSETOPT_OTP_FUNC_DEF(sndctrlbuf);
-#define NSETOPT_OTP_FUNC_DEF(F) \
- static ERL_NIF_TERM nsetopt_otp_##F(ErlNifEnv* env, \
- ESockDescriptor* descP, \
- ERL_NIF_TERM eVal)
-NSETOPT_OTP_FUNCS
-#undef NSETOPT_OTP_FUNC_DEF
+static ERL_NIF_TERM esock_setopt_otp(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int eOpt,
+ ERL_NIF_TERM eVal);
+/* *** esock_setopt_otp_debug ***
+ * *** esock_setopt_otp_iow ***
+ * *** esock_setopt_otp_ctrl_proc ***
+ * *** esock_setopt_otp_rcvbuf ***
+ * *** esock_setopt_otp_rcvctrlbuf ***
+ * *** esock_setopt_otp_sndctrlbuf ***
+ */
+#define ESOCK_SETOPT_OTP_FUNCS \
+ ESOCK_SETOPT_OTP_FUNC_DEF(debug); \
+ ESOCK_SETOPT_OTP_FUNC_DEF(iow); \
+ ESOCK_SETOPT_OTP_FUNC_DEF(ctrl_proc); \
+ ESOCK_SETOPT_OTP_FUNC_DEF(rcvbuf); \
+ ESOCK_SETOPT_OTP_FUNC_DEF(rcvctrlbuf); \
+ ESOCK_SETOPT_OTP_FUNC_DEF(sndctrlbuf);
+#define ESOCK_SETOPT_OTP_FUNC_DEF(F) \
+ static ERL_NIF_TERM esock_setopt_otp_##F(ErlNifEnv* env, \
+ ESockDescriptor* descP, \
+ ERL_NIF_TERM eVal)
+ESOCK_SETOPT_OTP_FUNCS
+#undef ESOCK_SETOPT_OTP_FUNC_DEF
/* Set native options */
-static ERL_NIF_TERM nsetopt_native(ErlNifEnv* env,
- ESockDescriptor* descP,
- int level,
- int eOpt,
- ERL_NIF_TERM eVal);
-static ERL_NIF_TERM nsetopt_level(ErlNifEnv* env,
- ESockDescriptor* descP,
- int level,
- int eOpt,
- ERL_NIF_TERM eVal);
-static ERL_NIF_TERM nsetopt_lvl_socket(ErlNifEnv* env,
+static ERL_NIF_TERM esock_setopt_native(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int level,
+ int eOpt,
+ ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_level(ErlNifEnv* env,
ESockDescriptor* descP,
+ int level,
int eOpt,
ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_socket(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int eOpt,
+ ERL_NIF_TERM eVal);
/* *** Handling set of socket options for level = socket *** */
#if defined(SO_BINDTODEVICE)
-static ERL_NIF_TERM nsetopt_lvl_sock_bindtodevice(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_sock_bindtodevice(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(SO_BROADCAST)
-static ERL_NIF_TERM nsetopt_lvl_sock_broadcast(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_sock_broadcast(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(SO_DEBUG)
-static ERL_NIF_TERM nsetopt_lvl_sock_debug(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_sock_debug(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(SO_DONTROUTE)
-static ERL_NIF_TERM nsetopt_lvl_sock_dontroute(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_sock_dontroute(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(SO_KEEPALIVE)
-static ERL_NIF_TERM nsetopt_lvl_sock_keepalive(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_sock_keepalive(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(SO_LINGER)
-static ERL_NIF_TERM nsetopt_lvl_sock_linger(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_sock_linger(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(SO_OOBINLINE)
-static ERL_NIF_TERM nsetopt_lvl_sock_oobinline(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_sock_oobinline(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(SO_PEEK_OFF)
-static ERL_NIF_TERM nsetopt_lvl_sock_peek_off(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_sock_peek_off(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(SO_PRIORITY)
-static ERL_NIF_TERM nsetopt_lvl_sock_priority(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_sock_priority(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(SO_RCVBUF)
-static ERL_NIF_TERM nsetopt_lvl_sock_rcvbuf(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_sock_rcvbuf(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(SO_RCVLOWAT)
-static ERL_NIF_TERM nsetopt_lvl_sock_rcvlowat(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_sock_rcvlowat(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(SO_RCVTIMEO)
-static ERL_NIF_TERM nsetopt_lvl_sock_rcvtimeo(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_sock_rcvtimeo(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(SO_REUSEADDR)
-static ERL_NIF_TERM nsetopt_lvl_sock_reuseaddr(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_sock_reuseaddr(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(SO_REUSEPORT)
-static ERL_NIF_TERM nsetopt_lvl_sock_reuseport(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_sock_reuseport(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(SO_SNDBUF)
-static ERL_NIF_TERM nsetopt_lvl_sock_sndbuf(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_sock_sndbuf(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(SO_SNDLOWAT)
-static ERL_NIF_TERM nsetopt_lvl_sock_sndlowat(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_sock_sndlowat(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(SO_SNDTIMEO)
-static ERL_NIF_TERM nsetopt_lvl_sock_sndtimeo(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_sock_sndtimeo(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(SO_TIMESTAMP)
-static ERL_NIF_TERM nsetopt_lvl_sock_timestamp(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_sock_timestamp(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
-static ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env,
- ESockDescriptor* descP,
- int eOpt,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_ip(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int eOpt,
+ ERL_NIF_TERM eVal);
/* *** Handling set of socket options for level = ip *** */
#if defined(IP_ADD_MEMBERSHIP)
-static ERL_NIF_TERM nsetopt_lvl_ip_add_membership(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_ip_add_membership(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(IP_ADD_SOURCE_MEMBERSHIP)
-static ERL_NIF_TERM nsetopt_lvl_ip_add_source_membership(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_ip_add_source_membership(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(IP_BLOCK_SOURCE)
-static ERL_NIF_TERM nsetopt_lvl_ip_block_source(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_ip_block_source(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(IP_DROP_MEMBERSHIP)
-static ERL_NIF_TERM nsetopt_lvl_ip_drop_membership(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_ip_drop_membership(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(IP_DROP_SOURCE_MEMBERSHIP)
-static ERL_NIF_TERM nsetopt_lvl_ip_drop_source_membership(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_ip_drop_source_membership(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(IP_FREEBIND)
-static ERL_NIF_TERM nsetopt_lvl_ip_freebind(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_ip_freebind(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(IP_HDRINCL)
-static ERL_NIF_TERM nsetopt_lvl_ip_hdrincl(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_ip_hdrincl(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(IP_MINTTL)
-static ERL_NIF_TERM nsetopt_lvl_ip_minttl(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_ip_minttl(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(IP_MSFILTER) && defined(IP_MSFILTER_SIZE)
-static ERL_NIF_TERM nsetopt_lvl_ip_msfilter(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_ip_msfilter(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
static BOOLEAN_T decode_ip_msfilter_mode(ErlNifEnv* env,
ERL_NIF_TERM eVal,
Uint32* mode);
-static ERL_NIF_TERM nsetopt_lvl_ip_msfilter_set(ErlNifEnv* env,
- SOCKET sock,
- struct ip_msfilter* msfP,
- SOCKLEN_T optLen);
+static ERL_NIF_TERM esock_setopt_lvl_ip_msfilter_set(ErlNifEnv* env,
+ SOCKET sock,
+ struct ip_msfilter* msfP,
+ SOCKLEN_T optLen);
#endif
#if defined(IP_MTU_DISCOVER)
-static ERL_NIF_TERM nsetopt_lvl_ip_mtu_discover(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_ip_mtu_discover(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(IP_MULTICAST_ALL)
-static ERL_NIF_TERM nsetopt_lvl_ip_multicast_all(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_ip_multicast_all(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(IP_MULTICAST_IF)
-static ERL_NIF_TERM nsetopt_lvl_ip_multicast_if(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_ip_multicast_if(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(IP_MULTICAST_LOOP)
-static ERL_NIF_TERM nsetopt_lvl_ip_multicast_loop(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_ip_multicast_loop(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(IP_MULTICAST_TTL)
-static ERL_NIF_TERM nsetopt_lvl_ip_multicast_ttl(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_ip_multicast_ttl(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(IP_NODEFRAG)
-static ERL_NIF_TERM nsetopt_lvl_ip_nodefrag(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_ip_nodefrag(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(IP_PKTINFO)
-static ERL_NIF_TERM nsetopt_lvl_ip_pktinfo(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_ip_pktinfo(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(IP_RECVDSTADDR)
-static ERL_NIF_TERM nsetopt_lvl_ip_recvdstaddr(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_ip_recvdstaddr(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(IP_RECVERR)
-static ERL_NIF_TERM nsetopt_lvl_ip_recverr(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_ip_recverr(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(IP_RECVIF)
-static ERL_NIF_TERM nsetopt_lvl_ip_recvif(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_ip_recvif(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(IP_RECVOPTS)
-static ERL_NIF_TERM nsetopt_lvl_ip_recvopts(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_ip_recvopts(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(IP_RECVORIGDSTADDR)
-static ERL_NIF_TERM nsetopt_lvl_ip_recvorigdstaddr(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_ip_recvorigdstaddr(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(IP_RECVTOS)
-static ERL_NIF_TERM nsetopt_lvl_ip_recvtos(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_ip_recvtos(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(IP_RECVTTL)
-static ERL_NIF_TERM nsetopt_lvl_ip_recvttl(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_ip_recvttl(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(IP_RETOPTS)
-static ERL_NIF_TERM nsetopt_lvl_ip_retopts(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
-#endif
-#if defined(IP_ROUTER_ALERT)
-static ERL_NIF_TERM nsetopt_lvl_ip_router_alert(ErlNifEnv* env,
+static ERL_NIF_TERM esock_setopt_lvl_ip_retopts(ErlNifEnv* env,
ESockDescriptor* descP,
ERL_NIF_TERM eVal);
#endif
+#if defined(IP_ROUTER_ALERT)
+static ERL_NIF_TERM esock_setopt_lvl_ip_router_alert(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
+#endif
#if defined(IP_SENDSRCADDR)
-static ERL_NIF_TERM nsetopt_lvl_ip_sendsrcaddr(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_ip_sendsrcaddr(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(IP_TOS)
-static ERL_NIF_TERM nsetopt_lvl_ip_tos(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_ip_tos(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(IP_TRANSPARENT)
-static ERL_NIF_TERM nsetopt_lvl_ip_transparent(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_ip_transparent(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(IP_TTL)
-static ERL_NIF_TERM nsetopt_lvl_ip_ttl(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_ip_ttl(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(IP_UNBLOCK_SOURCE)
-static ERL_NIF_TERM nsetopt_lvl_ip_unblock_source(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_ip_unblock_source(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(IP_DROP_MEMBERSHIP) || defined(IP_ADD_MEMBERSHIP)
static
-ERL_NIF_TERM nsetopt_lvl_ip_update_membership(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal,
- int opt);
+ERL_NIF_TERM esock_setopt_lvl_ip_update_membership(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal,
+ int opt);
#endif
#if defined(IP_ADD_SOURCE_MEMBERSHIP) || defined(IP_DROP_SOURCE_MEMBERSHIP) || defined(IP_BLOCK_SOURCE) || defined(IP_UNBLOCK_SOURCE)
static
-ERL_NIF_TERM nsetopt_lvl_ip_update_source(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal,
- int opt);
+ERL_NIF_TERM esock_setopt_lvl_ip_update_source(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal,
+ int opt);
#endif
/* *** Handling set of socket options for level = ipv6 *** */
#if defined(HAVE_IPV6)
-static ERL_NIF_TERM nsetopt_lvl_ipv6(ErlNifEnv* env,
- ESockDescriptor* descP,
- int eOpt,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_ipv6(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int eOpt,
+ ERL_NIF_TERM eVal);
#if defined(IPV6_ADDRFORM)
-static ERL_NIF_TERM nsetopt_lvl_ipv6_addrform(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_ipv6_addrform(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(IPV6_ADD_MEMBERSHIP)
-static ERL_NIF_TERM nsetopt_lvl_ipv6_add_membership(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_ipv6_add_membership(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(IPV6_AUTHHDR)
-static ERL_NIF_TERM nsetopt_lvl_ipv6_authhdr(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_ipv6_authhdr(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(IPV6_DROP_MEMBERSHIP)
-static ERL_NIF_TERM nsetopt_lvl_ipv6_drop_membership(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_ipv6_drop_membership(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(IPV6_DSTOPTS)
-static ERL_NIF_TERM nsetopt_lvl_ipv6_dstopts(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_ipv6_dstopts(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(IPV6_FLOWINFO)
-static ERL_NIF_TERM nsetopt_lvl_ipv6_flowinfo(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_ipv6_flowinfo(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(IPV6_HOPLIMIT)
-static ERL_NIF_TERM nsetopt_lvl_ipv6_hoplimit(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_ipv6_hoplimit(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(IPV6_HOPOPTS)
-static ERL_NIF_TERM nsetopt_lvl_ipv6_hopopts(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_ipv6_hopopts(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(IPV6_MTU)
-static ERL_NIF_TERM nsetopt_lvl_ipv6_mtu(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_ipv6_mtu(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(IPV6_MTU_DISCOVER)
-static ERL_NIF_TERM nsetopt_lvl_ipv6_mtu_discover(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_ipv6_mtu_discover(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(IPV6_MULTICAST_HOPS)
-static ERL_NIF_TERM nsetopt_lvl_ipv6_multicast_hops(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_ipv6_multicast_hops(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(IPV6_MULTICAST_IF)
-static ERL_NIF_TERM nsetopt_lvl_ipv6_multicast_if(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_ipv6_multicast_if(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(IPV6_MULTICAST_LOOP)
-static ERL_NIF_TERM nsetopt_lvl_ipv6_multicast_loop(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_ipv6_multicast_loop(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(IPV6_RECVERR)
-static ERL_NIF_TERM nsetopt_lvl_ipv6_recverr(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_ipv6_recverr(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(IPV6_RECVPKTINFO) || defined(IPV6_PKTINFO)
-static ERL_NIF_TERM nsetopt_lvl_ipv6_recvpktinfo(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_ipv6_recvpktinfo(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(IPV6_ROUTER_ALERT)
-static ERL_NIF_TERM nsetopt_lvl_ipv6_router_alert(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_ipv6_router_alert(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(IPV6_RTHDR)
-static ERL_NIF_TERM nsetopt_lvl_ipv6_rthdr(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_ipv6_rthdr(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(IPV6_UNICAST_HOPS)
-static ERL_NIF_TERM nsetopt_lvl_ipv6_unicast_hops(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_ipv6_unicast_hops(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(IPV6_V6ONLY)
-static ERL_NIF_TERM nsetopt_lvl_ipv6_v6only(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_ipv6_v6only(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(IPV6_ADD_MEMBERSHIP) || defined(IPV6_DROP_MEMBERSHIP)
-static ERL_NIF_TERM nsetopt_lvl_ipv6_update_membership(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal,
- int opt);
+static ERL_NIF_TERM esock_setopt_lvl_ipv6_update_membership(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal,
+ int opt);
#endif
#endif // defined(HAVE_IPV6)
-static ERL_NIF_TERM nsetopt_lvl_tcp(ErlNifEnv* env,
- ESockDescriptor* descP,
- int eOpt,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_tcp(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int eOpt,
+ ERL_NIF_TERM eVal);
#if defined(TCP_CONGESTION)
-static ERL_NIF_TERM nsetopt_lvl_tcp_congestion(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_tcp_congestion(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(TCP_MAXSEG)
-static ERL_NIF_TERM nsetopt_lvl_tcp_maxseg(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_tcp_maxseg(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(TCP_NODELAY)
-static ERL_NIF_TERM nsetopt_lvl_tcp_nodelay(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_tcp_nodelay(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
-static ERL_NIF_TERM nsetopt_lvl_udp(ErlNifEnv* env,
- ESockDescriptor* descP,
- int eOpt,
- ERL_NIF_TERM eVal);
-#if defined(UDP_CORK)
-static ERL_NIF_TERM nsetopt_lvl_udp_cork(ErlNifEnv* env,
+static ERL_NIF_TERM esock_setopt_lvl_udp(ErlNifEnv* env,
ESockDescriptor* descP,
+ int eOpt,
ERL_NIF_TERM eVal);
+#if defined(UDP_CORK)
+static ERL_NIF_TERM esock_setopt_lvl_udp_cork(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(HAVE_SCTP)
-static ERL_NIF_TERM nsetopt_lvl_sctp(ErlNifEnv* env,
- ESockDescriptor* descP,
- int eOpt,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_sctp(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int eOpt,
+ ERL_NIF_TERM eVal);
#if defined(SCTP_ASSOCINFO)
-static ERL_NIF_TERM nsetopt_lvl_sctp_associnfo(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_sctp_associnfo(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(SCTP_AUTOCLOSE)
-static ERL_NIF_TERM nsetopt_lvl_sctp_autoclose(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_sctp_autoclose(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(SCTP_DISABLE_FRAGMENTS)
-static ERL_NIF_TERM nsetopt_lvl_sctp_disable_fragments(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_sctp_disable_fragments(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(SCTP_EVENTS)
-static ERL_NIF_TERM nsetopt_lvl_sctp_events(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_sctp_events(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(SCTP_INITMSG)
-static ERL_NIF_TERM nsetopt_lvl_sctp_initmsg(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_sctp_initmsg(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(SCTP_MAXSEG)
-static ERL_NIF_TERM nsetopt_lvl_sctp_maxseg(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_sctp_maxseg(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(SCTP_NODELAY)
-static ERL_NIF_TERM nsetopt_lvl_sctp_nodelay(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_sctp_nodelay(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#if defined(SCTP_RTOINFO)
-static ERL_NIF_TERM nsetopt_lvl_sctp_rtoinfo(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_lvl_sctp_rtoinfo(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal);
#endif
#endif // defined(HAVE_SCTP)
-static ERL_NIF_TERM ngetopt(ErlNifEnv* env,
- ESockDescriptor* descP,
- BOOLEAN_T isEncoded,
- BOOLEAN_T isOTP,
- int level,
- ERL_NIF_TERM eOpt);
+static ERL_NIF_TERM esock_getopt(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ BOOLEAN_T isEncoded,
+ BOOLEAN_T isOTP,
+ int level,
+ ERL_NIF_TERM eOpt);
-static ERL_NIF_TERM ngetopt_otp(ErlNifEnv* env,
- ESockDescriptor* descP,
- int eOpt);
-/* *** ngetopt_otp_debug ***
- * *** ngetopt_otp_iow ***
- * *** ngetopt_otp_ctrl_proc ***
- * *** ngetopt_otp_rcvbuf ***
- * *** ngetopt_otp_rcvctrlbuf ***
- * *** ngetopt_otp_sndctrlbuf ***
- * *** ngetopt_otp_fd ***
- * *** ngetopt_otp_domain ***
- * *** ngetopt_otp_type ***
- * *** ngetopt_otp_protocol ***
- */
-#define NGETOPT_OTP_FUNCS \
- NGETOPT_OTP_FUNC_DEF(debug); \
- NGETOPT_OTP_FUNC_DEF(iow); \
- NGETOPT_OTP_FUNC_DEF(ctrl_proc); \
- NGETOPT_OTP_FUNC_DEF(rcvbuf); \
- NGETOPT_OTP_FUNC_DEF(rcvctrlbuf); \
- NGETOPT_OTP_FUNC_DEF(sndctrlbuf); \
- NGETOPT_OTP_FUNC_DEF(fd); \
- NGETOPT_OTP_FUNC_DEF(domain); \
- NGETOPT_OTP_FUNC_DEF(type); \
- NGETOPT_OTP_FUNC_DEF(protocol);
-#define NGETOPT_OTP_FUNC_DEF(F) \
- static ERL_NIF_TERM ngetopt_otp_##F(ErlNifEnv* env, \
- ESockDescriptor* descP)
-NGETOPT_OTP_FUNCS
-#undef NGETOPT_OTP_FUNC_DEF
+static ERL_NIF_TERM esock_getopt_otp(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int eOpt);
+/* *** esock_getopt_otp_debug ***
+ * *** esock_getopt_otp_iow ***
+ * *** esock_getopt_otp_ctrl_proc ***
+ * *** esock_getopt_otp_rcvbuf ***
+ * *** esock_getopt_otp_rcvctrlbuf ***
+ * *** esock_getopt_otp_sndctrlbuf ***
+ * *** esock_getopt_otp_fd ***
+ * *** esock_getopt_otp_domain ***
+ * *** esock_getopt_otp_type ***
+ * *** esock_getopt_otp_protocol ***
+ */
+#define ESOCK_GETOPT_OTP_FUNCS \
+ ESOCK_GETOPT_OTP_FUNC_DEF(debug); \
+ ESOCK_GETOPT_OTP_FUNC_DEF(iow); \
+ ESOCK_GETOPT_OTP_FUNC_DEF(ctrl_proc); \
+ ESOCK_GETOPT_OTP_FUNC_DEF(rcvbuf); \
+ ESOCK_GETOPT_OTP_FUNC_DEF(rcvctrlbuf); \
+ ESOCK_GETOPT_OTP_FUNC_DEF(sndctrlbuf); \
+ ESOCK_GETOPT_OTP_FUNC_DEF(fd); \
+ ESOCK_GETOPT_OTP_FUNC_DEF(domain); \
+ ESOCK_GETOPT_OTP_FUNC_DEF(type); \
+ ESOCK_GETOPT_OTP_FUNC_DEF(protocol);
+#define ESOCK_GETOPT_OTP_FUNC_DEF(F) \
+ static ERL_NIF_TERM esock_getopt_otp_##F(ErlNifEnv* env, \
+ ESockDescriptor* descP)
+ESOCK_GETOPT_OTP_FUNCS
+#undef ESOCK_GETOPT_OTP_FUNC_DEF
-static ERL_NIF_TERM ngetopt_native(ErlNifEnv* env,
- ESockDescriptor* descP,
- int level,
- ERL_NIF_TERM eOpt);
-static ERL_NIF_TERM ngetopt_native_unspec(ErlNifEnv* env,
- ESockDescriptor* descP,
- int level,
- int opt,
- SOCKOPTLEN_T valueSz);
-static ERL_NIF_TERM ngetopt_level(ErlNifEnv* env,
- ESockDescriptor* descP,
- int level,
- int eOpt);
-static ERL_NIF_TERM ngetopt_lvl_socket(ErlNifEnv* env,
+static ERL_NIF_TERM esock_getopt_native(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int level,
+ ERL_NIF_TERM eOpt);
+static ERL_NIF_TERM esock_getopt_native_unspec(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int level,
+ int opt,
+ SOCKOPTLEN_T valueSz);
+static ERL_NIF_TERM esock_getopt_level(ErlNifEnv* env,
ESockDescriptor* descP,
+ int level,
int eOpt);
+static ERL_NIF_TERM esock_getopt_lvl_socket(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int eOpt);
#if defined(SO_ACCEPTCONN)
-static ERL_NIF_TERM ngetopt_lvl_sock_acceptconn(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_sock_acceptconn(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(SO_BINDTODEVICE)
-static ERL_NIF_TERM ngetopt_lvl_sock_bindtodevice(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_sock_bindtodevice(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(SO_BROADCAST)
-static ERL_NIF_TERM ngetopt_lvl_sock_broadcast(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_sock_broadcast(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(SO_DEBUG)
-static ERL_NIF_TERM ngetopt_lvl_sock_debug(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_sock_debug(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(SO_DOMAIN)
-static ERL_NIF_TERM ngetopt_lvl_sock_domain(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_sock_domain(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(SO_DONTROUTE)
-static ERL_NIF_TERM ngetopt_lvl_sock_dontroute(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_sock_dontroute(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(SO_KEEPALIVE)
-static ERL_NIF_TERM ngetopt_lvl_sock_keepalive(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_sock_keepalive(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(SO_LINGER)
-static ERL_NIF_TERM ngetopt_lvl_sock_linger(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_sock_linger(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(SO_OOBINLINE)
-static ERL_NIF_TERM ngetopt_lvl_sock_oobinline(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_sock_oobinline(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(SO_PEEK_OFF)
-static ERL_NIF_TERM ngetopt_lvl_sock_peek_off(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_sock_peek_off(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(SO_PRIORITY)
-static ERL_NIF_TERM ngetopt_lvl_sock_priority(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_sock_priority(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(SO_PROTOCOL)
-static ERL_NIF_TERM ngetopt_lvl_sock_protocol(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_sock_protocol(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(SO_RCVBUF)
-static ERL_NIF_TERM ngetopt_lvl_sock_rcvbuf(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_sock_rcvbuf(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(SO_RCVLOWAT)
-static ERL_NIF_TERM ngetopt_lvl_sock_rcvlowat(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_sock_rcvlowat(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(SO_RCVTIMEO)
-static ERL_NIF_TERM ngetopt_lvl_sock_rcvtimeo(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_sock_rcvtimeo(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(SO_REUSEADDR)
-static ERL_NIF_TERM ngetopt_lvl_sock_reuseaddr(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_sock_reuseaddr(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(SO_REUSEPORT)
-static ERL_NIF_TERM ngetopt_lvl_sock_reuseport(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_sock_reuseport(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(SO_SNDBUF)
-static ERL_NIF_TERM ngetopt_lvl_sock_sndbuf(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_sock_sndbuf(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(SO_SNDLOWAT)
-static ERL_NIF_TERM ngetopt_lvl_sock_sndlowat(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_sock_sndlowat(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(SO_SNDTIMEO)
-static ERL_NIF_TERM ngetopt_lvl_sock_sndtimeo(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_sock_sndtimeo(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(SO_TIMESTAMP)
-static ERL_NIF_TERM ngetopt_lvl_sock_timestamp(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_sock_timestamp(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(SO_TYPE)
-static ERL_NIF_TERM ngetopt_lvl_sock_type(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_sock_type(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
-static ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env,
- ESockDescriptor* descP,
- int eOpt);
+static ERL_NIF_TERM esock_getopt_lvl_ip(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int eOpt);
#if defined(IP_FREEBIND)
-static ERL_NIF_TERM ngetopt_lvl_ip_freebind(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_ip_freebind(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(IP_HDRINCL)
-static ERL_NIF_TERM ngetopt_lvl_ip_hdrincl(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_ip_hdrincl(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(IP_MINTTL)
-static ERL_NIF_TERM ngetopt_lvl_ip_minttl(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_ip_minttl(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(IP_MTU)
-static ERL_NIF_TERM ngetopt_lvl_ip_mtu(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_ip_mtu(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(IP_MTU_DISCOVER)
-static ERL_NIF_TERM ngetopt_lvl_ip_mtu_discover(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_ip_mtu_discover(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(IP_MULTICAST_ALL)
-static ERL_NIF_TERM ngetopt_lvl_ip_multicast_all(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_ip_multicast_all(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(IP_MULTICAST_IF)
-static ERL_NIF_TERM ngetopt_lvl_ip_multicast_if(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_ip_multicast_if(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(IP_MULTICAST_LOOP)
-static ERL_NIF_TERM ngetopt_lvl_ip_multicast_loop(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_ip_multicast_loop(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(IP_MULTICAST_TTL)
-static ERL_NIF_TERM ngetopt_lvl_ip_multicast_ttl(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_ip_multicast_ttl(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(IP_NODEFRAG)
-static ERL_NIF_TERM ngetopt_lvl_ip_nodefrag(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_ip_nodefrag(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(IP_PKTINFO)
-static ERL_NIF_TERM ngetopt_lvl_ip_pktinfo(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_ip_pktinfo(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(IP_RECVDSTADDR)
-static ERL_NIF_TERM ngetopt_lvl_ip_recvdstaddr(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_ip_recvdstaddr(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(IP_RECVERR)
-static ERL_NIF_TERM ngetopt_lvl_ip_recverr(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_ip_recverr(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(IP_RECVIF)
-static ERL_NIF_TERM ngetopt_lvl_ip_recvif(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_ip_recvif(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(IP_RECVOPTS)
-static ERL_NIF_TERM ngetopt_lvl_ip_recvopts(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_ip_recvopts(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(IP_RECVORIGDSTADDR)
-static ERL_NIF_TERM ngetopt_lvl_ip_recvorigdstaddr(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_ip_recvorigdstaddr(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(IP_RECVTOS)
-static ERL_NIF_TERM ngetopt_lvl_ip_recvtos(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_ip_recvtos(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(IP_RECVTTL)
-static ERL_NIF_TERM ngetopt_lvl_ip_recvttl(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_ip_recvttl(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(IP_RETOPTS)
-static ERL_NIF_TERM ngetopt_lvl_ip_retopts(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_ip_retopts(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(IP_ROUTER_ALERT)
-static ERL_NIF_TERM ngetopt_lvl_ip_router_alert(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_ip_router_alert(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(IP_SENDSRCADDR)
-static ERL_NIF_TERM ngetopt_lvl_ip_sendsrcaddr(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_ip_sendsrcaddr(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(IP_TOS)
-static ERL_NIF_TERM ngetopt_lvl_ip_tos(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_ip_tos(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(IP_TRANSPARENT)
-static ERL_NIF_TERM ngetopt_lvl_ip_transparent(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_ip_transparent(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(IP_TTL)
-static ERL_NIF_TERM ngetopt_lvl_ip_ttl(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_ip_ttl(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(HAVE_IPV6)
-static ERL_NIF_TERM ngetopt_lvl_ipv6(ErlNifEnv* env,
- ESockDescriptor* descP,
- int eOpt);
+static ERL_NIF_TERM esock_getopt_lvl_ipv6(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int eOpt);
#if defined(IPV6_AUTHHDR)
-static ERL_NIF_TERM ngetopt_lvl_ipv6_authhdr(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_ipv6_authhdr(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(IPV6_DSTOPTS)
-static ERL_NIF_TERM ngetopt_lvl_ipv6_dstopts(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_ipv6_dstopts(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(IPV6_FLOWINFO)
-static ERL_NIF_TERM ngetopt_lvl_ipv6_flowinfo(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_ipv6_flowinfo(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(IPV6_HOPLIMIT)
-static ERL_NIF_TERM ngetopt_lvl_ipv6_hoplimit(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_ipv6_hoplimit(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(IPV6_HOPOPTS)
-static ERL_NIF_TERM ngetopt_lvl_ipv6_hopopts(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_ipv6_hopopts(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(IPV6_MTU)
-static ERL_NIF_TERM ngetopt_lvl_ipv6_mtu(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_ipv6_mtu(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(IPV6_MTU_DISCOVER)
-static ERL_NIF_TERM ngetopt_lvl_ipv6_mtu_discover(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_ipv6_mtu_discover(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(IPV6_MULTICAST_HOPS)
-static ERL_NIF_TERM ngetopt_lvl_ipv6_multicast_hops(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_ipv6_multicast_hops(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(IPV6_MULTICAST_IF)
-static ERL_NIF_TERM ngetopt_lvl_ipv6_multicast_if(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_ipv6_multicast_if(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(IPV6_MULTICAST_LOOP)
-static ERL_NIF_TERM ngetopt_lvl_ipv6_multicast_loop(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_ipv6_multicast_loop(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(IPV6_RECVERR)
-static ERL_NIF_TERM ngetopt_lvl_ipv6_recverr(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_ipv6_recverr(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(IPV6_RECVPKTINFO) || defined(IPV6_PKTINFO)
-static ERL_NIF_TERM ngetopt_lvl_ipv6_recvpktinfo(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_ipv6_recvpktinfo(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(IPV6_ROUTER_ALERT)
-static ERL_NIF_TERM ngetopt_lvl_ipv6_router_alert(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_ipv6_router_alert(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(IPV6_RTHDR)
-static ERL_NIF_TERM ngetopt_lvl_ipv6_rthdr(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_ipv6_rthdr(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(IPV6_UNICAST_HOPS)
-static ERL_NIF_TERM ngetopt_lvl_ipv6_unicast_hops(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_ipv6_unicast_hops(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(IPV6_V6ONLY)
-static ERL_NIF_TERM ngetopt_lvl_ipv6_v6only(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_ipv6_v6only(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#endif // defined(HAVE_IPV6)
-static ERL_NIF_TERM ngetopt_lvl_tcp(ErlNifEnv* env,
- ESockDescriptor* descP,
- int eOpt);
+static ERL_NIF_TERM esock_getopt_lvl_tcp(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int eOpt);
#if defined(TCP_CONGESTION)
-static ERL_NIF_TERM ngetopt_lvl_tcp_congestion(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_tcp_congestion(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(TCP_MAXSEG)
-static ERL_NIF_TERM ngetopt_lvl_tcp_maxseg(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_tcp_maxseg(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(TCP_NODELAY)
-static ERL_NIF_TERM ngetopt_lvl_tcp_nodelay(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_tcp_nodelay(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
-static ERL_NIF_TERM ngetopt_lvl_udp(ErlNifEnv* env,
- ESockDescriptor* descP,
- int eOpt);
+static ERL_NIF_TERM esock_getopt_lvl_udp(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int eOpt);
#if defined(UDP_CORK)
-static ERL_NIF_TERM ngetopt_lvl_udp_cork(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_udp_cork(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(HAVE_SCTP)
-static ERL_NIF_TERM ngetopt_lvl_sctp(ErlNifEnv* env,
- ESockDescriptor* descP,
- int eOpt);
+static ERL_NIF_TERM esock_getopt_lvl_sctp(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int eOpt);
#if defined(SCTP_ASSOCINFO)
-static ERL_NIF_TERM ngetopt_lvl_sctp_associnfo(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_sctp_associnfo(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(SCTP_AUTOCLOSE)
-static ERL_NIF_TERM ngetopt_lvl_sctp_autoclose(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_sctp_autoclose(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(SCTP_DISABLE_FRAGMENTS)
-static ERL_NIF_TERM ngetopt_lvl_sctp_disable_fragments(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_sctp_disable_fragments(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(SCTP_MAXSEG)
-static ERL_NIF_TERM ngetopt_lvl_sctp_maxseg(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_sctp_maxseg(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(SCTP_INITMSG)
-static ERL_NIF_TERM ngetopt_lvl_sctp_initmsg(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_sctp_initmsg(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(SCTP_NODELAY)
-static ERL_NIF_TERM ngetopt_lvl_sctp_nodelay(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_sctp_nodelay(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#if defined(SCTP_RTOINFO)
-static ERL_NIF_TERM ngetopt_lvl_sctp_rtoinfo(ErlNifEnv* env,
- ESockDescriptor* descP);
+static ERL_NIF_TERM esock_getopt_lvl_sctp_rtoinfo(ErlNifEnv* env,
+ ESockDescriptor* descP);
#endif
#endif // defined(HAVE_SCTP)
-static ERL_NIF_TERM nsockname(ErlNifEnv* env,
- ESockDescriptor* descP);
-static ERL_NIF_TERM npeername(ErlNifEnv* env,
- ESockDescriptor* descP);
-static ERL_NIF_TERM ncancel(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM op,
- ERL_NIF_TERM sockRef,
- ERL_NIF_TERM opRef);
-static ERL_NIF_TERM ncancel_connect(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM opRef);
-static ERL_NIF_TERM ncancel_accept(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef,
- ERL_NIF_TERM opRef);
-static ERL_NIF_TERM ncancel_accept_current(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef);
-static ERL_NIF_TERM ncancel_accept_waiting(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM opRef);
-static ERL_NIF_TERM ncancel_send(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef,
- ERL_NIF_TERM opRef);
-static ERL_NIF_TERM ncancel_send_current(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef);
-static ERL_NIF_TERM ncancel_send_waiting(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM opRef);
-static ERL_NIF_TERM ncancel_recv(ErlNifEnv* env,
+static ERL_NIF_TERM esock_sockname(ErlNifEnv* env,
+ ESockDescriptor* descP);
+static ERL_NIF_TERM esock_peername(ErlNifEnv* env,
+ ESockDescriptor* descP);
+static ERL_NIF_TERM esock_cancel(ErlNifEnv* env,
ESockDescriptor* descP,
+ ERL_NIF_TERM op,
ERL_NIF_TERM sockRef,
ERL_NIF_TERM opRef);
-static ERL_NIF_TERM ncancel_recv_current(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef);
-static ERL_NIF_TERM ncancel_recv_waiting(ErlNifEnv* env,
+static ERL_NIF_TERM esock_cancel_connect(ErlNifEnv* env,
ESockDescriptor* descP,
ERL_NIF_TERM opRef);
-static ERL_NIF_TERM ncancel_read_select(ErlNifEnv* env,
+static ERL_NIF_TERM esock_cancel_accept(ErlNifEnv* env,
ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
ERL_NIF_TERM opRef);
-static ERL_NIF_TERM ncancel_write_select(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM opRef);
-static ERL_NIF_TERM ncancel_mode_select(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM opRef,
- int smode,
- int rmode);
+static ERL_NIF_TERM esock_cancel_accept_current(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef);
+static ERL_NIF_TERM esock_cancel_accept_waiting(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM opRef);
+static ERL_NIF_TERM esock_cancel_send(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM opRef);
+static ERL_NIF_TERM esock_cancel_send_current(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef);
+static ERL_NIF_TERM esock_cancel_send_waiting(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM opRef);
+static ERL_NIF_TERM esock_cancel_recv(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM opRef);
+static ERL_NIF_TERM esock_cancel_recv_current(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef);
+static ERL_NIF_TERM esock_cancel_recv_waiting(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM opRef);
+static ERL_NIF_TERM esock_cancel_read_select(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM opRef);
+static ERL_NIF_TERM esock_cancel_write_select(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM opRef);
+static ERL_NIF_TERM esock_cancel_mode_select(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM opRef,
+ int smode,
+ int rmode);
#if defined(USE_SETOPT_STR_OPT)
-static ERL_NIF_TERM nsetopt_str_opt(ErlNifEnv* env,
- ESockDescriptor* descP,
- int level,
- int opt,
- int max,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_str_opt(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int level,
+ int opt,
+ int max,
+ ERL_NIF_TERM eVal);
#endif
-static ERL_NIF_TERM nsetopt_bool_opt(ErlNifEnv* env,
- ESockDescriptor* descP,
- int level,
- int opt,
- ERL_NIF_TERM eVal);
-static ERL_NIF_TERM nsetopt_int_opt(ErlNifEnv* env,
- ESockDescriptor* descP,
- int level,
- int opt,
- ERL_NIF_TERM eVal);
-static ERL_NIF_TERM nsetopt_timeval_opt(ErlNifEnv* env,
- ESockDescriptor* descP,
- int level,
- int opt,
- ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_bool_opt(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int level,
+ int opt,
+ ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_int_opt(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int level,
+ int opt,
+ ERL_NIF_TERM eVal);
+static ERL_NIF_TERM esock_setopt_timeval_opt(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int level,
+ int opt,
+ ERL_NIF_TERM eVal);
#if defined(USE_GETOPT_STR_OPT)
-static ERL_NIF_TERM ngetopt_str_opt(ErlNifEnv* env,
- ESockDescriptor* descP,
- int level,
- int opt,
- int max);
+static ERL_NIF_TERM esock_getopt_str_opt(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int level,
+ int opt,
+ int max);
#endif
-static ERL_NIF_TERM ngetopt_bool_opt(ErlNifEnv* env,
- ESockDescriptor* descP,
- int level,
- int opt);
-static ERL_NIF_TERM ngetopt_int_opt(ErlNifEnv* env,
- ESockDescriptor* descP,
- int level,
- int opt);
-static ERL_NIF_TERM ngetopt_timeval_opt(ErlNifEnv* env,
- ESockDescriptor* descP,
- int level,
- int opt);
+static ERL_NIF_TERM esock_getopt_bool_opt(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int level,
+ int opt);
+static ERL_NIF_TERM esock_getopt_int_opt(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int level,
+ int opt);
+static ERL_NIF_TERM esock_getopt_timeval_opt(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int level,
+ int opt);
static BOOLEAN_T send_check_writer(ErlNifEnv* env,
ESockDescriptor* descP,
@@ -2281,10 +2286,10 @@ static ERL_NIF_TERM recvmsg_check_msg(ErlNifEnv* env,
ErlNifBinary* ctrlBufP,
ERL_NIF_TERM sockRef);
-static ERL_NIF_TERM nfinalize_connection(ErlNifEnv* env,
+static ERL_NIF_TERM esock_finalize_connection(ErlNifEnv* env,
+ ESockDescriptor* descP);
+static ERL_NIF_TERM esock_finalize_close(ErlNifEnv* env,
ESockDescriptor* descP);
-static ERL_NIF_TERM nfinalize_close(ErlNifEnv* env,
- ESockDescriptor* descP);
extern char* encode_msghdr(ErlNifEnv* env,
ESockDescriptor* descP,
@@ -2417,11 +2422,11 @@ static BOOLEAN_T decode_native_get_opt(ErlNifEnv* env,
// static void encode_bool(BOOLEAN_T val, ERL_NIF_TERM* eVal);
static ERL_NIF_TERM encode_ip_tos(ErlNifEnv* env, int val);
-static void socket_stop_handle_current(ErlNifEnv* env,
- const char* role,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef,
- ESockRequestor* reqP);
+static void esock_stop_handle_current(ErlNifEnv* env,
+ const char* role,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ESockRequestor* reqP);
static void inform_waiting_procs(ErlNifEnv* env,
const char* role,
ESockDescriptor* descP,
@@ -2560,30 +2565,30 @@ static size_t my_strnlen(const char *s, size_t maxlen);
#endif
*/
-static void socket_dtor(ErlNifEnv* env, void* obj);
-static void socket_stop(ErlNifEnv* env,
- void* obj,
- int fd,
- int is_direct_call);
-static void socket_down(ErlNifEnv* env,
- void* obj,
- const ErlNifPid* pid,
- const ErlNifMonitor* mon);
+static void esock_dtor(ErlNifEnv* env, void* obj);
+static void esock_stop(ErlNifEnv* env,
+ void* obj,
+ int fd,
+ int is_direct_call);
+static void esock_down(ErlNifEnv* env,
+ void* obj,
+ const ErlNifPid* pid,
+ const ErlNifMonitor* mon);
#if !defined(__WIN32__)
-static void socket_down_acceptor(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef,
- const ErlNifPid* pid);
-static void socket_down_writer(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef,
- const ErlNifPid* pid);
-static void socket_down_reader(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef,
- const ErlNifPid* pid);
+static void esock_down_acceptor(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ const ErlNifPid* pid);
+static void esock_down_writer(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ const ErlNifPid* pid);
+static void esock_down_reader(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ const ErlNifPid* pid);
static char* esock_send_wrap_msg(ErlNifEnv* env,
ESockDescriptor* descP,
@@ -2995,11 +3000,11 @@ LOCAL_ERROR_REASON_ATOMS
/* *** Sockets *** */
-static ErlNifResourceType* sockets;
-static ErlNifResourceTypeInit socketInit = {
- socket_dtor,
- socket_stop,
- (ErlNifResourceDown*) socket_down
+static ErlNifResourceType* esocks;
+static ErlNifResourceTypeInit esockInit = {
+ esock_dtor,
+ esock_stop,
+ (ErlNifResourceDown*) esock_down
};
// Initiated when the nif is loaded
@@ -3100,7 +3105,7 @@ ERL_NIF_TERM nif_info(ErlNifEnv* env,
{
ESockDescriptor* descP;
- if (!enif_get_resource(env, argv[0], sockets, (void**) &descP)) {
+ if (!ESOCK_GET_RESOURCE(env, argv[0], (void**) &descP)) {
return enif_make_badarg(env);
}
SSDBG( descP, ("SOCKET", "nif_info -> get socket info\r\n") );
@@ -3297,7 +3302,7 @@ ERL_NIF_TERM nif_command(ErlNifEnv* env,
"\r\n (e) command data: %T"
"\r\n", cmd, ecdata) );
- result = ncommand(env, cmd, ecdata);
+ result = esock_command(env, cmd, ecdata);
SGDBG( ("SOCKET", "nif_command -> done with result: "
"\r\n %T"
@@ -3311,15 +3316,15 @@ ERL_NIF_TERM nif_command(ErlNifEnv* env,
#if !defined(__WIN32__)
static
-ERL_NIF_TERM ncommand(ErlNifEnv* env, Uint16 cmd, ERL_NIF_TERM ecdata)
+ERL_NIF_TERM esock_command(ErlNifEnv* env, Uint16 cmd, ERL_NIF_TERM ecdata)
{
ERL_NIF_TERM result;
- SGDBG( ("SOCKET", "ncommand -> entry with 0x%lX\r\n", cmd) );
+ SGDBG( ("SOCKET", "esock_command -> entry with 0x%lX\r\n", cmd) );
switch (cmd) {
- case SOCKET_CMD_DEBUG:
- result = ncommand_debug(env, ecdata);
+ case ESOCK_CMD_DEBUG:
+ result = esock_command_debug(env, ecdata);
break;
default:
@@ -3333,7 +3338,7 @@ ERL_NIF_TERM ncommand(ErlNifEnv* env, Uint16 cmd, ERL_NIF_TERM ecdata)
static
-ERL_NIF_TERM ncommand_debug(ErlNifEnv* env, ERL_NIF_TERM ecdata)
+ERL_NIF_TERM esock_command_debug(ErlNifEnv* env, ERL_NIF_TERM ecdata)
{
ERL_NIF_TERM result;
@@ -3346,7 +3351,7 @@ ERL_NIF_TERM ncommand_debug(ErlNifEnv* env, ERL_NIF_TERM ecdata)
data.dbg = FALSE;
result = esock_atom_ok;
} else {
- SGDBG( ("SOCKET", "ncommand_debug -> invalid debug value: %T\r\n",
+ SGDBG( ("SOCKET", "esock_command_debug -> invalid debug value: %T\r\n",
ecdata) );
result = esock_make_error(env, esock_atom_einval);
}
@@ -3366,20 +3371,20 @@ ERL_NIF_TERM ncommand_debug(ErlNifEnv* env, ERL_NIF_TERM ecdata)
*/
#if !defined(__WIN32__)
-#define SOCKET_INFO_REQ_FUNCS \
- SOCKET_INFO_REQ_FUNC_DECL(readers, readMtx, currentReaderP, readersQ) \
- SOCKET_INFO_REQ_FUNC_DECL(writers, writeMtx, currentWriterP, writersQ) \
- SOCKET_INFO_REQ_FUNC_DECL(acceptors, accMtx, currentAcceptorP, acceptorsQ)
+#define ESOCK_INFO_REQ_FUNCS \
+ ESOCK_INFO_REQ_FUNC_DECL(readers, readMtx, currentReaderP, readersQ) \
+ ESOCK_INFO_REQ_FUNC_DECL(writers, writeMtx, currentWriterP, writersQ) \
+ ESOCK_INFO_REQ_FUNC_DECL(acceptors, accMtx, currentAcceptorP, acceptorsQ)
-#define SOCKET_INFO_REQ_FUNC_DECL(F, MTX, CRP, Q) \
+#define ESOCK_INFO_REQ_FUNC_DECL(F, MTX, CRP, Q) \
static \
ERL_NIF_TERM esock_socket_info_##F(ErlNifEnv* env, \
ESockDescriptor* descP) \
{ \
return socket_info_reqs(env, descP, descP->MTX, descP->CRP, &descP->Q); \
}
-SOCKET_INFO_REQ_FUNCS
-#undef SOCKET_INFO_REQ_FUNC_DECL
+ESOCK_INFO_REQ_FUNCS
+#undef ESOCK_INFO_REQ_FUNC_DECL
static
@@ -3461,40 +3466,40 @@ ERL_NIF_TERM nif_supports(ErlNifEnv* env,
return enif_make_badarg(env);
}
- return nsupports(env, key);
+ return esock_supports(env, key);
#endif
}
-/* nsupports - what features do we support
+/* esock_supports - what features do we support
*
* This is to prove information about what features actually
* work on the current platform.
*/
#if !defined(__WIN32__)
static
-ERL_NIF_TERM nsupports(ErlNifEnv* env, int key)
+ERL_NIF_TERM esock_supports(ErlNifEnv* env, int key)
{
ERL_NIF_TERM result;
- SGDBG( ("SOCKET", "nsupports -> entry with 0x%lX\r\n", key) );
+ SGDBG( ("SOCKET", "esock_supports -> entry with 0x%lX\r\n", key) );
switch (key) {
- case SOCKET_SUPPORTS_OPTIONS:
- result = nsupports_options(env);
+ case ESOCK_SUPPORTS_OPTIONS:
+ result = esock_supports_options(env);
break;
- case SOCKET_SUPPORTS_SCTP:
- result = nsupports_sctp(env);
+ case ESOCK_SUPPORTS_SCTP:
+ result = esock_supports_sctp(env);
break;
- case SOCKET_SUPPORTS_IPV6:
- result = nsupports_ipv6(env);
+ case ESOCK_SUPPORTS_IPV6:
+ result = esock_supports_ipv6(env);
break;
- case SOCKET_SUPPORTS_LOCAL:
- result = nsupports_local(env);
+ case ESOCK_SUPPORTS_LOCAL:
+ result = esock_supports_local(env);
break;
default:
@@ -3509,19 +3514,19 @@ ERL_NIF_TERM nsupports(ErlNifEnv* env, int key)
#if !defined(__WIN32__)
static
-ERL_NIF_TERM nsupports_options(ErlNifEnv* env)
+ERL_NIF_TERM esock_supports_options(ErlNifEnv* env)
{
- ERL_NIF_TERM sockOpts = nsupports_options_socket(env);
+ ERL_NIF_TERM sockOpts = esock_supports_options_socket(env);
ERL_NIF_TERM sockOptsT = MKT2(env, esock_atom_socket, sockOpts);
- ERL_NIF_TERM ipOpts = nsupports_options_ip(env);
+ ERL_NIF_TERM ipOpts = esock_supports_options_ip(env);
ERL_NIF_TERM ipOptsT = MKT2(env, esock_atom_ip, ipOpts);
- ERL_NIF_TERM ipv6Opts = nsupports_options_ipv6(env);
+ ERL_NIF_TERM ipv6Opts = esock_supports_options_ipv6(env);
ERL_NIF_TERM ipv6OptsT = MKT2(env, esock_atom_ipv6, ipv6Opts);
- ERL_NIF_TERM tcpOpts = nsupports_options_tcp(env);
+ ERL_NIF_TERM tcpOpts = esock_supports_options_tcp(env);
ERL_NIF_TERM tcpOptsT = MKT2(env, esock_atom_tcp, tcpOpts);
- ERL_NIF_TERM udpOpts = nsupports_options_udp(env);
+ ERL_NIF_TERM udpOpts = esock_supports_options_udp(env);
ERL_NIF_TERM udpOptsT = MKT2(env, esock_atom_udp, udpOpts);
- ERL_NIF_TERM sctpOpts = nsupports_options_sctp(env);
+ ERL_NIF_TERM sctpOpts = esock_supports_options_sctp(env);
ERL_NIF_TERM sctpOptsT = MKT2(env, esock_atom_sctp, sctpOpts);
ERL_NIF_TERM optsA[] = {sockOptsT,
ipOptsT, ipv6OptsT,
@@ -3537,13 +3542,13 @@ ERL_NIF_TERM nsupports_options(ErlNifEnv* env)
#if !defined(__WIN32__)
static
-ERL_NIF_TERM nsupports_options_socket(ErlNifEnv* env)
+ERL_NIF_TERM esock_supports_options_socket(ErlNifEnv* env)
{
SocketTArray opts = TARRAY_CREATE(128);
ERL_NIF_TERM tmp, optsL;
- /* *** SOCKET_OPT_SOCK_ACCEPTCONN => SO_ACCEPTCONN *** */
+ /* *** ESOCK_OPT_SOCK_ACCEPTCONN => SO_ACCEPTCONN *** */
#if defined(SO_ACCEPTCONN)
tmp = MKT2(env, esock_atom_acceptconn, esock_atom_true);
#else
@@ -3552,12 +3557,12 @@ ERL_NIF_TERM nsupports_options_socket(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SOCK_ACCEPTFILTER => SO_ACCEPTFILTER *** */
+ /* *** ESOCK_OPT_SOCK_ACCEPTFILTER => SO_ACCEPTFILTER *** */
tmp = MKT2(env, esock_atom_acceptfilter, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SOCK_BINDTODEVICE => SO_BINDTODEVICE *** */
+ /* *** ESOCK_OPT_SOCK_BINDTODEVICE => SO_BINDTODEVICE *** */
#if defined(SO_BINDTODEVICE)
tmp = MKT2(env, esock_atom_bindtodevice, esock_atom_true);
#else
@@ -3566,7 +3571,7 @@ ERL_NIF_TERM nsupports_options_socket(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SOCK_BROADCAST => SO_BROADCAST *** */
+ /* *** ESOCK_OPT_SOCK_BROADCAST => SO_BROADCAST *** */
#if defined(SO_BROADCAST)
tmp = MKT2(env, esock_atom_broadcast, esock_atom_true);
#else
@@ -3575,12 +3580,12 @@ ERL_NIF_TERM nsupports_options_socket(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SOCK_BUSY_POLL => SO_BUSY_POLL *** */
+ /* *** ESOCK_OPT_SOCK_BUSY_POLL => SO_BUSY_POLL *** */
tmp = MKT2(env, esock_atom_busy_poll, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SOCK_DEBUG => SO_DEBUG *** */
+ /* *** ESOCK_OPT_SOCK_DEBUG => SO_DEBUG *** */
#if defined(SO_DEBUG)
tmp = MKT2(env, esock_atom_debug, esock_atom_true);
#else
@@ -3589,7 +3594,7 @@ ERL_NIF_TERM nsupports_options_socket(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SOCK_DOMAIN => SO_DOMAIN *** */
+ /* *** ESOCK_OPT_SOCK_DOMAIN => SO_DOMAIN *** */
#if defined(SO_DOMAIN)
tmp = MKT2(env, esock_atom_domain, esock_atom_true);
#else
@@ -3598,7 +3603,7 @@ ERL_NIF_TERM nsupports_options_socket(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SOCK_DONTROUTE => SO_DONTROUTE *** */
+ /* *** ESOCK_OPT_SOCK_DONTROUTE => SO_DONTROUTE *** */
#if defined(SO_DONTROUTE)
tmp = MKT2(env, esock_atom_dontroute, esock_atom_true);
#else
@@ -3607,12 +3612,12 @@ ERL_NIF_TERM nsupports_options_socket(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SOCK_ERROR => SO_ERROR *** */
+ /* *** ESOCK_OPT_SOCK_ERROR => SO_ERROR *** */
tmp = MKT2(env, esock_atom_error, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SOCK_KEEPALIVE => SO_KEEPALIVE *** */
+ /* *** ESOCK_OPT_SOCK_KEEPALIVE => SO_KEEPALIVE *** */
#if defined(SO_KEEPALIVE)
tmp = MKT2(env, esock_atom_keepalive, esock_atom_true);
#else
@@ -3621,7 +3626,7 @@ ERL_NIF_TERM nsupports_options_socket(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SOCK_LINGER => SO_LINGER *** */
+ /* *** ESOCK_OPT_SOCK_LINGER => SO_LINGER *** */
#if defined(SO_LINGER)
tmp = MKT2(env, esock_atom_linger, esock_atom_true);
#else
@@ -3630,12 +3635,12 @@ ERL_NIF_TERM nsupports_options_socket(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SOCK_MARK => SO_MARK *** */
+ /* *** ESOCK_OPT_SOCK_MARK => SO_MARK *** */
tmp = MKT2(env, esock_atom_mark, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SOCK_OOBINLINE => SO_OOBINLINE *** */
+ /* *** ESOCK_OPT_SOCK_OOBINLINE => SO_OOBINLINE *** */
#if defined(SO_OOBINLINE)
tmp = MKT2(env, esock_atom_oobinline, esock_atom_true);
#else
@@ -3644,12 +3649,12 @@ ERL_NIF_TERM nsupports_options_socket(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_PASSCRED => SO_PASSCRED *** */
+ /* *** ESOCK_OPT_PASSCRED => SO_PASSCRED *** */
tmp = MKT2(env, esock_atom_passcred, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SOCK_PEEK_OFF => SO_PEEK_OFF *** */
+ /* *** ESOCK_OPT_SOCK_PEEK_OFF => SO_PEEK_OFF *** */
#if defined(SO_PEEK_OFF)
tmp = MKT2(env, esock_atom_peek_off, esock_atom_true);
#else
@@ -3658,12 +3663,12 @@ ERL_NIF_TERM nsupports_options_socket(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SOCK_PEEKCRED => SO_PEEKCRED *** */
+ /* *** ESOCK_OPT_SOCK_PEEKCRED => SO_PEEKCRED *** */
tmp = MKT2(env, esock_atom_peekcred, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SOCK_PRIORITY => SO_PRIORITY *** */
+ /* *** ESOCK_OPT_SOCK_PRIORITY => SO_PRIORITY *** */
#if defined(SO_PRIORITY)
tmp = MKT2(env, esock_atom_priority, esock_atom_true);
#else
@@ -3672,7 +3677,7 @@ ERL_NIF_TERM nsupports_options_socket(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SOCK_PROTOCOL => SO_PROTOCOL *** */
+ /* *** ESOCK_OPT_SOCK_PROTOCOL => SO_PROTOCOL *** */
#if defined(SO_PROTOCOL)
tmp = MKT2(env, esock_atom_protocol, esock_atom_true);
#else
@@ -3681,7 +3686,7 @@ ERL_NIF_TERM nsupports_options_socket(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SOCK_RCVBUF => SO_RCVBUF *** */
+ /* *** ESOCK_OPT_SOCK_RCVBUF => SO_RCVBUF *** */
#if defined(SO_RCVBUF)
tmp = MKT2(env, esock_atom_rcvbuf, esock_atom_true);
#else
@@ -3690,12 +3695,12 @@ ERL_NIF_TERM nsupports_options_socket(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SOCK_RCVBUFFORCE => SO_RCVBUFFORCE *** */
+ /* *** ESOCK_OPT_SOCK_RCVBUFFORCE => SO_RCVBUFFORCE *** */
tmp = MKT2(env, esock_atom_rcvbufforce, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SOCK_RCVLOWAT => SO_RCVLOWAT *** */
+ /* *** ESOCK_OPT_SOCK_RCVLOWAT => SO_RCVLOWAT *** */
#if defined(SO_RCVLOWAT)
tmp = MKT2(env, esock_atom_rcvlowat, esock_atom_true);
#else
@@ -3704,7 +3709,7 @@ ERL_NIF_TERM nsupports_options_socket(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SOCK_RCVTIMEO => SO_RCVTIMEO *** */
+ /* *** ESOCK_OPT_SOCK_RCVTIMEO => SO_RCVTIMEO *** */
#if defined(SO_RCVTIMEO)
tmp = MKT2(env, esock_atom_rcvtimeo, esock_atom_true);
#else
@@ -3713,7 +3718,7 @@ ERL_NIF_TERM nsupports_options_socket(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SOCK_REUSEADDR => SO_REUSEADDR *** */
+ /* *** ESOCK_OPT_SOCK_REUSEADDR => SO_REUSEADDR *** */
#if defined(SO_REUSEADDR)
tmp = MKT2(env, esock_atom_reuseaddr, esock_atom_true);
#else
@@ -3722,7 +3727,7 @@ ERL_NIF_TERM nsupports_options_socket(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SOCK_REUSEPORT => SO_REUSEPORT *** */
+ /* *** ESOCK_OPT_SOCK_REUSEPORT => SO_REUSEPORT *** */
#if defined(SO_REUSEPORT)
tmp = MKT2(env, esock_atom_reuseport, esock_atom_true);
#else
@@ -3731,17 +3736,17 @@ ERL_NIF_TERM nsupports_options_socket(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SOCK_RXQ_OVFL => SO_RXQ_OVFL *** */
+ /* *** ESOCK_OPT_SOCK_RXQ_OVFL => SO_RXQ_OVFL *** */
tmp = MKT2(env, esock_atom_rxq_ovfl, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SOCK_SETFIB => SO_SETFIB *** */
+ /* *** ESOCK_OPT_SOCK_SETFIB => SO_SETFIB *** */
tmp = MKT2(env, esock_atom_setfib, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SOCK_SNDBUF => SO_SNDBUF *** */
+ /* *** ESOCK_OPT_SOCK_SNDBUF => SO_SNDBUF *** */
#if defined(SO_SNDBUF)
tmp = MKT2(env, esock_atom_sndbuf, esock_atom_true);
#else
@@ -3750,12 +3755,12 @@ ERL_NIF_TERM nsupports_options_socket(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SOCK_SNDBUFFORCE => SO_SNDBUFFORCE *** */
+ /* *** ESOCK_OPT_SOCK_SNDBUFFORCE => SO_SNDBUFFORCE *** */
tmp = MKT2(env, esock_atom_sndbufforce, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SOCK_SNDLOWAT => SO_SNDLOWAT *** */
+ /* *** ESOCK_OPT_SOCK_SNDLOWAT => SO_SNDLOWAT *** */
#if defined(SO_SNDLOWAT)
tmp = MKT2(env, esock_atom_sndlowat, esock_atom_true);
#else
@@ -3764,7 +3769,7 @@ ERL_NIF_TERM nsupports_options_socket(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SOCK_SNDTIMEO => SO_SNDTIMEO *** */
+ /* *** ESOCK_OPT_SOCK_SNDTIMEO => SO_SNDTIMEO *** */
#if defined(SO_SNDTIMEO)
tmp = MKT2(env, esock_atom_sndtimeo, esock_atom_true);
#else
@@ -3773,7 +3778,7 @@ ERL_NIF_TERM nsupports_options_socket(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SOCK_TIMESTAMP => SO_TIMESTAMP *** */
+ /* *** ESOCK_OPT_SOCK_TIMESTAMP => SO_TIMESTAMP *** */
#if defined(SO_TIMESTAMP)
tmp = MKT2(env, esock_atom_timestamp, esock_atom_true);
#else
@@ -3782,7 +3787,7 @@ ERL_NIF_TERM nsupports_options_socket(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SOCK_TYPE => SO_TYPE *** */
+ /* *** ESOCK_OPT_SOCK_TYPE => SO_TYPE *** */
#if defined(SO_TYPE)
tmp = MKT2(env, esock_atom_type, esock_atom_true);
#else
@@ -3801,13 +3806,13 @@ ERL_NIF_TERM nsupports_options_socket(ErlNifEnv* env)
#if !defined(__WIN32__)
static
-ERL_NIF_TERM nsupports_options_ip(ErlNifEnv* env)
+ERL_NIF_TERM esock_supports_options_ip(ErlNifEnv* env)
{
SocketTArray opts = TARRAY_CREATE(128);
ERL_NIF_TERM tmp, optsL;
- /* *** SOCKET_OPT_IP_ADD_MEMBERSHIP => IP_ADD_MEMBERSHIP *** */
+ /* *** ESOCK_OPT_IP_ADD_MEMBERSHIP => IP_ADD_MEMBERSHIP *** */
#if defined(IP_ADD_MEMBERSHIP)
tmp = MKT2(env, esock_atom_add_membership, esock_atom_true);
#else
@@ -3816,7 +3821,7 @@ ERL_NIF_TERM nsupports_options_ip(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IP_ADD_SOURCE_MEMBERSHIP => IP_ADD_SOURCE_MEMBERSHIP *** */
+ /* *** ESOCK_OPT_IP_ADD_SOURCE_MEMBERSHIP => IP_ADD_SOURCE_MEMBERSHIP *** */
#if defined(IP_ADD_SOURCE_MEMBERSHIP)
tmp = MKT2(env, esock_atom_add_source_membership, esock_atom_true);
#else
@@ -3825,7 +3830,7 @@ ERL_NIF_TERM nsupports_options_ip(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IP_BLOCK_SOURCE => IP_BLOCK_SOURCE *** */
+ /* *** ESOCK_OPT_IP_BLOCK_SOURCE => IP_BLOCK_SOURCE *** */
#if defined(IP_BLOCK_SOURCE)
tmp = MKT2(env, esock_atom_block_source, esock_atom_true);
#else
@@ -3834,12 +3839,12 @@ ERL_NIF_TERM nsupports_options_ip(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IP_DONTFRAG => IP_DONTFRAG *** */
+ /* *** ESOCK_OPT_IP_DONTFRAG => IP_DONTFRAG *** */
tmp = MKT2(env, esock_atom_dontfrag, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IP_DROP_MEMBERSHIP => IP_DROP_MEMBERSHIP *** */
+ /* *** ESOCK_OPT_IP_DROP_MEMBERSHIP => IP_DROP_MEMBERSHIP *** */
#if defined(IP_DROP_MEMBERSHIP)
tmp = MKT2(env, esock_atom_drop_membership, esock_atom_true);
#else
@@ -3848,7 +3853,7 @@ ERL_NIF_TERM nsupports_options_ip(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IP_DROP_SOURCE_MEMBERSHIP => IP_DROP_SOURCE_MEMBERSHIP *** */
+ /* *** ESOCK_OPT_IP_DROP_SOURCE_MEMBERSHIP => IP_DROP_SOURCE_MEMBERSHIP *** */
#if defined(IP_DROP_SOURCE_MEMBERSHIP)
tmp = MKT2(env, esock_atom_drop_source_membership, esock_atom_true);
#else
@@ -3857,7 +3862,7 @@ ERL_NIF_TERM nsupports_options_ip(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IP_FREEBIND => IP_FREEBIND *** */
+ /* *** ESOCK_OPT_IP_FREEBIND => IP_FREEBIND *** */
#if defined(IP_FREEBIND)
tmp = MKT2(env, esock_atom_freebind, esock_atom_true);
#else
@@ -3866,7 +3871,7 @@ ERL_NIF_TERM nsupports_options_ip(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IP_HDRINCL => IP_HDRINCL *** */
+ /* *** ESOCK_OPT_IP_HDRINCL => IP_HDRINCL *** */
#if defined(IP_HDRINCL)
tmp = MKT2(env, esock_atom_hdrincl, esock_atom_true);
#else
@@ -3875,7 +3880,7 @@ ERL_NIF_TERM nsupports_options_ip(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IP_MINTTL => IP_MINTTL *** */
+ /* *** ESOCK_OPT_IP_MINTTL => IP_MINTTL *** */
#if defined(IP_MINTTL)
tmp = MKT2(env, esock_atom_minttl, esock_atom_true);
#else
@@ -3884,7 +3889,7 @@ ERL_NIF_TERM nsupports_options_ip(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IP_MSFILTER => IP_MSFILTER / IP_MSFILTER_SIZE *** */
+ /* *** ESOCK_OPT_IP_MSFILTER => IP_MSFILTER / IP_MSFILTER_SIZE *** */
#if defined(IP_MSFILTER) && defined(IP_MSFILTER_SIZE)
tmp = MKT2(env, esock_atom_msfilter, esock_atom_true);
#else
@@ -3893,12 +3898,12 @@ ERL_NIF_TERM nsupports_options_ip(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IP_MTU => IP_MTU *** */
+ /* *** ESOCK_OPT_IP_MTU => IP_MTU *** */
tmp = MKT2(env, esock_atom_mtu, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IP_MTU_DISCOVER => IP_MTU_DISCOVER *** */
+ /* *** ESOCK_OPT_IP_MTU_DISCOVER => IP_MTU_DISCOVER *** */
#if defined(IP_MTU_DISCOVER)
tmp = MKT2(env, esock_atom_mtu_discover, esock_atom_true);
#else
@@ -3907,7 +3912,7 @@ ERL_NIF_TERM nsupports_options_ip(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IP_MULTICAST_ALL => IP_MULTICAST_ALL *** */
+ /* *** ESOCK_OPT_IP_MULTICAST_ALL => IP_MULTICAST_ALL *** */
#if defined(IP_MULTICAST_ALL)
tmp = MKT2(env, esock_atom_multicast_all, esock_atom_true);
#else
@@ -3916,7 +3921,7 @@ ERL_NIF_TERM nsupports_options_ip(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IP_MULTICAST_IF => IP_MULTICAST_IF *** */
+ /* *** ESOCK_OPT_IP_MULTICAST_IF => IP_MULTICAST_IF *** */
#if defined(IP_MULTICAST_IF)
tmp = MKT2(env, esock_atom_multicast_if, esock_atom_true);
#else
@@ -3925,7 +3930,7 @@ ERL_NIF_TERM nsupports_options_ip(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IP_MULTICAST_LOOP => IP_MULTICAST_LOOP *** */
+ /* *** ESOCK_OPT_IP_MULTICAST_LOOP => IP_MULTICAST_LOOP *** */
#if defined(IP_MULTICAST_LOOP)
tmp = MKT2(env, esock_atom_multicast_loop, esock_atom_true);
#else
@@ -3934,7 +3939,7 @@ ERL_NIF_TERM nsupports_options_ip(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IP_MULTICAST_TTL => IP_MULTICAST_TTL *** */
+ /* *** ESOCK_OPT_IP_MULTICAST_TTL => IP_MULTICAST_TTL *** */
#if defined(IP_MULTICAST_TTL)
tmp = MKT2(env, esock_atom_multicast_ttl, esock_atom_true);
#else
@@ -3943,7 +3948,7 @@ ERL_NIF_TERM nsupports_options_ip(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IP_NODEFRAG => IP_NODEFRAG *** */
+ /* *** ESOCK_OPT_IP_NODEFRAG => IP_NODEFRAG *** */
#if defined(IP_NODEFRAG)
tmp = MKT2(env, esock_atom_nodefrag, esock_atom_true);
#else
@@ -3952,12 +3957,12 @@ ERL_NIF_TERM nsupports_options_ip(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IP_OPTIONS => IP_OPTIONS *** */
+ /* *** ESOCK_OPT_IP_OPTIONS => IP_OPTIONS *** */
tmp = MKT2(env, esock_atom_options, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IP_PKTINFO => IP_PKTINFO *** */
+ /* *** ESOCK_OPT_IP_PKTINFO => IP_PKTINFO *** */
#if defined(IP_PKTINFO)
tmp = MKT2(env, esock_atom_pktinfo, esock_atom_true);
#else
@@ -3966,7 +3971,7 @@ ERL_NIF_TERM nsupports_options_ip(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IP_RECVDSTADDR => IP_RECVDSTADDR *** */
+ /* *** ESOCK_OPT_IP_RECVDSTADDR => IP_RECVDSTADDR *** */
#if defined(IP_RECVDSTADDR)
tmp = MKT2(env, esock_atom_recvdstaddr, esock_atom_true);
#else
@@ -3975,7 +3980,7 @@ ERL_NIF_TERM nsupports_options_ip(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IP_RECVERR => IP_RECVERR *** */
+ /* *** ESOCK_OPT_IP_RECVERR => IP_RECVERR *** */
#if defined(IP_RECVERR)
tmp = MKT2(env, esock_atom_recverr, esock_atom_true);
#else
@@ -3984,7 +3989,7 @@ ERL_NIF_TERM nsupports_options_ip(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IP_RECVIF => IP_RECVIF *** */
+ /* *** ESOCK_OPT_IP_RECVIF => IP_RECVIF *** */
#if defined(IP_RECVIF)
tmp = MKT2(env, esock_atom_recvif, esock_atom_true);
#else
@@ -3993,7 +3998,7 @@ ERL_NIF_TERM nsupports_options_ip(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IP_RECVOPTS => IP_RECVOPTS *** */
+ /* *** ESOCK_OPT_IP_RECVOPTS => IP_RECVOPTS *** */
#if defined(IP_RECVOPTS)
tmp = MKT2(env, esock_atom_recvopts, esock_atom_true);
#else
@@ -4002,7 +4007,7 @@ ERL_NIF_TERM nsupports_options_ip(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IP_RECVORIGDSTADDR => IP_RECVORIGDSTADDR *** */
+ /* *** ESOCK_OPT_IP_RECVORIGDSTADDR => IP_RECVORIGDSTADDR *** */
#if defined(IP_RECVORIGDSTADDR)
tmp = MKT2(env, esock_atom_recvorigdstaddr, esock_atom_true);
#else
@@ -4011,7 +4016,7 @@ ERL_NIF_TERM nsupports_options_ip(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IP_RECVTOS => IP_RECVTOS *** */
+ /* *** ESOCK_OPT_IP_RECVTOS => IP_RECVTOS *** */
#if defined(IP_RECVTOS)
tmp = MKT2(env, esock_atom_recvtos, esock_atom_true);
#else
@@ -4020,7 +4025,7 @@ ERL_NIF_TERM nsupports_options_ip(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IP_RECVTTL => IP_RECVTTL *** */
+ /* *** ESOCK_OPT_IP_RECVTTL => IP_RECVTTL *** */
#if defined(IP_RECVTTL)
tmp = MKT2(env, esock_atom_recvttl, esock_atom_true);
#else
@@ -4029,7 +4034,7 @@ ERL_NIF_TERM nsupports_options_ip(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IP_RETOPTS => IP_RETOPTS *** */
+ /* *** ESOCK_OPT_IP_RETOPTS => IP_RETOPTS *** */
#if defined(IP_RETOPTS)
tmp = MKT2(env, esock_atom_retopts, esock_atom_true);
#else
@@ -4038,7 +4043,7 @@ ERL_NIF_TERM nsupports_options_ip(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IP_ROUTER_ALERT => IP_ROUTER_ALERT *** */
+ /* *** ESOCK_OPT_IP_ROUTER_ALERT => IP_ROUTER_ALERT *** */
#if defined(IP_ROUTER_ALERT)
tmp = MKT2(env, esock_atom_router_alert, esock_atom_true);
#else
@@ -4047,7 +4052,7 @@ ERL_NIF_TERM nsupports_options_ip(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IP_SENDSRCADDR => IP_SENDSRCADDR *** */
+ /* *** ESOCK_OPT_IP_SENDSRCADDR => IP_SENDSRCADDR *** */
#if defined(IP_SENDSRCADDR)
tmp = MKT2(env, esock_atom_sendsrcaddr, esock_atom_true);
#else
@@ -4056,7 +4061,7 @@ ERL_NIF_TERM nsupports_options_ip(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IP_TOS => IP_TOS *** */
+ /* *** ESOCK_OPT_IP_TOS => IP_TOS *** */
#if defined(IP_TOS)
tmp = MKT2(env, esock_atom_tos, esock_atom_true);
#else
@@ -4065,7 +4070,7 @@ ERL_NIF_TERM nsupports_options_ip(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IP_TRANSPARENT => IP_TRANSPARENT *** */
+ /* *** ESOCK_OPT_IP_TRANSPARENT => IP_TRANSPARENT *** */
#if defined(IP_TRANSPARENT)
tmp = MKT2(env, esock_atom_transparent, esock_atom_true);
#else
@@ -4074,7 +4079,7 @@ ERL_NIF_TERM nsupports_options_ip(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IP_TTL => IP_TTL *** */
+ /* *** ESOCK_OPT_IP_TTL => IP_TTL *** */
#if defined(IP_TTL)
tmp = MKT2(env, esock_atom_ttl, esock_atom_true);
#else
@@ -4083,7 +4088,7 @@ ERL_NIF_TERM nsupports_options_ip(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IP_UNBLOCK_SOURCE => IP_UNBLOCK_SOURCE *** */
+ /* *** ESOCK_OPT_IP_UNBLOCK_SOURCE => IP_UNBLOCK_SOURCE *** */
#if defined(IP_UNBLOCK_SOURCE)
tmp = MKT2(env, esock_atom_unblock_source, esock_atom_true);
#else
@@ -4102,13 +4107,13 @@ ERL_NIF_TERM nsupports_options_ip(ErlNifEnv* env)
#if !defined(__WIN32__)
static
-ERL_NIF_TERM nsupports_options_ipv6(ErlNifEnv* env)
+ERL_NIF_TERM esock_supports_options_ipv6(ErlNifEnv* env)
{
SocketTArray opts = TARRAY_CREATE(128);
ERL_NIF_TERM tmp, optsL;
- /* *** SOCKET_OPT_IPV6_ADDRFORM => IPV6_ADDRFORM *** */
+ /* *** ESOCK_OPT_IPV6_ADDRFORM => IPV6_ADDRFORM *** */
#if defined(IPV6_ADDRFORM)
tmp = MKT2(env, esock_atom_addrform, esock_atom_true);
#else
@@ -4117,7 +4122,7 @@ ERL_NIF_TERM nsupports_options_ipv6(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IPV6_ADD_MEMBERSHIP => IPV6_ADD_MEMBERSHIP *** */
+ /* *** ESOCK_OPT_IPV6_ADD_MEMBERSHIP => IPV6_ADD_MEMBERSHIP *** */
#if defined(IPV6_ADD_MEMBERSHIP)
tmp = MKT2(env, esock_atom_add_membership, esock_atom_true);
#else
@@ -4126,7 +4131,7 @@ ERL_NIF_TERM nsupports_options_ipv6(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IPV6_AUTHHDR => IPV6_AUTHHDR *** */
+ /* *** ESOCK_OPT_IPV6_AUTHHDR => IPV6_AUTHHDR *** */
#if defined(IPV6_AUTHHDR)
tmp = MKT2(env, esock_atom_authhdr, esock_atom_true);
#else
@@ -4135,17 +4140,17 @@ ERL_NIF_TERM nsupports_options_ipv6(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IPV6_AUTH_LEVEL => IPV6_AUTH_LEVEL *** */
+ /* *** ESOCK_OPT_IPV6_AUTH_LEVEL => IPV6_AUTH_LEVEL *** */
tmp = MKT2(env, esock_atom_auth_level, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IPV6_CHECKSUM => IPV6_CHECKSUM *** */
+ /* *** ESOCK_OPT_IPV6_CHECKSUM => IPV6_CHECKSUM *** */
tmp = MKT2(env, esock_atom_checksum, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IPV6_DROP_MEMBERSHIP => IPV6_DROP_MEMBERSHIP *** */
+ /* *** ESOCK_OPT_IPV6_DROP_MEMBERSHIP => IPV6_DROP_MEMBERSHIP *** */
#if defined(IPV6_DROP_MEMBERSHIP)
tmp = MKT2(env, esock_atom_drop_membership, esock_atom_true);
#else
@@ -4154,7 +4159,7 @@ ERL_NIF_TERM nsupports_options_ipv6(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IPV6_DSTOPTS => IPV6_DSTOPTS *** */
+ /* *** ESOCK_OPT_IPV6_DSTOPTS => IPV6_DSTOPTS *** */
#if defined(IPV6_DSTOPTS)
tmp = MKT2(env, esock_atom_dstopts, esock_atom_true);
#else
@@ -4163,22 +4168,22 @@ ERL_NIF_TERM nsupports_options_ipv6(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IPV6_ESP_NETWORK_LEVEL => IPV6_ESP_NETWORK_LEVEL *** */
+ /* *** ESOCK_OPT_IPV6_ESP_NETWORK_LEVEL => IPV6_ESP_NETWORK_LEVEL *** */
tmp = MKT2(env, esock_atom_esp_network_level, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IPV6_ESP_TRANS_LEVEL => IPV6_ESP_TRANS_LEVEL *** */
+ /* *** ESOCK_OPT_IPV6_ESP_TRANS_LEVEL => IPV6_ESP_TRANS_LEVEL *** */
tmp = MKT2(env, esock_atom_esp_trans_level, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IPV6_FAITH => IPV6_FAITH *** */
+ /* *** ESOCK_OPT_IPV6_FAITH => IPV6_FAITH *** */
tmp = MKT2(env, esock_atom_faith, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IPV6_FLOWINFO => IPV6_FLOWINFO *** */
+ /* *** ESOCK_OPT_IPV6_FLOWINFO => IPV6_FLOWINFO *** */
#if defined(IPV6_FLOWINFO)
tmp = MKT2(env, esock_atom_flowinfo, esock_atom_true);
#else
@@ -4187,7 +4192,7 @@ ERL_NIF_TERM nsupports_options_ipv6(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IPV6_HOPLIMIT => IPV6_HOPLIMIT *** */
+ /* *** ESOCK_OPT_IPV6_HOPLIMIT => IPV6_HOPLIMIT *** */
#if defined(IPV6_HOPLIMIT)
tmp = MKT2(env, esock_atom_hoplimit, esock_atom_true);
#else
@@ -4196,7 +4201,7 @@ ERL_NIF_TERM nsupports_options_ipv6(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IPV6_HOPOPTS => IPV6_HOPOPTS *** */
+ /* *** ESOCK_OPT_IPV6_HOPOPTS => IPV6_HOPOPTS *** */
#if defined(IPV6_HOPOPTS)
tmp = MKT2(env, esock_atom_hopopts, esock_atom_true);
#else
@@ -4205,22 +4210,22 @@ ERL_NIF_TERM nsupports_options_ipv6(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IPV6_IPCOMP_LEVEL => IPV6_IPCOMP_LEVEL *** */
+ /* *** ESOCK_OPT_IPV6_IPCOMP_LEVEL => IPV6_IPCOMP_LEVEL *** */
tmp = MKT2(env, esock_atom_ipcomp_level, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IPV6_JOIN_GROUP => IPV6_JOIN_GROUP *** */
+ /* *** ESOCK_OPT_IPV6_JOIN_GROUP => IPV6_JOIN_GROUP *** */
tmp = MKT2(env, esock_atom_join_group, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IPV6_LEAVE_GROUP => IPV6_LEAVE_GROUP *** */
+ /* *** ESOCK_OPT_IPV6_LEAVE_GROUP => IPV6_LEAVE_GROUP *** */
tmp = MKT2(env, esock_atom_leave_group, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IPV6_MTU => IPV6_MTU *** */
+ /* *** ESOCK_OPT_IPV6_MTU => IPV6_MTU *** */
#if defined(IPV6_MTU)
tmp = MKT2(env, esock_atom_mtu, esock_atom_true);
#else
@@ -4229,7 +4234,7 @@ ERL_NIF_TERM nsupports_options_ipv6(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IPV6_MTU_DISCOVER => IPV6_MTU_DISCOVER *** */
+ /* *** ESOCK_OPT_IPV6_MTU_DISCOVER => IPV6_MTU_DISCOVER *** */
#if defined(IPV6_MTU_DISCOVER)
tmp = MKT2(env, esock_atom_mtu_discover, esock_atom_true);
#else
@@ -4238,7 +4243,7 @@ ERL_NIF_TERM nsupports_options_ipv6(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IPV6_MULTICAST_HOPS => IPV6_MULTICAST_HOPS *** */
+ /* *** ESOCK_OPT_IPV6_MULTICAST_HOPS => IPV6_MULTICAST_HOPS *** */
#if defined(IPV6_MULTICAST_HOPS)
tmp = MKT2(env, esock_atom_multicast_hops, esock_atom_true);
#else
@@ -4247,7 +4252,7 @@ ERL_NIF_TERM nsupports_options_ipv6(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IPV6_MULTICAST_IF => IPV6_MULTICAST_IF *** */
+ /* *** ESOCK_OPT_IPV6_MULTICAST_IF => IPV6_MULTICAST_IF *** */
#if defined(IPV6_MULTICAST_IF)
tmp = MKT2(env, esock_atom_multicast_if, esock_atom_true);
#else
@@ -4256,7 +4261,7 @@ ERL_NIF_TERM nsupports_options_ipv6(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IPV6_MULTICAST_LOOP => IPV6_MULTICAST_LOOP *** */
+ /* *** ESOCK_OPT_IPV6_MULTICAST_LOOP => IPV6_MULTICAST_LOOP *** */
#if defined(IPV6_MULTICAST_LOOP)
tmp = MKT2(env, esock_atom_multicast_loop, esock_atom_true);
#else
@@ -4265,17 +4270,17 @@ ERL_NIF_TERM nsupports_options_ipv6(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IPV6_PORTRANGE => IPV6_PORTRANGE *** */
+ /* *** ESOCK_OPT_IPV6_PORTRANGE => IPV6_PORTRANGE *** */
tmp = MKT2(env, esock_atom_portrange, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IPV6_PKTOPTIONS => IPV6_PKTOPTIONS *** */
+ /* *** ESOCK_OPT_IPV6_PKTOPTIONS => IPV6_PKTOPTIONS *** */
tmp = MKT2(env, esock_atom_pktoptions, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IPV6_RECVERR => IPV6_RECVERR *** */
+ /* *** ESOCK_OPT_IPV6_RECVERR => IPV6_RECVERR *** */
#if defined(IPV6_RECVERR)
tmp = MKT2(env, esock_atom_recverr, esock_atom_true);
#else
@@ -4284,7 +4289,7 @@ ERL_NIF_TERM nsupports_options_ipv6(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IPV6_RECVPKTINFO => IPV6_RECVPKTINFO *** */
+ /* *** ESOCK_OPT_IPV6_RECVPKTINFO => IPV6_RECVPKTINFO *** */
#if defined(IPV6_RECVPKTINFO)
tmp = MKT2(env, esock_atom_recvpktinfo, esock_atom_true);
#else
@@ -4293,12 +4298,12 @@ ERL_NIF_TERM nsupports_options_ipv6(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IPV6_RECVTCLASS => IPV6_RECVTCLASS *** */
+ /* *** ESOCK_OPT_IPV6_RECVTCLASS => IPV6_RECVTCLASS *** */
tmp = MKT2(env, esock_atom_recvtclass, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IPV6_ROUTER_ALERT => IPV6_ROUTER_ALERT *** */
+ /* *** ESOCK_OPT_IPV6_ROUTER_ALERT => IPV6_ROUTER_ALERT *** */
#if defined(IPV6_ROUTER_ALERT)
tmp = MKT2(env, esock_atom_router_alert, esock_atom_true);
#else
@@ -4307,7 +4312,7 @@ ERL_NIF_TERM nsupports_options_ipv6(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IPV6_RTHDR => IPV6_RTHDR *** */
+ /* *** ESOCK_OPT_IPV6_RTHDR => IPV6_RTHDR *** */
#if defined(IPV6_RTHDR)
tmp = MKT2(env, esock_atom_rthdr, esock_atom_true);
#else
@@ -4316,12 +4321,12 @@ ERL_NIF_TERM nsupports_options_ipv6(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IPV6_TCLASS => IPV6_TCLASS *** */
+ /* *** ESOCK_OPT_IPV6_TCLASS => IPV6_TCLASS *** */
tmp = MKT2(env, esock_atom_tclass, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IPV6_UNICAST_HOPS => IPV6_UNICAST_HOPS *** */
+ /* *** ESOCK_OPT_IPV6_UNICAST_HOPS => IPV6_UNICAST_HOPS *** */
#if defined(IPV6_UNICAST_HOPS)
tmp = MKT2(env, esock_atom_unicast_hops, esock_atom_true);
#else
@@ -4330,12 +4335,12 @@ ERL_NIF_TERM nsupports_options_ipv6(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IPV6_USE_MIN_MTU => IPV6_USE_MIN_MTU *** */
+ /* *** ESOCK_OPT_IPV6_USE_MIN_MTU => IPV6_USE_MIN_MTU *** */
tmp = MKT2(env, esock_atom_use_min_mtu, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_IPV6_V6ONLY => IPV6_V6ONLY *** */
+ /* *** ESOCK_OPT_IPV6_V6ONLY => IPV6_V6ONLY *** */
#if defined(IPV6_V6ONLY)
tmp = MKT2(env, esock_atom_v6only, esock_atom_true);
#else
@@ -4354,13 +4359,13 @@ ERL_NIF_TERM nsupports_options_ipv6(ErlNifEnv* env)
#if !defined(__WIN32__)
static
-ERL_NIF_TERM nsupports_options_tcp(ErlNifEnv* env)
+ERL_NIF_TERM esock_supports_options_tcp(ErlNifEnv* env)
{
SocketTArray opts = TARRAY_CREATE(32);
ERL_NIF_TERM tmp, optsL;
- /* *** SOCKET_OPT_TCP_CONGESTION => TCP_CONGESTION *** */
+ /* *** ESOCK_OPT_TCP_CONGESTION => TCP_CONGESTION *** */
#if defined(TCP_CONGESTION)
tmp = MKT2(env, esock_atom_congestion, esock_atom_true);
#else
@@ -4369,7 +4374,7 @@ ERL_NIF_TERM nsupports_options_tcp(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_TCP_CORK => TCP_CORK *** */
+ /* *** ESOCK_OPT_TCP_CORK => TCP_CORK *** */
#if defined(TCP_CORK)
tmp = MKT2(env, esock_atom_cork, esock_atom_true);
#else
@@ -4378,27 +4383,27 @@ ERL_NIF_TERM nsupports_options_tcp(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_TCP_INFO => TCP_INFO *** */
+ /* *** ESOCK_OPT_TCP_INFO => TCP_INFO *** */
tmp = MKT2(env, esock_atom_info, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_TCP_KEEPCNT => TCP_KEEPCNT *** */
+ /* *** ESOCK_OPT_TCP_KEEPCNT => TCP_KEEPCNT *** */
tmp = MKT2(env, esock_atom_keepcnt, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_TCP_KEEPIDLE => TCP_KEEPIDLE *** */
+ /* *** ESOCK_OPT_TCP_KEEPIDLE => TCP_KEEPIDLE *** */
tmp = MKT2(env, esock_atom_keepidle, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_TCP_KEEPINTVL => TCP_KEEPINTVL *** */
+ /* *** ESOCK_OPT_TCP_KEEPINTVL => TCP_KEEPINTVL *** */
tmp = MKT2(env, esock_atom_keepintvl, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_TCP_MAXSEG => TCP_MAXSEG *** */
+ /* *** ESOCK_OPT_TCP_MAXSEG => TCP_MAXSEG *** */
#if defined(TCP_MAXSEG)
tmp = MKT2(env, esock_atom_maxseg, esock_atom_true);
#else
@@ -4407,12 +4412,12 @@ ERL_NIF_TERM nsupports_options_tcp(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_TCP_MD5SIG => TCP_MD5SIG *** */
+ /* *** ESOCK_OPT_TCP_MD5SIG => TCP_MD5SIG *** */
tmp = MKT2(env, esock_atom_md5sig, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_TCP_NODELAY => TCP_NODELAY *** */
+ /* *** ESOCK_OPT_TCP_NODELAY => TCP_NODELAY *** */
#if defined(TCP_NODELAY)
tmp = MKT2(env, esock_atom_nodelay, esock_atom_true);
#else
@@ -4421,22 +4426,22 @@ ERL_NIF_TERM nsupports_options_tcp(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_TCP_NOOPT => TCP_NOOPT *** */
+ /* *** ESOCK_OPT_TCP_NOOPT => TCP_NOOPT *** */
tmp = MKT2(env, esock_atom_noopt, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_TCP_NOPUSH => TCP_NOPUSH *** */
+ /* *** ESOCK_OPT_TCP_NOPUSH => TCP_NOPUSH *** */
tmp = MKT2(env, esock_atom_nopush, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_TCP_SYNCNT => TCP_SYNCNT *** */
+ /* *** ESOCK_OPT_TCP_SYNCNT => TCP_SYNCNT *** */
tmp = MKT2(env, esock_atom_syncnt, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_TCP_USER_TIMEOUT => TCP_USER_TIMEOUT *** */
+ /* *** ESOCK_OPT_TCP_USER_TIMEOUT => TCP_USER_TIMEOUT *** */
tmp = MKT2(env, esock_atom_user_timeout, esock_atom_false);
TARRAY_ADD(opts, tmp);
@@ -4451,13 +4456,13 @@ ERL_NIF_TERM nsupports_options_tcp(ErlNifEnv* env)
#if !defined(__WIN32__)
static
-ERL_NIF_TERM nsupports_options_udp(ErlNifEnv* env)
+ERL_NIF_TERM esock_supports_options_udp(ErlNifEnv* env)
{
SocketTArray opts = TARRAY_CREATE(8);
ERL_NIF_TERM tmp, optsL;
- /* *** SOCKET_OPT_UDP_CORK => UDP_CORK *** */
+ /* *** ESOCK_OPT_UDP_CORK => UDP_CORK *** */
#if defined(UDP_CORK)
tmp = MKT2(env, esock_atom_cork, esock_atom_true);
#else
@@ -4476,18 +4481,18 @@ ERL_NIF_TERM nsupports_options_udp(ErlNifEnv* env)
#if !defined(__WIN32__)
static
-ERL_NIF_TERM nsupports_options_sctp(ErlNifEnv* env)
+ERL_NIF_TERM esock_supports_options_sctp(ErlNifEnv* env)
{
SocketTArray opts = TARRAY_CREATE(64);
ERL_NIF_TERM tmp, optsL;
- /* *** SOCKET_OPT_SCTP_ADAPTION_LAYER => SCTP_ADAPTION_LAYER *** */
+ /* *** ESOCK_OPT_SCTP_ADAPTION_LAYER => SCTP_ADAPTION_LAYER *** */
tmp = MKT2(env, esock_atom_adaption_layer, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SCTP_ASSOCINFO => SCTP_ASSOCINFO *** */
+ /* *** ESOCK_OPT_SCTP_ASSOCINFO => SCTP_ASSOCINFO *** */
#if defined(SCTP_ASSOCINFO)
tmp = MKT2(env, esock_atom_associnfo, esock_atom_true);
#else
@@ -4496,32 +4501,32 @@ ERL_NIF_TERM nsupports_options_sctp(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SCTP_AUTH_ACTIVE_KEY => SCTP_AUTH_ACTIVE_KEY *** */
+ /* *** ESOCK_OPT_SCTP_AUTH_ACTIVE_KEY => SCTP_AUTH_ACTIVE_KEY *** */
tmp = MKT2(env, esock_atom_auth_active_key, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SCTP_AUTH_ASCONF => SCTP_AUTH_ASCONF *** */
+ /* *** ESOCK_OPT_SCTP_AUTH_ASCONF => SCTP_AUTH_ASCONF *** */
tmp = MKT2(env, esock_atom_auth_asconf, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SCTP_AUTH_CHUNK => SCTP_AUTH_CHUNK *** */
+ /* *** ESOCK_OPT_SCTP_AUTH_CHUNK => SCTP_AUTH_CHUNK *** */
tmp = MKT2(env, esock_atom_auth_chunk, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SCTP_AUTH_DELETE_KEY => SCTP_AUTH_DELETE_KEY *** */
+ /* *** ESOCK_OPT_SCTP_AUTH_DELETE_KEY => SCTP_AUTH_DELETE_KEY *** */
tmp = MKT2(env, esock_atom_auth_delete_key, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SCTP_AUTH_KEY => SCTP_AUTH_KEY *** */
+ /* *** ESOCK_OPT_SCTP_AUTH_KEY => SCTP_AUTH_KEY *** */
tmp = MKT2(env, esock_atom_auth_key, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SCTP_AUTOCLOSE => SCTP_AUTOCLOSE *** */
+ /* *** ESOCK_OPT_SCTP_AUTOCLOSE => SCTP_AUTOCLOSE *** */
#if defined(SCTP_AUTOCLOSE)
tmp = MKT2(env, esock_atom_autoclose, esock_atom_true);
#else
@@ -4530,22 +4535,22 @@ ERL_NIF_TERM nsupports_options_sctp(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SCTP_CONTEXT => SCTP_CONTEXT *** */
+ /* *** ESOCK_OPT_SCTP_CONTEXT => SCTP_CONTEXT *** */
tmp = MKT2(env, esock_atom_context, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SCTP_DEFAULT_SEND_PARAMS => SCTP_DEFAULT_SEND_PARAMS *** */
+ /* *** ESOCK_OPT_SCTP_DEFAULT_SEND_PARAMS => SCTP_DEFAULT_SEND_PARAMS *** */
tmp = MKT2(env, esock_atom_default_send_params, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SCTP_DELAYED_ACK_TIME => SCTP_DELAYED_ACK_TIME *** */
+ /* *** ESOCK_OPT_SCTP_DELAYED_ACK_TIME => SCTP_DELAYED_ACK_TIME *** */
tmp = MKT2(env, esock_atom_delayed_ack_time, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SCTP_DISABLE_FRAGMENTS => SCTP_DISABLE_FRAGMENTS *** */
+ /* *** ESOCK_OPT_SCTP_DISABLE_FRAGMENTS => SCTP_DISABLE_FRAGMENTS *** */
#if defined(SCTP_DISABLE_FRAGMENTS)
tmp = MKT2(env, esock_atom_disable_fragments, esock_atom_true);
#else
@@ -4554,12 +4559,12 @@ ERL_NIF_TERM nsupports_options_sctp(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SCTP_HMAC_IDENT => SCTP_HMAC_IDENT *** */
+ /* *** ESOCK_OPT_SCTP_HMAC_IDENT => SCTP_HMAC_IDENT *** */
tmp = MKT2(env, esock_atom_hmac_ident, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SCTP_EVENTS => SCTP_EVENTS *** */
+ /* *** ESOCK_OPT_SCTP_EVENTS => SCTP_EVENTS *** */
#if defined(SCTP_EVENTS)
tmp = MKT2(env, esock_atom_events, esock_atom_true);
#else
@@ -4568,22 +4573,22 @@ ERL_NIF_TERM nsupports_options_sctp(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SCTP_EXPLICIT_EOR => SCTP_EXPLICIT_EOR *** */
+ /* *** ESOCK_OPT_SCTP_EXPLICIT_EOR => SCTP_EXPLICIT_EOR *** */
tmp = MKT2(env, esock_atom_explicit_eor, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SCTP_FRAGMENT_INTERLEAVE => SCTP_FRAGMENT_INTERLEAVE *** */
+ /* *** ESOCK_OPT_SCTP_FRAGMENT_INTERLEAVE => SCTP_FRAGMENT_INTERLEAVE *** */
tmp = MKT2(env, esock_atom_fragment_interleave, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SCTP_GET_PEER_ADDR_INFO => SCTP_GET_PEER_ADDR_INFO *** */
+ /* *** ESOCK_OPT_SCTP_GET_PEER_ADDR_INFO => SCTP_GET_PEER_ADDR_INFO *** */
tmp = MKT2(env, esock_atom_get_peer_addr_info, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SCTP_INITMSG => SCTP_INITMSG *** */
+ /* *** ESOCK_OPT_SCTP_INITMSG => SCTP_INITMSG *** */
#if defined(SCTP_INITMSG)
tmp = MKT2(env, esock_atom_initmsg, esock_atom_true);
#else
@@ -4592,17 +4597,17 @@ ERL_NIF_TERM nsupports_options_sctp(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SCTP_I_WANT_MAPPED_V4_ADDR => SCTP_I_WANT_MAPPED_V4_ADDR *** */
+ /* *** ESOCK_OPT_SCTP_I_WANT_MAPPED_V4_ADDR => SCTP_I_WANT_MAPPED_V4_ADDR *** */
tmp = MKT2(env, esock_atom_i_want_mapped_v4_addr, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SCTP_LOCAL_AUTH_CHUNKS => SCTP_LOCAL_AUTH_CHUNKS *** */
+ /* *** ESOCK_OPT_SCTP_LOCAL_AUTH_CHUNKS => SCTP_LOCAL_AUTH_CHUNKS *** */
tmp = MKT2(env, esock_atom_local_auth_chunks, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SCTP_MAXSEG => SCTP_MAXSEG *** */
+ /* *** ESOCK_OPT_SCTP_MAXSEG => SCTP_MAXSEG *** */
#if defined(SCTP_MAXSEG)
tmp = MKT2(env, esock_atom_maxseg, esock_atom_true);
#else
@@ -4611,12 +4616,12 @@ ERL_NIF_TERM nsupports_options_sctp(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SCTP_MAXBURST => SCTP_MAXBURST *** */
+ /* *** ESOCK_OPT_SCTP_MAXBURST => SCTP_MAXBURST *** */
tmp = MKT2(env, esock_atom_maxburst, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SCTP_NODELAY => SCTP_NODELAY *** */
+ /* *** ESOCK_OPT_SCTP_NODELAY => SCTP_NODELAY *** */
#if defined(SCTP_NODELAY)
tmp = MKT2(env, esock_atom_nodelay, esock_atom_true);
#else
@@ -4625,32 +4630,32 @@ ERL_NIF_TERM nsupports_options_sctp(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SCTP_PARTIAL_DELIVERY_POINT => SCTP_PARTIAL_DELIVERY_POINT *** */
+ /* *** ESOCK_OPT_SCTP_PARTIAL_DELIVERY_POINT => SCTP_PARTIAL_DELIVERY_POINT *** */
tmp = MKT2(env, esock_atom_partial_delivery_point, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SCTP_PEER_ADDR_PARAMS => SCTP_PEER_ADDR_PARAMS *** */
+ /* *** ESOCK_OPT_SCTP_PEER_ADDR_PARAMS => SCTP_PEER_ADDR_PARAMS *** */
tmp = MKT2(env, esock_atom_peer_addr_params, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SCTP_PEER_AUTH_CHUNKS => SCTP_PEER_AUTH_CHUNKS *** */
+ /* *** ESOCK_OPT_SCTP_PEER_AUTH_CHUNKS => SCTP_PEER_AUTH_CHUNKS *** */
tmp = MKT2(env, esock_atom_peer_auth_chunks, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SCTP_PRIMARY_ADDR => SCTP_PRIMARY_ADDR *** */
+ /* *** ESOCK_OPT_SCTP_PRIMARY_ADDR => SCTP_PRIMARY_ADDR *** */
tmp = MKT2(env, esock_atom_primary_addr, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SCTP_RESET_STREAMS => SCTP_RESET_STREAMS *** */
+ /* *** ESOCK_OPT_SCTP_RESET_STREAMS => SCTP_RESET_STREAMS *** */
tmp = MKT2(env, esock_atom_reset_streams, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SCTP_RTOINFO => SCTP_RTOINFO *** */
+ /* *** ESOCK_OPT_SCTP_RTOINFO => SCTP_RTOINFO *** */
#if defined(SCTP_RTOINFO)
tmp = MKT2(env, esock_atom_rtoinfo, esock_atom_true);
#else
@@ -4659,17 +4664,17 @@ ERL_NIF_TERM nsupports_options_sctp(ErlNifEnv* env)
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SCTP_SET_PEER_PRIMARY_ADDR => SCTP_SET_PEER_PRIMARY_ADDR *** */
+ /* *** ESOCK_OPT_SCTP_SET_PEER_PRIMARY_ADDR => SCTP_SET_PEER_PRIMARY_ADDR *** */
tmp = MKT2(env, esock_atom_set_peer_primary_addr, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SCTP_STATUS => SCTP_STATUS *** */
+ /* *** ESOCK_OPT_SCTP_STATUS => SCTP_STATUS *** */
tmp = MKT2(env, esock_atom_status, esock_atom_false);
TARRAY_ADD(opts, tmp);
- /* *** SOCKET_OPT_SCTP_USE_EXT_RECVINFO => SCTP_USE_EXT_RECVINFO *** */
+ /* *** ESOCK_OPT_SCTP_USE_EXT_RECVINFO => SCTP_USE_EXT_RECVINFO *** */
tmp = MKT2(env, esock_atom_use_ext_recvinfo, esock_atom_false);
TARRAY_ADD(opts, tmp);
@@ -4684,7 +4689,7 @@ ERL_NIF_TERM nsupports_options_sctp(ErlNifEnv* env)
#if !defined(__WIN32__)
static
-ERL_NIF_TERM nsupports_sctp(ErlNifEnv* env)
+ERL_NIF_TERM esock_supports_sctp(ErlNifEnv* env)
{
ERL_NIF_TERM supports;
@@ -4702,7 +4707,7 @@ ERL_NIF_TERM nsupports_sctp(ErlNifEnv* env)
#if !defined(__WIN32__)
static
-ERL_NIF_TERM nsupports_ipv6(ErlNifEnv* env)
+ERL_NIF_TERM esock_supports_ipv6(ErlNifEnv* env)
{
ERL_NIF_TERM supports;
@@ -4721,7 +4726,7 @@ ERL_NIF_TERM nsupports_ipv6(ErlNifEnv* env)
#if !defined(__WIN32__)
static
-ERL_NIF_TERM nsupports_local(ErlNifEnv* env)
+ERL_NIF_TERM esock_supports_local(ErlNifEnv* env)
{
ERL_NIF_TERM supports;
@@ -4813,7 +4818,7 @@ ERL_NIF_TERM nif_open(ErlNifEnv* env,
#endif
- result = nopen(env, domain, type, proto, netns);
+ result = esock_open(env, domain, type, proto, netns);
SGDBG( ("SOCKET", "nif_open -> done with result: "
"\r\n %T"
@@ -4825,7 +4830,7 @@ ERL_NIF_TERM nif_open(ErlNifEnv* env,
}
-/* nopen - create an endpoint for communication
+/* esock_open - create an endpoint for communication
*
* Assumes the input has been validated.
*
@@ -4836,9 +4841,9 @@ ERL_NIF_TERM nif_open(ErlNifEnv* env,
#if !defined(__WIN32__)
static
-ERL_NIF_TERM nopen(ErlNifEnv* env,
- int domain, int type, int protocol,
- char* netns)
+ERL_NIF_TERM esock_open(ErlNifEnv* env,
+ int domain, int type, int protocol,
+ char* netns)
{
ESockDescriptor* descP;
ERL_NIF_TERM res;
@@ -4849,7 +4854,7 @@ ERL_NIF_TERM nopen(ErlNifEnv* env,
int current_ns = 0;
#endif
- SGDBG( ("SOCKET", "nopen -> entry with"
+ SGDBG( ("SOCKET", "esock_open -> entry with"
"\r\n domain: %d"
"\r\n type: %d"
"\r\n protocol: %d"
@@ -4865,7 +4870,7 @@ ERL_NIF_TERM nopen(ErlNifEnv* env,
if ((sock = sock_open(domain, type, proto)) == INVALID_SOCKET)
return esock_make_error_errno(env, sock_errno());
- SGDBG( ("SOCKET", "nopen -> open success: %d\r\n", sock) );
+ SGDBG( ("SOCKET", "esock_open -> open success: %d\r\n", sock) );
/* NOTE that if the protocol = 0 (default) and the domain is not
@@ -4877,7 +4882,7 @@ ERL_NIF_TERM nopen(ErlNifEnv* env,
&& (domain != AF_LOCAL)
#endif
)
- if (!nopen_which_protocol(sock, &proto)) {
+ if (!esock_open_which_protocol(sock, &proto)) {
if (proto == ESOCK_WHICH_PROTO_ERROR) {
save_errno = sock_errno();
while ((sock_close(sock) == INVALID_SOCKET) &&
@@ -4907,7 +4912,7 @@ ERL_NIF_TERM nopen(ErlNifEnv* env,
return esock_make_error_errno(env, save_errno);
}
- SGDBG( ("SOCKET", "nopen -> event success: %d\r\n", event) );
+ SGDBG( ("SOCKET", "esock_open -> event success: %d\r\n", event) );
SET_NONBLOCKING(sock);
@@ -4919,7 +4924,7 @@ ERL_NIF_TERM nopen(ErlNifEnv* env,
return enif_make_badarg(env);
}
- descP->state = SOCKET_STATE_OPEN;
+ descP->state = ESOCK_STATE_OPEN;
descP->domain = domain;
descP->type = type;
descP->protocol = proto;
@@ -4948,7 +4953,7 @@ ERL_NIF_TERM nopen(ErlNifEnv* env,
if (enif_self(env, &descP->ctrlPid) == NULL)
return esock_make_error(env, atom_exself);
- if (MONP("nopen -> ctrl",
+ if (MONP("esock_open -> ctrl",
env, descP,
&descP->ctrlPid,
&descP->ctrlMon) != 0)
@@ -4962,7 +4967,7 @@ ERL_NIF_TERM nopen(ErlNifEnv* env,
static
-BOOLEAN_T nopen_which_protocol(SOCKET sock, int* proto)
+BOOLEAN_T esock_open_which_protocol(SOCKET sock, int* proto)
{
#if defined(SO_PROTOCOL)
int val;
@@ -5121,7 +5126,7 @@ ERL_NIF_TERM nif_bind(ErlNifEnv* env,
/* Extract arguments and perform preliminary validation */
if ((argc != 2) ||
- !enif_get_resource(env, argv[0], sockets, (void**) &descP)) {
+ !ESOCK_GET_RESOURCE(env, argv[0], (void**) &descP)) {
return enif_make_badarg(env);
}
eSockAddr = argv[1];
@@ -5138,13 +5143,13 @@ ERL_NIF_TERM nif_bind(ErlNifEnv* env,
/* Make sure we are ready
* Not sure how this would even happen, but...
*/
- if (descP->state != SOCKET_STATE_OPEN)
+ if (descP->state != ESOCK_STATE_OPEN)
return esock_make_error(env, atom_exbadstate);
if ((xres = esock_decode_sockaddr(env, eSockAddr, &sockAddr, &addrLen)) != NULL)
return esock_make_error_str(env, xres);
- return nbind(env, descP, &sockAddr, addrLen);
+ return esock_bind(env, descP, &sockAddr, addrLen);
#endif // if defined(__WIN32__)
}
@@ -5152,24 +5157,24 @@ ERL_NIF_TERM nif_bind(ErlNifEnv* env,
#if !defined(__WIN32__)
static
-ERL_NIF_TERM nbind(ErlNifEnv* env,
- ESockDescriptor* descP,
- ESockAddress* sockAddrP,
- unsigned int addrLen)
+ERL_NIF_TERM esock_bind(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ESockAddress* sockAddrP,
+ unsigned int addrLen)
{
int port, ntohs_port;
- SSDBG( descP, ("SOCKET", "nbind -> try bind\r\n") );
+ SSDBG( descP, ("SOCKET", "esock_bind -> try bind\r\n") );
if (IS_SOCKET_ERROR(sock_bind(descP->sock,
(struct sockaddr*) sockAddrP, addrLen))) {
return esock_make_error_errno(env, sock_errno());
}
- SSDBG( descP, ("SOCKET", "nbind -> bound - get port\r\n") );
+ SSDBG( descP, ("SOCKET", "esock_bind -> bound - get port\r\n") );
port = which_address_port(sockAddrP);
- SSDBG( descP, ("SOCKET", "nbind -> port: %d\r\n", port) );
+ SSDBG( descP, ("SOCKET", "esock_bind -> port: %d\r\n", port) );
if (port == 0) {
SOCKLEN_T len = sizeof(ESockAddress);
sys_memzero((char *) sockAddrP, len);
@@ -5181,7 +5186,8 @@ ERL_NIF_TERM nbind(ErlNifEnv* env,
ntohs_port = sock_ntohs(port);
- SSDBG( descP, ("SOCKET", "nbind -> done with port = %d\r\n", ntohs_port) );
+ SSDBG( descP, ("SOCKET",
+ "esock_bind -> done with port = %d\r\n", ntohs_port) );
return esock_make_ok2(env, MKI(env, ntohs_port));
@@ -5221,7 +5227,7 @@ ERL_NIF_TERM nif_connect(ErlNifEnv* env,
sockRef = argv[0];
if ((argc != 2) ||
- !enif_get_resource(env, sockRef, sockets, (void**) &descP)) {
+ !ESOCK_GET_RESOURCE(env, sockRef, (void**) &descP)) {
return enif_make_badarg(env);
}
eSockAddr = argv[1];
@@ -5248,7 +5254,7 @@ ERL_NIF_TERM nif_connect(ErlNifEnv* env,
MLOCK(descP->writeMtx);
MLOCK(descP->cfgMtx);
- res = nconnect(env, descP, sockRef);
+ res = esock_connect(env, descP, sockRef);
MUNLOCK(descP->cfgMtx);
MUNLOCK(descP->writeMtx);
@@ -5262,9 +5268,9 @@ ERL_NIF_TERM nif_connect(ErlNifEnv* env,
#if !defined(__WIN32__)
static
-ERL_NIF_TERM nconnect(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef)
+ERL_NIF_TERM esock_connect(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef)
{
ERL_NIF_TERM res, ref;
int code, sres, save_errno = 0;
@@ -5277,17 +5283,17 @@ ERL_NIF_TERM nconnect(ErlNifEnv* env,
return esock_make_error(env, atom_closed);
if (!IS_OPEN(descP)) {
- SSDBG( descP, ("SOCKET", "nconnect -> not open\r\n") );
+ SSDBG( descP, ("SOCKET", "esock_connect -> not open\r\n") );
return esock_make_error(env, atom_exbadstate);
}
if (IS_CONNECTED(descP)) {
- SSDBG( descP, ("SOCKET", "nconnect -> already connected\r\n") );
+ SSDBG( descP, ("SOCKET", "esock_connect -> already connected\r\n") );
return esock_make_error(env, atom_eisconn);
}
if (IS_CONNECTING(descP) && !is_connector(env, descP)) {
- SSDBG( descP, ("SOCKET", "nconnect -> already connecting\r\n") );
+ SSDBG( descP, ("SOCKET", "esock_connect -> already connecting\r\n") );
return esock_make_error(env, esock_atom_einval);
}
@@ -5301,14 +5307,15 @@ ERL_NIF_TERM nconnect(ErlNifEnv* env,
descP->addrLen);
save_errno = sock_errno();
- SSDBG( descP, ("SOCKET", "nconnect -> connect result: %d, %d\r\n",
+ SSDBG( descP, ("SOCKET", "esock_connect -> connect result: %d, %d\r\n",
code, save_errno) );
if (IS_SOCKET_ERROR(code)) {
switch (save_errno) {
case ERRNO_BLOCK: /* Winsock2 */
case EINPROGRESS: /* Unix & OSE!! */
- SSDBG( descP, ("SOCKET", "nconnect -> would block => select\r\n") );
+ SSDBG( descP, ("SOCKET",
+ "esock_connect -> would block => select\r\n") );
ref = MKREF(env);
@@ -5322,13 +5329,13 @@ ERL_NIF_TERM nconnect(ErlNifEnv* env,
if (enif_self(env, &descP->connPid) == NULL)
return esock_make_error(env, atom_exself);
- if (MONP("nconnect -> conn",
+ if (MONP("esock_connect -> conn",
env, descP,
&descP->connPid,
&descP->connMon) != 0)
return esock_make_error(env, atom_exmon);
- descP->state = SOCKET_STATE_CONNECTING;
+ descP->state = ESOCK_STATE_CONNECTING;
if ((sres = esock_select_write(env, descP->sock, descP, NULL,
sockRef, ref)) < 0) {
@@ -5343,18 +5350,19 @@ ERL_NIF_TERM nconnect(ErlNifEnv* env,
break;
case EISCONN:
- SSDBG( descP, ("SOCKET", "nconnect -> *already* connected\r\n") );
+ SSDBG( descP, ("SOCKET",
+ "esock_connect -> *already* connected\r\n") );
{
/* This is ***strange*** so make sure */
int err = 0;
if (!verify_is_connected(descP, &err)) {
- descP->state = SOCKET_STATE_OPEN; /* restore state */
+ descP->state = ESOCK_STATE_OPEN; /* restore state */
res = esock_make_error_errno(env, err);
} else {
- descP->state = SOCKET_STATE_CONNECTED;
+ descP->state = ESOCK_STATE_CONNECTED;
/* And just to be on the safe side, reset these */
enif_set_pid_undefined(&descP->connPid);
- DEMONP("nconnect -> connected",
+ DEMONP("esock_connect -> connected",
env, descP, &descP->connMon);
descP->isReadable = TRUE;
descP->isWritable = TRUE;
@@ -5364,7 +5372,7 @@ ERL_NIF_TERM nconnect(ErlNifEnv* env,
break;
default:
- SSDBG( descP, ("SOCKET", "nconnect -> other error(1): %d\r\n",
+ SSDBG( descP, ("SOCKET", "esock_connect -> other error(1): %d\r\n",
save_errno) );
res = esock_make_error_errno(env, save_errno);
break;
@@ -5372,11 +5380,11 @@ ERL_NIF_TERM nconnect(ErlNifEnv* env,
} else if (code == 0) { /* ok we are connected */
- SSDBG( descP, ("SOCKET", "nconnect -> connected\r\n") );
+ SSDBG( descP, ("SOCKET", "esock_connect -> connected\r\n") );
- descP->state = SOCKET_STATE_CONNECTED;
+ descP->state = ESOCK_STATE_CONNECTED;
enif_set_pid_undefined(&descP->connPid);
- DEMONP("nconnect -> connected", env, descP, &descP->connMon);
+ DEMONP("esock_connect -> connected", env, descP, &descP->connMon);
descP->isReadable = TRUE;
descP->isWritable = TRUE;
@@ -5385,7 +5393,7 @@ ERL_NIF_TERM nconnect(ErlNifEnv* env,
} else {
/* Do we really need this case? */
- SSDBG( descP, ("SOCKET", "nconnect -> other error(2): %d\r\n",
+ SSDBG( descP, ("SOCKET", "esock_connect -> other error(2): %d\r\n",
save_errno) );
res = esock_make_error_errno(env, save_errno);
@@ -5422,23 +5430,23 @@ ERL_NIF_TERM nif_finalize_connection(ErlNifEnv* env,
/* Extract arguments and perform preliminary validation */
if ((argc != 1) ||
- !enif_get_resource(env, argv[0], sockets, (void**) &descP)) {
+ !ESOCK_GET_RESOURCE(env, argv[0], (void**) &descP)) {
return enif_make_badarg(env);
}
- return nfinalize_connection(env, descP);
+ return esock_finalize_connection(env, descP);
#endif
}
-/* *** nfinalize_connection ***
+/* *** esock_finalize_connection ***
* Perform the final check to verify a connection.
*/
#if !defined(__WIN32__)
static
-ERL_NIF_TERM nfinalize_connection(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_finalize_connection(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
int error;
@@ -5446,13 +5454,14 @@ ERL_NIF_TERM nfinalize_connection(ErlNifEnv* env,
return esock_make_error(env, atom_enotconn);
if (!verify_is_connected(descP, &error)) {
- descP->state = SOCKET_STATE_OPEN; /* restore state */
+ descP->state = ESOCK_STATE_OPEN; /* restore state */
return esock_make_error_errno(env, error);
}
- descP->state = SOCKET_STATE_CONNECTED;
+ descP->state = ESOCK_STATE_CONNECTED;
enif_set_pid_undefined(&descP->connPid);
- DEMONP("nfinalize_connection -> connected", env, descP, &descP->connMon);
+ DEMONP("esock_finalize_connection -> connected",
+ env, descP, &descP->connMon);
descP->isReadable = TRUE;
descP->isWritable = TRUE;
@@ -5565,7 +5574,7 @@ ERL_NIF_TERM nif_listen(ErlNifEnv* env,
/* Extract arguments and perform preliminary validation */
if ((argc != 2) ||
- !enif_get_resource(env, argv[0], sockets, (void**) &descP) ||
+ !ESOCK_GET_RESOURCE(env, argv[0], (void**) &descP) ||
!GET_INT(env, argv[1], &backlog)) {
return enif_make_badarg(env);
}
@@ -5576,7 +5585,7 @@ ERL_NIF_TERM nif_listen(ErlNifEnv* env,
"\r\n backlog: %d"
"\r\n", descP->sock, argv[0], backlog) );
- return nlisten(env, descP, backlog);
+ return esock_listen(env, descP, backlog);
#endif // if defined(__WIN32__)
}
@@ -5585,9 +5594,9 @@ ERL_NIF_TERM nif_listen(ErlNifEnv* env,
#if !defined(__WIN32__)
static
-ERL_NIF_TERM nlisten(ErlNifEnv* env,
- ESockDescriptor* descP,
- int backlog)
+ERL_NIF_TERM esock_listen(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int backlog)
{
/*
@@ -5597,7 +5606,7 @@ ERL_NIF_TERM nlisten(ErlNifEnv* env,
if (IS_CLOSED(descP) || IS_CLOSING(descP))
return esock_make_error(env, atom_closed);
- if (descP->state == SOCKET_STATE_CLOSED)
+ if (descP->state == ESOCK_STATE_CLOSED)
return esock_make_error(env, atom_exbadstate);
if (!IS_OPEN(descP))
@@ -5611,7 +5620,7 @@ ERL_NIF_TERM nlisten(ErlNifEnv* env,
if (IS_SOCKET_ERROR(sock_listen(descP->sock, backlog)))
return esock_make_error_errno(env, sock_errno());
- descP->state = SOCKET_STATE_LISTENING;
+ descP->state = ESOCK_STATE_LISTENING;
return esock_atom_ok;
@@ -5648,7 +5657,7 @@ ERL_NIF_TERM nif_accept(ErlNifEnv* env,
sockRef = argv[0];
if ((argc != 2) ||
- !enif_get_resource(env, sockRef, sockets, (void**) &descP)) {
+ !ESOCK_GET_RESOURCE(env, sockRef, (void**) &descP)) {
return enif_make_badarg(env);
}
ref = argv[1];
@@ -5669,15 +5678,15 @@ ERL_NIF_TERM nif_accept(ErlNifEnv* env,
"\r\n",
descP->sock,
sockRef, ref,
- ((descP->state == SOCKET_STATE_LISTENING) ? "listening" :
- ((descP->state == SOCKET_STATE_ACCEPTING) ? "accepting" : "other")),
+ ((descP->state == ESOCK_STATE_LISTENING) ? "listening" :
+ ((descP->state == ESOCK_STATE_ACCEPTING) ? "accepting" : "other")),
descP->currentAcceptorP,
descP->currentAcceptor.pid,
esock_make_monitor_term(env, &descP->currentAcceptor.mon),
descP->currentAcceptor.env,
descP->currentAcceptor.ref) );
- res = naccept_erts(env, descP, sockRef, ref);
+ res = esock_accept(env, descP, sockRef, ref);
MUNLOCK(descP->accMtx);
@@ -5689,7 +5698,7 @@ ERL_NIF_TERM nif_accept(ErlNifEnv* env,
#if !defined(__WIN32__)
static
-ERL_NIF_TERM naccept_erts(ErlNifEnv* env,
+ERL_NIF_TERM esock_accept(ErlNifEnv* env,
ESockDescriptor* descP,
ERL_NIF_TERM sockRef,
ERL_NIF_TERM ref)
@@ -5700,12 +5709,12 @@ ERL_NIF_TERM naccept_erts(ErlNifEnv* env,
return esock_make_error(env, atom_closed);
switch (descP->state) {
- case SOCKET_STATE_LISTENING:
- res = naccept_listening(env, descP, sockRef, ref);
+ case ESOCK_STATE_LISTENING:
+ res = esock_accept_listening(env, descP, sockRef, ref);
break;
- case SOCKET_STATE_ACCEPTING:
- res = naccept_accepting(env, descP, sockRef, ref);
+ case ESOCK_STATE_ACCEPTING:
+ res = esock_accept_accepting(env, descP, sockRef, ref);
break;
default:
@@ -5718,16 +5727,16 @@ ERL_NIF_TERM naccept_erts(ErlNifEnv* env,
#endif // if !defined(__WIN32__)
-/* *** naccept_listening ***
+/* *** esock_accept_listening ***
*
* We have no active acceptor (and therefor no acceptors in queue).
*/
#if !defined(__WIN32__)
static
-ERL_NIF_TERM naccept_listening(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef,
- ERL_NIF_TERM accRef)
+ERL_NIF_TERM esock_accept_listening(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM accRef)
{
ESockAddress remote;
unsigned int n;
@@ -5736,14 +5745,14 @@ ERL_NIF_TERM naccept_listening(ErlNifEnv* env,
ErlNifPid caller;
ERL_NIF_TERM res;
- SSDBG( descP, ("SOCKET", "naccept_listening -> get caller\r\n") );
+ SSDBG( descP, ("SOCKET", "esock_accept_listening -> get caller\r\n") );
if (enif_self(env, &caller) == NULL)
return esock_make_error(env, atom_exself);
n = sizeof(remote);
sys_memzero((char *) &remote, n);
- SSDBG( descP, ("SOCKET", "naccept_listening -> try accept\r\n") );
+ SSDBG( descP, ("SOCKET", "esock_accept_listening -> try accept\r\n") );
accSock = sock_accept(descP->sock, (struct sockaddr*) &remote, &n);
if (accSock == INVALID_SOCKET) {
@@ -5751,10 +5760,11 @@ ERL_NIF_TERM naccept_listening(ErlNifEnv* env,
SSDBG( descP,
("SOCKET",
- "naccept_listening -> accept failed (%d)\r\n", save_errno) );
+ "esock_accept_listening -> accept failed (%d)\r\n",
+ save_errno) );
- res = naccept_listening_error(env, descP, sockRef, accRef,
- caller, save_errno);
+ res = esock_accept_listening_error(env, descP, sockRef, accRef,
+ caller, save_errno);
} else {
@@ -5762,9 +5772,9 @@ ERL_NIF_TERM naccept_listening(ErlNifEnv* env,
* We got one
*/
- SSDBG( descP, ("SOCKET", "naccept_listening -> success\r\n") );
+ SSDBG( descP, ("SOCKET", "esock_accept_listening -> success\r\n") );
- res = naccept_listening_accept(env, descP, accSock, caller, &remote);
+ res = esock_accept_listening_accept(env, descP, accSock, caller, &remote);
}
@@ -5772,7 +5782,7 @@ ERL_NIF_TERM naccept_listening(ErlNifEnv* env,
}
-/* *** naccept_listening_error ***
+/* *** esock_accept_listening_error ***
*
* The accept call resultet in an error - handle it.
* There are only two cases:
@@ -5780,12 +5790,12 @@ ERL_NIF_TERM naccept_listening(ErlNifEnv* env,
* 2) Other => Return the value (converted to an atom)
*/
static
-ERL_NIF_TERM naccept_listening_error(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef,
- ERL_NIF_TERM accRef,
- ErlNifPid caller,
- int save_errno)
+ERL_NIF_TERM esock_accept_listening_error(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM accRef,
+ ErlNifPid caller,
+ int save_errno)
{
ERL_NIF_TERM res;
@@ -5794,10 +5804,10 @@ ERL_NIF_TERM naccept_listening_error(ErlNifEnv* env,
/* *** Try again later *** */
SSDBG( descP,
- ("SOCKET", "naccept_listening_error -> would block\r\n") );
+ ("SOCKET", "esock_accept_listening_error -> would block\r\n") );
descP->currentAcceptor.pid = caller;
- if (MONP("naccept_listening -> current acceptor",
+ if (MONP("esock_accept_listening -> current acceptor",
env, descP,
&descP->currentAcceptor.pid,
&descP->currentAcceptor.mon) != 0) {
@@ -5809,14 +5819,14 @@ ERL_NIF_TERM naccept_listening_error(ErlNifEnv* env,
descP->currentAcceptor.ref = CP_TERM(descP->currentAcceptor.env,
accRef);
descP->currentAcceptorP = &descP->currentAcceptor;
- res = naccept_busy_retry(env, descP,
- sockRef, accRef,
- NULL, SOCKET_STATE_ACCEPTING);
+ res = esock_accept_busy_retry(env, descP,
+ sockRef, accRef,
+ NULL, ESOCK_STATE_ACCEPTING);
}
} else {
SSDBG( descP,
("SOCKET",
- "naccept_listening -> errno: %d\r\n", save_errno) );
+ "esock_accept_listening -> errno: %d\r\n", save_errno) );
res = esock_make_error_errno(env, save_errno);
}
@@ -5824,20 +5834,20 @@ ERL_NIF_TERM naccept_listening_error(ErlNifEnv* env,
}
-/* *** naccept_listening_accept ***
+/* *** esock_accept_listening_accept ***
*
* The accept call was successful (accepted) - handle the new connection.
*/
static
-ERL_NIF_TERM naccept_listening_accept(ErlNifEnv* env,
- ESockDescriptor* descP,
- SOCKET accSock,
- ErlNifPid caller,
- ESockAddress* remote)
+ERL_NIF_TERM esock_accept_listening_accept(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ SOCKET accSock,
+ ErlNifPid caller,
+ ESockAddress* remote)
{
ERL_NIF_TERM res;
- naccept_accepted(env, descP, accSock, caller, remote, &res);
+ esock_accept_accepted(env, descP, accSock, caller, remote, &res);
return res;
}
@@ -5845,7 +5855,7 @@ ERL_NIF_TERM naccept_listening_accept(ErlNifEnv* env,
-/* *** naccept_accepting ***
+/* *** esock_accept_accepting ***
*
* We have an active acceptor and possibly acceptors waiting in queue.
* If the pid of the calling process is not the pid of the "current process",
@@ -5853,20 +5863,20 @@ ERL_NIF_TERM naccept_listening_accept(ErlNifEnv* env,
*/
#if !defined(__WIN32__)
static
-ERL_NIF_TERM naccept_accepting(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef,
- ERL_NIF_TERM ref)
+ERL_NIF_TERM esock_accept_accepting(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM ref)
{
ErlNifPid caller;
ERL_NIF_TERM res;
- SSDBG( descP, ("SOCKET", "naccept_accepting -> get caller\r\n") );
+ SSDBG( descP, ("SOCKET", "esock_accept_accepting -> get caller\r\n") );
if (enif_self(env, &caller) == NULL)
return esock_make_error(env, atom_exself);
- SSDBG( descP, ("SOCKET", "naccept_accepting -> check: "
+ SSDBG( descP, ("SOCKET", "esock_accept_accepting -> check: "
"are caller current acceptor:"
"\r\n Caller: %T"
"\r\n Current: %T"
@@ -5875,18 +5885,19 @@ ERL_NIF_TERM naccept_accepting(ErlNifEnv* env,
if (COMPARE_PIDS(&descP->currentAcceptor.pid, &caller) == 0) {
SSDBG( descP,
- ("SOCKET", "naccept_accepting -> current acceptor\r\n") );
+ ("SOCKET", "esock_accept_accepting -> current acceptor\r\n") );
- res = naccept_accepting_current(env, descP, sockRef, ref);
+ res = esock_accept_accepting_current(env, descP, sockRef, ref);
} else {
/* Not the "current acceptor", so (maybe) push onto queue */
SSDBG( descP,
- ("SOCKET", "naccept_accepting -> *not* current acceptor\r\n") );
+ ("SOCKET",
+ "esock_accept_accepting -> *not* current acceptor\r\n") );
- res = naccept_accepting_other(env, descP, ref, caller);
+ res = esock_accept_accepting_other(env, descP, ref, caller);
}
@@ -5896,14 +5907,14 @@ ERL_NIF_TERM naccept_accepting(ErlNifEnv* env,
-/* *** naccept_accepting_current ***
+/* *** esock_accept_accepting_current ***
* Handles when the current acceptor makes another attempt.
*/
static
-ERL_NIF_TERM naccept_accepting_current(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef,
- ERL_NIF_TERM accRef)
+ERL_NIF_TERM esock_accept_accepting_current(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM accRef)
{
ESockAddress remote;
unsigned int n;
@@ -5911,7 +5922,8 @@ ERL_NIF_TERM naccept_accepting_current(ErlNifEnv* env,
int save_errno;
ERL_NIF_TERM res;
- SSDBG( descP, ("SOCKET", "naccept_accepting_current -> try accept\r\n") );
+ SSDBG( descP, ("SOCKET",
+ "esock_accept_accepting_current -> try accept\r\n") );
n = sizeof(descP->remote);
sys_memzero((char *) &remote, n);
accSock = sock_accept(descP->sock, (struct sockaddr*) &remote, &n);
@@ -5921,18 +5933,19 @@ ERL_NIF_TERM naccept_accepting_current(ErlNifEnv* env,
SSDBG( descP,
("SOCKET",
- "naccept_accepting_current -> accept failed: %d\r\n",
+ "esock_accept_accepting_current -> accept failed: %d\r\n",
save_errno) );
- res = naccept_accepting_current_error(env, descP, sockRef,
- accRef, save_errno);
+ res = esock_accept_accepting_current_error(env, descP, sockRef,
+ accRef, save_errno);
} else {
- SSDBG( descP, ("SOCKET", "naccept_accepting_current -> accepted\r\n") );
+ SSDBG( descP, ("SOCKET",
+ "esock_accept_accepting_current -> accepted\r\n") );
- res = naccept_accepting_current_accept(env, descP, sockRef,
- accSock, &remote);
+ res = esock_accept_accepting_current_accept(env, descP, sockRef,
+ accSock, &remote);
}
@@ -5940,27 +5953,27 @@ ERL_NIF_TERM naccept_accepting_current(ErlNifEnv* env,
}
-/* *** naccept_accepting_current_accept ***
+/* *** esock_accept_accepting_current_accept ***
* Handles when the current acceptor succeeded in its accept call -
* handle the new connection.
*/
static
-ERL_NIF_TERM naccept_accepting_current_accept(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef,
- SOCKET accSock,
- ESockAddress* remote)
+ERL_NIF_TERM esock_accept_accepting_current_accept(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ SOCKET accSock,
+ ESockAddress* remote)
{
ERL_NIF_TERM res;
- if (naccept_accepted(env, descP, accSock,
- descP->currentAcceptor.pid, remote, &res)) {
+ if (esock_accept_accepted(env, descP, accSock,
+ descP->currentAcceptor.pid, remote, &res)) {
/* Clean out the old cobweb's before trying to invite a new spider */
descP->currentAcceptor.ref = esock_atom_undefined;
enif_set_pid_undefined(&descP->currentAcceptor.pid);
- esock_free_env("naccept_accepting_current_accept - "
+ esock_free_env("esock_accept_accepting_current_accept - "
"current-accept-env",
descP->currentAcceptor.env);
descP->currentAcceptor.env = NULL;
@@ -5969,10 +5982,10 @@ ERL_NIF_TERM naccept_accepting_current_accept(ErlNifEnv* env,
SSDBG( descP,
("SOCKET",
- "naccept_accepting_current_accept -> "
+ "esock_accept_accepting_current_accept -> "
"no more writers\r\n") );
- descP->state = SOCKET_STATE_LISTENING;
+ descP->state = ESOCK_STATE_LISTENING;
descP->currentAcceptorP = NULL;
ESOCK_ASSERT(!descP->currentAcceptor.env);
@@ -5986,18 +5999,18 @@ ERL_NIF_TERM naccept_accepting_current_accept(ErlNifEnv* env,
}
-/* *** naccept_accepting_current_error ***
+/* *** esock_accept_accepting_current_error ***
* The accept call of current acceptor resultet in an error - handle it.
* There are only two cases:
* 1) BLOCK => Attempt a "retry"
* 2) Other => Return the value (converted to an atom)
*/
static
-ERL_NIF_TERM naccept_accepting_current_error(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef,
- ERL_NIF_TERM opRef,
- int save_errno)
+ERL_NIF_TERM esock_accept_accepting_current_error(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM opRef,
+ int save_errno)
{
ESockRequestor req;
ERL_NIF_TERM res, reason;
@@ -6011,13 +6024,13 @@ ERL_NIF_TERM naccept_accepting_current_error(ErlNifEnv* env,
SSDBG( descP,
("SOCKET",
- "naccept_accepting_current_error -> "
+ "esock_accept_accepting_current_error -> "
"would block: try again\r\n") );
- res = naccept_busy_retry(env, descP, sockRef, opRef,
- &descP->currentAcceptor.pid,
- /* No state change */
- descP->state);
+ res = esock_accept_busy_retry(env, descP, sockRef, opRef,
+ &descP->currentAcceptor.pid,
+ /* No state change */
+ descP->state);
} else {
@@ -6026,12 +6039,13 @@ ERL_NIF_TERM naccept_accepting_current_error(ErlNifEnv* env,
while (acceptor_pop(env, descP, &req)) {
SSDBG( descP,
- ("SOCKET", "naccept_accepting_current_error -> abort %T\r\n",
+ ("SOCKET",
+ "esock_accept_accepting_current_error -> abort %T\r\n",
req.pid) );
esock_send_abort_msg(env, sockRef, req.ref, req.env,
reason, &req.pid);
req.env = NULL;
- DEMONP("naccept_accepting_current_error -> pop'ed writer",
+ DEMONP("esock_accept_accepting_current_error -> pop'ed writer",
env, descP, &req.mon);
}
@@ -6041,16 +6055,16 @@ ERL_NIF_TERM naccept_accepting_current_error(ErlNifEnv* env,
}
-/* *** naccept_accepting_other ***
+/* *** esock_accept_accepting_other ***
* Handles when the another acceptor makes an attempt, which
* results (maybe) in the request beeing pushed onto the
* acceptor queue.
*/
static
-ERL_NIF_TERM naccept_accepting_other(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM ref,
- ErlNifPid caller)
+ERL_NIF_TERM esock_accept_accepting_other(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM ref,
+ ErlNifPid caller)
{
ERL_NIF_TERM result;
@@ -6065,18 +6079,18 @@ ERL_NIF_TERM naccept_accepting_other(ErlNifEnv* env,
-/* *** naccept_busy_retry ***
+/* *** esock_accept_busy_retry ***
*
* Perform a retry select. If successful, set nextState.
*/
#if !defined(__WIN32__)
static
-ERL_NIF_TERM naccept_busy_retry(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef,
- ERL_NIF_TERM accRef,
- ErlNifPid* pid,
- unsigned int nextState)
+ERL_NIF_TERM esock_accept_busy_retry(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM accRef,
+ ErlNifPid* pid,
+ unsigned int nextState)
{
int sres;
ERL_NIF_TERM res, reason;
@@ -6095,17 +6109,17 @@ ERL_NIF_TERM naccept_busy_retry(ErlNifEnv* env,
-/* *** naccept_accepted ***
+/* *** esock_accept_accepted ***
*
* Generic function handling a successful accept.
*/
static
-BOOLEAN_T naccept_accepted(ErlNifEnv* env,
- ESockDescriptor* descP,
- SOCKET accSock,
- ErlNifPid pid,
- ESockAddress* remote,
- ERL_NIF_TERM* result)
+BOOLEAN_T esock_accept_accepted(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ SOCKET accSock,
+ ErlNifPid pid,
+ ESockAddress* remote,
+ ERL_NIF_TERM* result)
{
ESockDescriptor* accDescP;
HANDLE accEvent;
@@ -6143,7 +6157,7 @@ BOOLEAN_T naccept_accepted(ErlNifEnv* env,
enif_release_resource(accDescP);
accDescP->ctrlPid = pid;
- if (MONP("naccept_accepted -> ctrl",
+ if (MONP("esock_accept_accepted -> ctrl",
env, accDescP,
&accDescP->ctrlPid,
&accDescP->ctrlMon) != 0) {
@@ -6156,7 +6170,7 @@ BOOLEAN_T naccept_accepted(ErlNifEnv* env,
accDescP->remote = *remote;
SET_NONBLOCKING(accDescP->sock);
- accDescP->state = SOCKET_STATE_CONNECTED;
+ accDescP->state = ESOCK_STATE_CONNECTED;
accDescP->isReadable = TRUE;
accDescP->isWritable = TRUE;
@@ -6209,7 +6223,7 @@ ERL_NIF_TERM nif_send(ErlNifEnv* env,
sockRef = argv[0]; // We need this in case we send in case we send abort
sendRef = argv[1];
- if (!enif_get_resource(env, sockRef, sockets, (void**) &descP)) {
+ if (!ESOCK_GET_RESOURCE(env, sockRef, (void**) &descP)) {
return enif_make_badarg(env);
}
@@ -6234,7 +6248,7 @@ ERL_NIF_TERM nif_send(ErlNifEnv* env,
* is done!
*/
- res = nsend(env, descP, sockRef, sendRef, &sndData, flags);
+ res = esock_send(env, descP, sockRef, sendRef, &sndData, flags);
MUNLOCK(descP->writeMtx);
@@ -6245,7 +6259,7 @@ ERL_NIF_TERM nif_send(ErlNifEnv* env,
-/* *** nsend ***
+/* *** esock_send ***
*
* Do the actual send.
* Do some initial writer checks, do the actual send and then
@@ -6254,12 +6268,12 @@ ERL_NIF_TERM nif_send(ErlNifEnv* env,
*/
#if !defined(__WIN32__)
static
-ERL_NIF_TERM nsend(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef,
- ERL_NIF_TERM sendRef,
- ErlNifBinary* sndDataP,
- int flags)
+ERL_NIF_TERM esock_send(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM sendRef,
+ ErlNifBinary* sndDataP,
+ int flags)
{
int save_errno;
ssize_t written;
@@ -6331,16 +6345,15 @@ ERL_NIF_TERM nif_sendto(ErlNifEnv* env,
/* Extract arguments and perform preliminary validation */
if ((argc != 5) ||
- !enif_get_resource(env, argv[0], sockets, (void**) &descP) ||
!GET_BIN(env, argv[2], &sndData) ||
!GET_UINT(env, argv[4], &eflags)) {
return enif_make_badarg(env);
}
- sockRef = argv[0]; // We need this in case we send in case we send abort
+ sockRef = argv[0]; // We need this in case we send abort (to the caller)
sendRef = argv[1];
eSockAddr = argv[3];
- if (!enif_get_resource(env, sockRef, sockets, (void**) &descP)) {
+ if (!ESOCK_GET_RESOURCE(env, sockRef, (void**) &descP)) {
return enif_make_badarg(env);
}
@@ -6369,8 +6382,8 @@ ERL_NIF_TERM nif_sendto(ErlNifEnv* env,
MLOCK(descP->writeMtx);
- res = nsendto(env, descP, sockRef, sendRef, &sndData, flags,
- &remoteAddr, remoteAddrLen);
+ res = esock_sendto(env, descP, sockRef, sendRef, &sndData, flags,
+ &remoteAddr, remoteAddrLen);
MUNLOCK(descP->writeMtx);
@@ -6386,14 +6399,14 @@ ERL_NIF_TERM nif_sendto(ErlNifEnv* env,
#if !defined(__WIN32__)
static
-ERL_NIF_TERM nsendto(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef,
- ERL_NIF_TERM sendRef,
- ErlNifBinary* dataP,
- int flags,
- ESockAddress* toAddrP,
- unsigned int toAddrLen)
+ERL_NIF_TERM esock_sendto(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM sendRef,
+ ErlNifBinary* dataP,
+ int flags,
+ ESockAddress* toAddrP,
+ unsigned int toAddrLen)
{
int save_errno;
ssize_t written;
@@ -6468,11 +6481,11 @@ ERL_NIF_TERM nif_sendmsg(ErlNifEnv* env,
!GET_UINT(env, argv[3], &eflags)) {
return enif_make_badarg(env);
}
- sockRef = argv[0]; // We need this in case we send in case we send abort
+ sockRef = argv[0]; // We need this in case we send abort (to the caller)
sendRef = argv[1];
eMsgHdr = argv[2];
- if (!enif_get_resource(env, sockRef, sockets, (void**) &descP)) {
+ if (!ESOCK_GET_RESOURCE(env, sockRef, (void**) &descP)) {
return enif_make_badarg(env);
}
@@ -6489,7 +6502,7 @@ ERL_NIF_TERM nif_sendmsg(ErlNifEnv* env,
MLOCK(descP->writeMtx);
- res = nsendmsg_erts(env, descP, sockRef, sendRef, eMsgHdr, flags);
+ res = esock_sendmsg(env, descP, sockRef, sendRef, eMsgHdr, flags);
MUNLOCK(descP->writeMtx);
@@ -6506,7 +6519,7 @@ ERL_NIF_TERM nif_sendmsg(ErlNifEnv* env,
#if !defined(__WIN32__)
static
-ERL_NIF_TERM nsendmsg_erts(ErlNifEnv* env,
+ERL_NIF_TERM esock_sendmsg(ErlNifEnv* env,
ESockDescriptor* descP,
ERL_NIF_TERM sockRef,
ERL_NIF_TERM sendRef,
@@ -6540,7 +6553,8 @@ ERL_NIF_TERM nsendmsg_erts(ErlNifEnv* env,
/* We don't need the address */
- SSDBG( descP, ("SOCKET", "nsendmsg_erts -> connected: no address\r\n") );
+ SSDBG( descP, ("SOCKET",
+ "esock_sendmsg -> connected: no address\r\n") );
msgHdr.msg_name = NULL;
msgHdr.msg_namelen = 0;
@@ -6555,7 +6569,7 @@ ERL_NIF_TERM nsendmsg_erts(ErlNifEnv* env,
if (!GET_MAP_VAL(env, eMsgHdr, esock_atom_addr, &eAddr))
return esock_make_error(env, esock_atom_einval);
- SSDBG( descP, ("SOCKET", "nsendmsg_erts -> not connected: "
+ SSDBG( descP, ("SOCKET", "esock_sendmsg -> not connected: "
"\r\n address: %T"
"\r\n", eAddr) );
@@ -6575,7 +6589,7 @@ ERL_NIF_TERM nsendmsg_erts(ErlNifEnv* env,
if (!GET_LIST_LEN(env, eIOV, &iovLen) && (iovLen > 0))
return esock_make_error(env, esock_atom_einval);
- SSDBG( descP, ("SOCKET", "nsendmsg_erts -> iov length: %d\r\n", iovLen) );
+ SSDBG( descP, ("SOCKET", "esock_sendmsg -> iov length: %d\r\n", iovLen) );
iovBins = MALLOC(iovLen * sizeof(ErlNifBinary));
ESOCK_ASSERT( (iovBins != NULL) );
@@ -6593,7 +6607,7 @@ ERL_NIF_TERM nsendmsg_erts(ErlNifEnv* env,
ctrlBufLen = 0;
ctrlBuf = NULL;
}
- SSDBG( descP, ("SOCKET", "nsendmsg_erts -> optional ctrl: "
+ SSDBG( descP, ("SOCKET", "esock_sendmsg -> optional ctrl: "
"\r\n ctrlBuf: 0x%lX"
"\r\n ctrlBufLen: %d"
"\r\n eCtrl: %T\r\n", ctrlBuf, ctrlBufLen, eCtrl) );
@@ -6611,7 +6625,8 @@ ERL_NIF_TERM nsendmsg_erts(ErlNifEnv* env,
SSDBG( descP, ("SOCKET",
- "nsendmsg_erts -> total (iov) data size: %d\r\n", dataSize) );
+ "esock_sendmsg -> "
+ "total (iov) data size: %d\r\n", dataSize) );
/* Decode the ctrl and initiate that part of the msghdr.
@@ -6769,10 +6784,10 @@ ERL_NIF_TERM nif_recv(ErlNifEnv* env,
!GET_UINT(env, argv[3], &eflags)) {
return enif_make_badarg(env);
}
- sockRef = argv[0]; // We need this in case we case we send abort
+ sockRef = argv[0]; // We need this in case we send abort (to the caller)
recvRef = argv[1];
- if (!enif_get_resource(env, sockRef, sockets, (void**) &descP)) {
+ if (!ESOCK_GET_RESOURCE(env, sockRef, (void**) &descP)) {
return enif_make_badarg(env);
}
@@ -6789,7 +6804,7 @@ ERL_NIF_TERM nif_recv(ErlNifEnv* env,
* is done!
*/
- res = nrecv(env, descP, sockRef, recvRef, len, flags);
+ res = esock_recv(env, descP, sockRef, recvRef, len, flags);
MUNLOCK(descP->readMtx);
@@ -6806,12 +6821,12 @@ ERL_NIF_TERM nif_recv(ErlNifEnv* env,
*/
#if !defined(__WIN32__)
static
-ERL_NIF_TERM nrecv(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef,
- ERL_NIF_TERM recvRef,
- int len,
- int flags)
+ERL_NIF_TERM esock_recv(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM recvRef,
+ int len,
+ int flags)
{
ssize_t read;
ErlNifBinary buf;
@@ -6819,7 +6834,7 @@ ERL_NIF_TERM nrecv(ErlNifEnv* env,
int save_errno;
int bufSz = (len ? len : descP->rBufSz);
- SSDBG( descP, ("SOCKET", "nrecv -> entry with"
+ SSDBG( descP, ("SOCKET", "esock_recv -> entry with"
"\r\n len: %d (%d:%d)"
"\r\n flags: %d"
"\r\n", len, descP->rNumCnt, bufSz, flags) );
@@ -6842,7 +6857,7 @@ ERL_NIF_TERM nrecv(ErlNifEnv* env,
SOCK_CNT_INC(env, descP, sockRef, atom_read_tries, &descP->readTries, 1);
// If it fails (read = -1), we need errno...
- SSDBG( descP, ("SOCKET", "nrecv -> try read (%d)\r\n", buf.size) );
+ SSDBG( descP, ("SOCKET", "esock_recv -> try read (%d)\r\n", buf.size) );
read = sock_recv(descP->sock, buf.data, buf.size, flags);
if (IS_SOCKET_ERROR(read)) {
save_errno = sock_errno();
@@ -6850,7 +6865,8 @@ ERL_NIF_TERM nrecv(ErlNifEnv* env,
save_errno = -1; // The value does not actually matter in this case
}
- SSDBG( descP, ("SOCKET", "nrecv -> read: %d (%d)\r\n", read, save_errno) );
+ SSDBG( descP, ("SOCKET",
+ "esock_recv -> read: %d (%d)\r\n", read, save_errno) );
return recv_check_result(env, descP,
read, len,
@@ -6911,10 +6927,10 @@ ERL_NIF_TERM nif_recvfrom(ErlNifEnv* env,
!GET_UINT(env, argv[3], &eflags)) {
return enif_make_badarg(env);
}
- sockRef = argv[0]; // We need this in case we send in case we send abort
+ sockRef = argv[0]; // We need this in case we send abort (to the caller)
recvRef = argv[1];
- if (!enif_get_resource(env, sockRef, sockets, (void**) &descP)) {
+ if (!ESOCK_GET_RESOURCE(env, sockRef, (void**) &descP)) {
return enif_make_badarg(env);
}
@@ -6951,7 +6967,7 @@ ERL_NIF_TERM nif_recvfrom(ErlNifEnv* env,
* </KOLLA>
*/
- res = nrecvfrom_erts(env, descP, sockRef, recvRef, bufSz, flags);
+ res = esock_recvfrom(env, descP, sockRef, recvRef, bufSz, flags);
MUNLOCK(descP->readMtx);
@@ -6967,7 +6983,7 @@ ERL_NIF_TERM nif_recvfrom(ErlNifEnv* env,
*/
#if !defined(__WIN32__)
static
-ERL_NIF_TERM nrecvfrom_erts(ErlNifEnv* env,
+ERL_NIF_TERM esock_recvfrom(ErlNifEnv* env,
ESockDescriptor* descP,
ERL_NIF_TERM sockRef,
ERL_NIF_TERM recvRef,
@@ -6982,7 +6998,7 @@ ERL_NIF_TERM nrecvfrom_erts(ErlNifEnv* env,
ERL_NIF_TERM readerCheck;
int bufSz = (len ? len : descP->rBufSz);
- SSDBG( descP, ("SOCKET", "nrecvfrom_erts -> entry with"
+ SSDBG( descP, ("SOCKET", "esock_recvfrom -> entry with"
"\r\n len: %d (%d)"
"\r\n flags: %d"
"\r\n", len, bufSz, flags) );
@@ -7080,10 +7096,10 @@ ERL_NIF_TERM nif_recvmsg(ErlNifEnv* env,
!GET_UINT(env, argv[4], &eflags)) {
return enif_make_badarg(env);
}
- sockRef = argv[0]; // We need this in case we send in case we send abort
+ sockRef = argv[0]; // We need this in case we send abort (to the caller)
recvRef = argv[1];
- if (!enif_get_resource(env, sockRef, sockets, (void**) &descP)) {
+ if (!ESOCK_GET_RESOURCE(env, sockRef, (void**) &descP)) {
return enif_make_badarg(env);
}
@@ -7121,7 +7137,7 @@ ERL_NIF_TERM nif_recvmsg(ErlNifEnv* env,
* </KOLLA>
*/
- res = nrecvmsg_erts(env, descP, sockRef, recvRef, bufSz, ctrlSz, flags);
+ res = esock_recvmsg(env, descP, sockRef, recvRef, bufSz, ctrlSz, flags);
MUNLOCK(descP->readMtx);
@@ -7137,7 +7153,7 @@ ERL_NIF_TERM nif_recvmsg(ErlNifEnv* env,
*/
#if !defined(__WIN32__)
static
-ERL_NIF_TERM nrecvmsg_erts(ErlNifEnv* env,
+ERL_NIF_TERM esock_recvmsg(ErlNifEnv* env,
ESockDescriptor* descP,
ERL_NIF_TERM sockRef,
ERL_NIF_TERM recvRef,
@@ -7157,7 +7173,7 @@ ERL_NIF_TERM nrecvmsg_erts(ErlNifEnv* env,
ERL_NIF_TERM readerCheck;
ESockAddress addr;
- SSDBG( descP, ("SOCKET", "nrecvmsg_erts -> entry with"
+ SSDBG( descP, ("SOCKET", "esock_recvmsg -> entry with"
"\r\n bufSz: %d (%d)"
"\r\n ctrlSz: %d (%d)"
"\r\n flags: %d"
@@ -7246,28 +7262,28 @@ ERL_NIF_TERM nif_close(ErlNifEnv* env,
ESockDescriptor* descP;
if ((argc != 1) ||
- !enif_get_resource(env, argv[0], sockets, (void**) &descP)) {
+ !ESOCK_GET_RESOURCE(env, argv[0], (void**) &descP)) {
return enif_make_badarg(env);
}
if (IS_CLOSED(descP) || IS_CLOSING(descP))
return esock_make_error(env, atom_closed);
- return nclose(env, descP);
+ return esock_close(env, descP);
#endif // if defined(__WIN32__)
}
#if !defined(__WIN32__)
static
-ERL_NIF_TERM nclose(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_close(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
ERL_NIF_TERM reply, reason;
BOOLEAN_T doClose;
SSDBG( descP, ("SOCKET",
- "nclose -> [%d] entry (0x%lX, 0x%lX, 0x%lX, 0x%lX)\r\n",
+ "esock_close -> [%d] entry (0x%lX, 0x%lX, 0x%lX, 0x%lX)\r\n",
descP->sock,
descP->state,
descP->currentWriterP,
@@ -7276,10 +7292,10 @@ ERL_NIF_TERM nclose(ErlNifEnv* env,
MLOCK(descP->closeMtx);
- doClose = nclose_check(env, descP, &reason);
+ doClose = esock_close_check(env, descP, &reason);
if (doClose) {
- reply = nclose_do(env, descP);
+ reply = esock_close_do(env, descP);
} else {
reply = esock_make_error(env, reason);
}
@@ -7287,7 +7303,7 @@ ERL_NIF_TERM nclose(ErlNifEnv* env,
MUNLOCK(descP->closeMtx);
SSDBG( descP,
- ("SOCKET", "nclose -> [%d] done when: "
+ ("SOCKET", "esock_close -> [%d] done when: "
"\r\n state: 0x%lX"
"\r\n reply: %T"
"\r\n", descP->sock, descP->state, reply) );
@@ -7297,23 +7313,23 @@ ERL_NIF_TERM nclose(ErlNifEnv* env,
-/* *** nclose_check ***
+/* *** esock_close_check ***
*
* Check if we should try to perform the first stage close.
*/
static
-BOOLEAN_T nclose_check(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM* reason)
+BOOLEAN_T esock_close_check(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM* reason)
{
BOOLEAN_T doClose;
- if (descP->state == SOCKET_STATE_CLOSED) {
+ if (descP->state == ESOCK_STATE_CLOSED) {
doClose = FALSE;
*reason = atom_closed;
- } else if (descP->state == SOCKET_STATE_CLOSING) {
+ } else if (descP->state == ESOCK_STATE_CLOSING) {
doClose = FALSE;
*reason = atom_closing;
@@ -7343,7 +7359,7 @@ BOOLEAN_T nclose_check(ErlNifEnv* env,
* </KOLLA>
*/
- if (MONP("nclose_check -> closer",
+ if (MONP("esock_close_check -> closer",
env, descP,
&descP->closerPid,
&descP->closerMon) != 0) {
@@ -7354,7 +7370,7 @@ BOOLEAN_T nclose_check(ErlNifEnv* env,
} else {
descP->closeLocal = TRUE;
- descP->state = SOCKET_STATE_CLOSING;
+ descP->state = ESOCK_STATE_CLOSING;
descP->isReadable = FALSE;
descP->isWritable = FALSE;
doClose = TRUE;
@@ -7370,13 +7386,13 @@ BOOLEAN_T nclose_check(ErlNifEnv* env,
-/* *** nclose_do ***
+/* *** esock_close_do ***
*
* Perform (do) the first stage close.
*/
static
-ERL_NIF_TERM nclose_do(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_close_do(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
int domain = descP->domain;
int type = descP->type;
@@ -7384,7 +7400,7 @@ ERL_NIF_TERM nclose_do(ErlNifEnv* env,
int sres;
ERL_NIF_TERM reply, reason;
- descP->closeEnv = esock_alloc_env("nclose-do - close-env");
+ descP->closeEnv = esock_alloc_env("esock_close_do - close-env");
descP->closeRef = MKREF(descP->closeEnv);
sres = esock_select_stop(env, descP->sock, descP);
@@ -7393,7 +7409,8 @@ ERL_NIF_TERM nclose_do(ErlNifEnv* env,
/* Prep done - inform the caller it can finalize (close) directly */
SSDBG( descP,
- ("SOCKET", "nclose -> [%d] stop was called\r\n", descP->sock) );
+ ("SOCKET",
+ "esock_close -> [%d] stop was called\r\n", descP->sock) );
dec_socket(domain, type, protocol);
reply = esock_atom_ok;
@@ -7403,7 +7420,7 @@ ERL_NIF_TERM nclose_do(ErlNifEnv* env,
/* The stop callback function has been *scheduled* which means that we
* have to wait for it to complete. */
SSDBG( descP,
- ("SOCKET", "nclose -> [%d] stop was scheduled\r\n",
+ ("SOCKET", "esock_close -> [%d] stop was scheduled\r\n",
descP->sock) );
dec_socket(domain, type, protocol); // SHALL WE DO THIS AT finalize?
@@ -7412,7 +7429,7 @@ ERL_NIF_TERM nclose_do(ErlNifEnv* env,
} else {
SSDBG( descP,
- ("SOCKET", "nclose -> [%d] stop failed: %d\r\n",
+ ("SOCKET", "esock_close -> [%d] stop failed: %d\r\n",
descP->sock, sres) );
/* <KOLLA>
@@ -7426,7 +7443,7 @@ ERL_NIF_TERM nclose_do(ErlNifEnv* env,
*/
// Do we need this?
- DEMONP("nclose_do -> closer", env, descP, &descP->closerMon);
+ DEMONP("esock_close_do -> closer", env, descP, &descP->closerMon);
reason = MKT2(env, esock_atom_select_failed, MKI(env, sres));
reply = esock_make_error(env, reason);
@@ -7464,22 +7481,22 @@ ERL_NIF_TERM nif_finalize_close(ErlNifEnv* env,
/* Extract arguments and perform preliminary validation */
if ((argc != 1) ||
- !enif_get_resource(env, argv[0], sockets, (void**) &descP)) {
+ !ESOCK_GET_RESOURCE(env, argv[0], (void**) &descP)) {
return enif_make_badarg(env);
}
- return nfinalize_close(env, descP);
+ return esock_finalize_close(env, descP);
#endif // if defined(__WIN32__)
}
-/* *** nfinalize_close ***
+/* *** esock_finalize_close ***
* Perform the final step in the socket close.
*/
#if !defined(__WIN32__)
static
-ERL_NIF_TERM nfinalize_close(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_finalize_close(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
ERL_NIF_TERM reply;
@@ -7516,7 +7533,7 @@ ERL_NIF_TERM nfinalize_close(ErlNifEnv* env,
descP->sock = INVALID_SOCKET;
descP->event = INVALID_EVENT;
- descP->state = SOCKET_STATE_CLOSED;
+ descP->state = ESOCK_STATE_CLOSED;
return reply;
}
@@ -7548,7 +7565,7 @@ ERL_NIF_TERM nif_shutdown(ErlNifEnv* env,
int how;
if ((argc != 2) ||
- !enif_get_resource(env, argv[0], sockets, (void**) &descP) ||
+ !ESOCK_GET_RESOURCE(env, argv[0], (void**) &descP) ||
!GET_UINT(env, argv[1], &ehow)) {
return enif_make_badarg(env);
}
@@ -7559,7 +7576,7 @@ ERL_NIF_TERM nif_shutdown(ErlNifEnv* env,
if (!ehow2how(ehow, &how))
return enif_make_badarg(env);
- return nshutdown(env, descP, how);
+ return esock_shutdown(env, descP, how);
#endif // if defined(__WIN32__)
}
@@ -7567,9 +7584,9 @@ ERL_NIF_TERM nif_shutdown(ErlNifEnv* env,
#if !defined(__WIN32__)
static
-ERL_NIF_TERM nshutdown(ErlNifEnv* env,
- ESockDescriptor* descP,
- int how)
+ERL_NIF_TERM esock_shutdown(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int how)
{
ERL_NIF_TERM reply;
@@ -7637,7 +7654,7 @@ ERL_NIF_TERM nif_setopt(ErlNifEnv* env,
/* Extract arguments and perform preliminary validation */
if ((argc != 5) ||
- !enif_get_resource(env, argv[0], sockets, (void**) &descP) ||
+ !ESOCK_GET_RESOURCE(env, argv[0], (void**) &descP) ||
!GET_INT(env, argv[2], &eLevel) ||
!GET_INT(env, argv[3], &eOpt)) {
SGDBG( ("SOCKET", "nif_setopt -> failed initial arg check\r\n") );
@@ -7674,7 +7691,7 @@ ERL_NIF_TERM nif_setopt(ErlNifEnv* env,
MLOCK(descP->cfgMtx);
- result = nsetopt(env, descP, isEncoded, isOTP, level, eOpt, eVal);
+ result = esock_setopt(env, descP, isEncoded, isOTP, level, eOpt, eVal);
MUNLOCK(descP->cfgMtx);
@@ -7691,13 +7708,13 @@ ERL_NIF_TERM nif_setopt(ErlNifEnv* env,
#if !defined(__WIN32__)
static
-ERL_NIF_TERM nsetopt(ErlNifEnv* env,
- ESockDescriptor* descP,
- BOOLEAN_T isEncoded,
- BOOLEAN_T isOTP,
- int level,
- int eOpt,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ BOOLEAN_T isEncoded,
+ BOOLEAN_T isOTP,
+ int level,
+ int eOpt,
+ ERL_NIF_TERM eVal)
{
ERL_NIF_TERM result;
@@ -7705,11 +7722,11 @@ ERL_NIF_TERM nsetopt(ErlNifEnv* env,
/* These are not actual socket options,
* but options for our implementation.
*/
- result = nsetopt_otp(env, descP, eOpt, eVal);
+ result = esock_setopt_otp(env, descP, eOpt, eVal);
} else if (!isEncoded) {
- result = nsetopt_native(env, descP, level, eOpt, eVal);
+ result = esock_setopt_native(env, descP, level, eOpt, eVal);
} else {
- result = nsetopt_level(env, descP, level, eOpt, eVal);
+ result = esock_setopt_level(env, descP, level, eOpt, eVal);
}
return result;
@@ -7717,45 +7734,45 @@ ERL_NIF_TERM nsetopt(ErlNifEnv* env,
-/* nsetopt_otp - Handle OTP (level) options
+/* esock_setopt_otp - Handle OTP (level) options
*/
static
-ERL_NIF_TERM nsetopt_otp(ErlNifEnv* env,
- ESockDescriptor* descP,
- int eOpt,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_otp(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int eOpt,
+ ERL_NIF_TERM eVal)
{
ERL_NIF_TERM result;
SSDBG( descP,
- ("SOCKET", "nsetopt_otp -> entry with"
+ ("SOCKET", "esock_setopt_otp -> entry with"
"\r\n eOpt: %d"
"\r\n eVal: %T"
"\r\n", eOpt, eVal) );
switch (eOpt) {
- case SOCKET_OPT_OTP_DEBUG:
- result = nsetopt_otp_debug(env, descP, eVal);
+ case ESOCK_OPT_OTP_DEBUG:
+ result = esock_setopt_otp_debug(env, descP, eVal);
break;
- case SOCKET_OPT_OTP_IOW:
- result = nsetopt_otp_iow(env, descP, eVal);
+ case ESOCK_OPT_OTP_IOW:
+ result = esock_setopt_otp_iow(env, descP, eVal);
break;
- case SOCKET_OPT_OTP_CTRL_PROC:
- result = nsetopt_otp_ctrl_proc(env, descP, eVal);
+ case ESOCK_OPT_OTP_CTRL_PROC:
+ result = esock_setopt_otp_ctrl_proc(env, descP, eVal);
break;
- case SOCKET_OPT_OTP_RCVBUF:
- result = nsetopt_otp_rcvbuf(env, descP, eVal);
+ case ESOCK_OPT_OTP_RCVBUF:
+ result = esock_setopt_otp_rcvbuf(env, descP, eVal);
break;
- case SOCKET_OPT_OTP_RCVCTRLBUF:
- result = nsetopt_otp_rcvctrlbuf(env, descP, eVal);
+ case ESOCK_OPT_OTP_RCVCTRLBUF:
+ result = esock_setopt_otp_rcvctrlbuf(env, descP, eVal);
break;
- case SOCKET_OPT_OTP_SNDCTRLBUF:
- result = nsetopt_otp_sndctrlbuf(env, descP, eVal);
+ case ESOCK_OPT_OTP_SNDCTRLBUF:
+ result = esock_setopt_otp_sndctrlbuf(env, descP, eVal);
break;
default:
@@ -7767,12 +7784,12 @@ ERL_NIF_TERM nsetopt_otp(ErlNifEnv* env,
}
-/* nsetopt_otp_debug - Handle the OTP (level) debug options
+/* esock_setopt_otp_debug - Handle the OTP (level) debug options
*/
static
-ERL_NIF_TERM nsetopt_otp_debug(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_otp_debug(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
descP->dbg = esock_decode_bool(eVal);
@@ -7780,12 +7797,12 @@ ERL_NIF_TERM nsetopt_otp_debug(ErlNifEnv* env,
}
-/* nsetopt_otp_iow - Handle the OTP (level) iow options
+/* esock_setopt_otp_iow - Handle the OTP (level) iow options
*/
static
-ERL_NIF_TERM nsetopt_otp_iow(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_otp_iow(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
descP->iow = esock_decode_bool(eVal);
@@ -7794,19 +7811,20 @@ ERL_NIF_TERM nsetopt_otp_iow(ErlNifEnv* env,
-/* nsetopt_otp_ctrl_proc - Handle the OTP (level) controlling_process options
+/* esock_setopt_otp_ctrl_proc - Handle the OTP (level)
+ * controlling_process options
*/
static
-ERL_NIF_TERM nsetopt_otp_ctrl_proc(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_otp_ctrl_proc(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
ErlNifPid caller, newCtrlPid;
ESockMonitor newCtrlMon;
int xres;
SSDBG( descP,
- ("SOCKET", "nsetopt_otp_ctrl_proc -> entry with"
+ ("SOCKET", "esock_setopt_otp_ctrl_proc -> entry with"
"\r\n eVal: %T"
"\r\n", eVal) );
@@ -7815,7 +7833,8 @@ ERL_NIF_TERM nsetopt_otp_ctrl_proc(ErlNifEnv* env,
return esock_make_error(env, atom_exself);
if (COMPARE_PIDS(&descP->ctrlPid, &caller) != 0) {
- SSDBG( descP, ("SOCKET", "nsetopt_otp_ctrl_proc -> not owner (%T)\r\n",
+ SSDBG( descP, ("SOCKET",
+ "esock_setopt_otp_ctrl_proc -> not owner (%T)\r\n",
descP->ctrlPid) );
return esock_make_error(env, esock_atom_not_owner);
}
@@ -7825,13 +7844,14 @@ ERL_NIF_TERM nsetopt_otp_ctrl_proc(ErlNifEnv* env,
return esock_make_error(env, esock_atom_einval);
}
- if ((xres = MONP("nsetopt_otp_ctrl_proc -> (new) ctrl",
+ if ((xres = MONP("esock_setopt_otp_ctrl_proc -> (new) ctrl",
env, descP, &newCtrlPid, &newCtrlMon)) != 0) {
- esock_warning_msg("Failed monitor %d) (new) controlling process\r\n", xres);
+ esock_warning_msg("Failed monitor (%d) (new) controlling process\r\n",
+ xres);
return esock_make_error(env, esock_atom_einval);
}
- if ((xres = DEMONP("nsetopt_otp_ctrl_proc -> (old) ctrl",
+ if ((xres = DEMONP("esock_setopt_otp_ctrl_proc -> (old) ctrl",
env, descP, &descP->ctrlMon)) != 0) {
esock_warning_msg("Failed demonitor (%d) "
"old controlling process %T (%T)\r\n",
@@ -7841,14 +7861,14 @@ ERL_NIF_TERM nsetopt_otp_ctrl_proc(ErlNifEnv* env,
descP->ctrlPid = newCtrlPid;
descP->ctrlMon = newCtrlMon;
- SSDBG( descP, ("SOCKET", "nsetopt_otp_ctrl_proc -> done\r\n") );
+ SSDBG( descP, ("SOCKET", "esock_setopt_otp_ctrl_proc -> done\r\n") );
return esock_atom_ok;
}
-/* nsetopt_otp_rcvbuf - Handle the OTP (level) rcvbuf option
+/* esock_setopt_otp_rcvbuf - Handle the OTP (level) rcvbuf option
* The (otp) rcvbuf option is provided as:
*
* BufSz :: integer() | {N :: pos_integer(), BufSz :: pod_integer()}
@@ -7856,9 +7876,9 @@ ERL_NIF_TERM nsetopt_otp_ctrl_proc(ErlNifEnv* env,
* Where N is the max number of reads.
*/
static
-ERL_NIF_TERM nsetopt_otp_rcvbuf(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_otp_rcvbuf(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
const ERL_NIF_TERM* t; // The array of the elements of the tuple
int tsz; // The size of the tuple - should be 2
@@ -7875,7 +7895,7 @@ ERL_NIF_TERM nsetopt_otp_rcvbuf(ErlNifEnv* env,
if ((xres = esock_decode_bufsz(env,
eVal,
- SOCKET_RECV_BUFFER_SIZE_DEFAULT,
+ ESOCK_RECV_BUFFER_SIZE_DEFAULT,
&bufSz)) != NULL)
return esock_make_error_str(env, xres);
@@ -7892,7 +7912,7 @@ ERL_NIF_TERM nsetopt_otp_rcvbuf(ErlNifEnv* env,
if ((xres = esock_decode_bufsz(env,
t[1],
- SOCKET_RECV_BUFFER_SIZE_DEFAULT,
+ ESOCK_RECV_BUFFER_SIZE_DEFAULT,
&bufSz)) != NULL)
return esock_make_error_str(env, xres);
@@ -7908,19 +7928,19 @@ ERL_NIF_TERM nsetopt_otp_rcvbuf(ErlNifEnv* env,
-/* nsetopt_otp_rcvctrlbuf - Handle the OTP (level) rcvctrlbuf option
+/* esock_setopt_otp_rcvctrlbuf - Handle the OTP (level) rcvctrlbuf option
*/
static
-ERL_NIF_TERM nsetopt_otp_rcvctrlbuf(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_otp_rcvctrlbuf(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
size_t val;
char* xres;
if ((xres = esock_decode_bufsz(env,
eVal,
- SOCKET_RECV_CTRL_BUFFER_SIZE_DEFAULT,
+ ESOCK_RECV_CTRL_BUFFER_SIZE_DEFAULT,
&val)) != NULL)
return esock_make_error_str(env, xres);
@@ -7931,19 +7951,19 @@ ERL_NIF_TERM nsetopt_otp_rcvctrlbuf(ErlNifEnv* env,
-/* nsetopt_otp_sndctrlbuf - Handle the OTP (level) sndctrlbuf option
+/* esock_setopt_otp_sndctrlbuf - Handle the OTP (level) sndctrlbuf option
*/
static
-ERL_NIF_TERM nsetopt_otp_sndctrlbuf(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_otp_sndctrlbuf(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
size_t val;
char* xres;
if ((xres = esock_decode_bufsz(env,
eVal,
- SOCKET_SEND_CTRL_BUFFER_SIZE_DEFAULT,
+ ESOCK_SEND_CTRL_BUFFER_SIZE_DEFAULT,
&val)) != NULL)
return esock_make_error_str(env, xres);
@@ -7958,17 +7978,17 @@ ERL_NIF_TERM nsetopt_otp_sndctrlbuf(ErlNifEnv* env,
* in "native mode" (option is provided as is and value as a binary).
*/
static
-ERL_NIF_TERM nsetopt_native(ErlNifEnv* env,
- ESockDescriptor* descP,
- int level,
- int opt,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_native(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int level,
+ int opt,
+ ERL_NIF_TERM eVal)
{
ErlNifBinary val;
ERL_NIF_TERM result;
SSDBG( descP,
- ("SOCKET", "nsetopt_native -> entry with"
+ ("SOCKET", "esock_setopt_native -> entry with"
"\r\n level: %d"
"\r\n opt: %d"
"\r\n eVal: %T"
@@ -7986,7 +8006,7 @@ ERL_NIF_TERM nsetopt_native(ErlNifEnv* env,
}
SSDBG( descP,
- ("SOCKET", "nsetopt_native -> done when"
+ ("SOCKET", "esock_setopt_native -> done when"
"\r\n result: %T"
"\r\n", result) );
@@ -7995,25 +8015,25 @@ ERL_NIF_TERM nsetopt_native(ErlNifEnv* env,
-/* nsetopt_level - A "proper" level (option) has been specified
+/* esock_setopt_level - A "proper" level (option) has been specified
*/
static
-ERL_NIF_TERM nsetopt_level(ErlNifEnv* env,
- ESockDescriptor* descP,
- int level,
- int eOpt,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_level(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int level,
+ int eOpt,
+ ERL_NIF_TERM eVal)
{
ERL_NIF_TERM result;
SSDBG( descP,
- ("SOCKET", "nsetopt_level -> entry with"
+ ("SOCKET", "esock_setopt_level -> entry with"
"\r\n level: %d"
"\r\n", level) );
switch (level) {
case SOL_SOCKET:
- result = nsetopt_lvl_socket(env, descP, eOpt, eVal);
+ result = esock_setopt_lvl_socket(env, descP, eOpt, eVal);
break;
#if defined(SOL_IP)
@@ -8021,7 +8041,7 @@ ERL_NIF_TERM nsetopt_level(ErlNifEnv* env,
#else
case IPPROTO_IP:
#endif
- result = nsetopt_lvl_ip(env, descP, eOpt, eVal);
+ result = esock_setopt_lvl_ip(env, descP, eOpt, eVal);
break;
#if defined(HAVE_IPV6)
@@ -8030,33 +8050,34 @@ ERL_NIF_TERM nsetopt_level(ErlNifEnv* env,
#else
case IPPROTO_IPV6:
#endif
- result = nsetopt_lvl_ipv6(env, descP, eOpt, eVal);
+ result = esock_setopt_lvl_ipv6(env, descP, eOpt, eVal);
break;
#endif
case IPPROTO_TCP:
- result = nsetopt_lvl_tcp(env, descP, eOpt, eVal);
+ result = esock_setopt_lvl_tcp(env, descP, eOpt, eVal);
break;
case IPPROTO_UDP:
- result = nsetopt_lvl_udp(env, descP, eOpt, eVal);
+ result = esock_setopt_lvl_udp(env, descP, eOpt, eVal);
break;
#if defined(HAVE_SCTP)
case IPPROTO_SCTP:
- result = nsetopt_lvl_sctp(env, descP, eOpt, eVal);
+ result = esock_setopt_lvl_sctp(env, descP, eOpt, eVal);
break;
#endif
default:
SSDBG( descP,
- ("SOCKET", "nsetopt_level -> unknown level (%d)\r\n", level) );
+ ("SOCKET",
+ "esock_setopt_level -> unknown level (%d)\r\n", level) );
result = esock_make_error(env, esock_atom_einval);
break;
}
SSDBG( descP,
- ("SOCKET", "nsetopt_level -> done when"
+ ("SOCKET", "esock_setopt_level -> done when"
"\r\n result: %T"
"\r\n", result) );
@@ -8065,139 +8086,140 @@ ERL_NIF_TERM nsetopt_level(ErlNifEnv* env,
-/* nsetopt_lvl_socket - Level *SOCKET* option
+/* esock_setopt_lvl_socket - Level *SOCKET* option
*/
static
-ERL_NIF_TERM nsetopt_lvl_socket(ErlNifEnv* env,
- ESockDescriptor* descP,
- int eOpt,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_socket(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int eOpt,
+ ERL_NIF_TERM eVal)
{
ERL_NIF_TERM result;
SSDBG( descP,
- ("SOCKET", "nsetopt_lvl_socket -> entry with"
+ ("SOCKET", "esock_setopt_lvl_socket -> entry with"
"\r\n opt: %d"
"\r\n", eOpt) );
switch (eOpt) {
#if defined(SO_BINDTODEVICE)
- case SOCKET_OPT_SOCK_BINDTODEVICE:
- result = nsetopt_lvl_sock_bindtodevice(env, descP, eVal);
+ case ESOCK_OPT_SOCK_BINDTODEVICE:
+ result = esock_setopt_lvl_sock_bindtodevice(env, descP, eVal);
break;
#endif
#if defined(SO_BROADCAST)
- case SOCKET_OPT_SOCK_BROADCAST:
- result = nsetopt_lvl_sock_broadcast(env, descP, eVal);
+ case ESOCK_OPT_SOCK_BROADCAST:
+ result = esock_setopt_lvl_sock_broadcast(env, descP, eVal);
break;
#endif
#if defined(SO_DEBUG)
- case SOCKET_OPT_SOCK_DEBUG:
- result = nsetopt_lvl_sock_debug(env, descP, eVal);
+ case ESOCK_OPT_SOCK_DEBUG:
+ result = esock_setopt_lvl_sock_debug(env, descP, eVal);
break;
#endif
#if defined(SO_DONTROUTE)
- case SOCKET_OPT_SOCK_DONTROUTE:
- result = nsetopt_lvl_sock_dontroute(env, descP, eVal);
+ case ESOCK_OPT_SOCK_DONTROUTE:
+ result = esock_setopt_lvl_sock_dontroute(env, descP, eVal);
break;
#endif
#if defined(SO_KEEPALIVE)
- case SOCKET_OPT_SOCK_KEEPALIVE:
- result = nsetopt_lvl_sock_keepalive(env, descP, eVal);
+ case ESOCK_OPT_SOCK_KEEPALIVE:
+ result = esock_setopt_lvl_sock_keepalive(env, descP, eVal);
break;
#endif
#if defined(SO_LINGER)
- case SOCKET_OPT_SOCK_LINGER:
- result = nsetopt_lvl_sock_linger(env, descP, eVal);
+ case ESOCK_OPT_SOCK_LINGER:
+ result = esock_setopt_lvl_sock_linger(env, descP, eVal);
break;
#endif
#if defined(SO_PEEK_OFF)
- case SOCKET_OPT_SOCK_PEEK_OFF:
- result = nsetopt_lvl_sock_peek_off(env, descP, eVal);
+ case ESOCK_OPT_SOCK_PEEK_OFF:
+ result = esock_setopt_lvl_sock_peek_off(env, descP, eVal);
break;
#endif
#if defined(SO_OOBINLINE)
- case SOCKET_OPT_SOCK_OOBINLINE:
- result = nsetopt_lvl_sock_oobinline(env, descP, eVal);
+ case ESOCK_OPT_SOCK_OOBINLINE:
+ result = esock_setopt_lvl_sock_oobinline(env, descP, eVal);
break;
#endif
#if defined(SO_PRIORITY)
- case SOCKET_OPT_SOCK_PRIORITY:
- result = nsetopt_lvl_sock_priority(env, descP, eVal);
+ case ESOCK_OPT_SOCK_PRIORITY:
+ result = esock_setopt_lvl_sock_priority(env, descP, eVal);
break;
#endif
#if defined(SO_RCVBUF)
- case SOCKET_OPT_SOCK_RCVBUF:
- result = nsetopt_lvl_sock_rcvbuf(env, descP, eVal);
+ case ESOCK_OPT_SOCK_RCVBUF:
+ result = esock_setopt_lvl_sock_rcvbuf(env, descP, eVal);
break;
#endif
#if defined(SO_RCVLOWAT)
- case SOCKET_OPT_SOCK_RCVLOWAT:
- result = nsetopt_lvl_sock_rcvlowat(env, descP, eVal);
+ case ESOCK_OPT_SOCK_RCVLOWAT:
+ result = esock_setopt_lvl_sock_rcvlowat(env, descP, eVal);
break;
#endif
#if defined(SO_RCVTIMEO)
- case SOCKET_OPT_SOCK_RCVTIMEO:
- result = nsetopt_lvl_sock_rcvtimeo(env, descP, eVal);
+ case ESOCK_OPT_SOCK_RCVTIMEO:
+ result = esock_setopt_lvl_sock_rcvtimeo(env, descP, eVal);
break;
#endif
#if defined(SO_REUSEADDR)
- case SOCKET_OPT_SOCK_REUSEADDR:
- result = nsetopt_lvl_sock_reuseaddr(env, descP, eVal);
+ case ESOCK_OPT_SOCK_REUSEADDR:
+ result = esock_setopt_lvl_sock_reuseaddr(env, descP, eVal);
break;
#endif
#if defined(SO_REUSEPORT)
- case SOCKET_OPT_SOCK_REUSEPORT:
- result = nsetopt_lvl_sock_reuseport(env, descP, eVal);
+ case ESOCK_OPT_SOCK_REUSEPORT:
+ result = esock_setopt_lvl_sock_reuseport(env, descP, eVal);
break;
#endif
#if defined(SO_SNDBUF)
- case SOCKET_OPT_SOCK_SNDBUF:
- result = nsetopt_lvl_sock_sndbuf(env, descP, eVal);
+ case ESOCK_OPT_SOCK_SNDBUF:
+ result = esock_setopt_lvl_sock_sndbuf(env, descP, eVal);
break;
#endif
#if defined(SO_SNDLOWAT)
- case SOCKET_OPT_SOCK_SNDLOWAT:
- result = nsetopt_lvl_sock_sndlowat(env, descP, eVal);
+ case ESOCK_OPT_SOCK_SNDLOWAT:
+ result = esock_setopt_lvl_sock_sndlowat(env, descP, eVal);
break;
#endif
#if defined(SO_SNDTIMEO)
- case SOCKET_OPT_SOCK_SNDTIMEO:
- result = nsetopt_lvl_sock_sndtimeo(env, descP, eVal);
+ case ESOCK_OPT_SOCK_SNDTIMEO:
+ result = esock_setopt_lvl_sock_sndtimeo(env, descP, eVal);
break;
#endif
#if defined(SO_TIMESTAMP)
- case SOCKET_OPT_SOCK_TIMESTAMP:
- result = nsetopt_lvl_sock_timestamp(env, descP, eVal);
+ case ESOCK_OPT_SOCK_TIMESTAMP:
+ result = esock_setopt_lvl_sock_timestamp(env, descP, eVal);
break;
#endif
default:
SSDBG( descP,
- ("SOCKET", "nsetopt_lvl_socket -> unknown opt (%d)\r\n", eOpt) );
+ ("SOCKET",
+ "esock_setopt_lvl_socket -> unknown opt (%d)\r\n", eOpt) );
result = esock_make_error(env, esock_atom_einval);
break;
}
SSDBG( descP,
- ("SOCKET", "nsetopt_lvl_socket -> done when"
+ ("SOCKET", "esock_setopt_lvl_socket -> done when"
"\r\n result: %T"
"\r\n", result) );
@@ -8207,66 +8229,66 @@ ERL_NIF_TERM nsetopt_lvl_socket(ErlNifEnv* env,
#if defined(SO_BINDTODEVICE)
static
-ERL_NIF_TERM nsetopt_lvl_sock_bindtodevice(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_sock_bindtodevice(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
- return nsetopt_str_opt(env, descP,
- SOL_SOCKET, SO_BROADCAST,
- IFNAMSIZ, eVal);
+ return esock_setopt_str_opt(env, descP,
+ SOL_SOCKET, SO_BINDTODEVICE,
+ IFNAMSIZ, eVal);
}
#endif
#if defined(SO_BROADCAST)
static
-ERL_NIF_TERM nsetopt_lvl_sock_broadcast(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_sock_broadcast(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
- return nsetopt_bool_opt(env, descP, SOL_SOCKET, SO_BROADCAST, eVal);
+ return esock_setopt_bool_opt(env, descP, SOL_SOCKET, SO_BROADCAST, eVal);
}
#endif
#if defined(SO_DEBUG)
static
-ERL_NIF_TERM nsetopt_lvl_sock_debug(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_sock_debug(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
- return nsetopt_int_opt(env, descP, SOL_SOCKET, SO_DEBUG, eVal);
+ return esock_setopt_int_opt(env, descP, SOL_SOCKET, SO_DEBUG, eVal);
}
#endif
#if defined(SO_DONTROUTE)
static
-ERL_NIF_TERM nsetopt_lvl_sock_dontroute(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_sock_dontroute(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
- return nsetopt_bool_opt(env, descP, SOL_SOCKET, SO_DONTROUTE, eVal);
+ return esock_setopt_bool_opt(env, descP, SOL_SOCKET, SO_DONTROUTE, eVal);
}
#endif
#if defined(SO_KEEPALIVE)
static
-ERL_NIF_TERM nsetopt_lvl_sock_keepalive(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_sock_keepalive(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
- return nsetopt_bool_opt(env, descP, SOL_SOCKET, SO_KEEPALIVE, eVal);
+ return esock_setopt_bool_opt(env, descP, SOL_SOCKET, SO_KEEPALIVE, eVal);
}
#endif
#if defined(SO_LINGER)
static
-ERL_NIF_TERM nsetopt_lvl_sock_linger(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_sock_linger(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
ERL_NIF_TERM result;
struct linger val;
@@ -8290,346 +8312,347 @@ ERL_NIF_TERM nsetopt_lvl_sock_linger(ErlNifEnv* env,
#if defined(SO_OOBINLINE)
static
-ERL_NIF_TERM nsetopt_lvl_sock_oobinline(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_sock_oobinline(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
- return nsetopt_bool_opt(env, descP, SOL_SOCKET, SO_OOBINLINE, eVal);
+ return esock_setopt_bool_opt(env, descP, SOL_SOCKET, SO_OOBINLINE, eVal);
}
#endif
#if defined(SO_PEEK_OFF)
static
-ERL_NIF_TERM nsetopt_lvl_sock_peek_off(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_sock_peek_off(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
- return nsetopt_int_opt(env, descP, SOL_SOCKET, SO_PEEK_OFF, eVal);
+ return esock_setopt_int_opt(env, descP, SOL_SOCKET, SO_PEEK_OFF, eVal);
}
#endif
#if defined(SO_PRIORITY)
static
-ERL_NIF_TERM nsetopt_lvl_sock_priority(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_sock_priority(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
- return nsetopt_int_opt(env, descP, SOL_SOCKET, SO_PRIORITY, eVal);
+ return esock_setopt_int_opt(env, descP, SOL_SOCKET, SO_PRIORITY, eVal);
}
#endif
#if defined(SO_RCVBUF)
static
-ERL_NIF_TERM nsetopt_lvl_sock_rcvbuf(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_sock_rcvbuf(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
- return nsetopt_int_opt(env, descP, SOL_SOCKET, SO_RCVBUF, eVal);
+ return esock_setopt_int_opt(env, descP, SOL_SOCKET, SO_RCVBUF, eVal);
}
#endif
#if defined(SO_RCVLOWAT)
static
-ERL_NIF_TERM nsetopt_lvl_sock_rcvlowat(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_sock_rcvlowat(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
- return nsetopt_int_opt(env, descP, SOL_SOCKET, SO_RCVLOWAT, eVal);
+ return esock_setopt_int_opt(env, descP, SOL_SOCKET, SO_RCVLOWAT, eVal);
}
#endif
#if defined(SO_RCVTIMEO)
static
-ERL_NIF_TERM nsetopt_lvl_sock_rcvtimeo(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_sock_rcvtimeo(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
- return nsetopt_timeval_opt(env, descP, SOL_SOCKET, SO_RCVTIMEO, eVal);
+ return esock_setopt_timeval_opt(env, descP, SOL_SOCKET, SO_RCVTIMEO, eVal);
}
#endif
#if defined(SO_REUSEADDR)
static
-ERL_NIF_TERM nsetopt_lvl_sock_reuseaddr(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_sock_reuseaddr(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
- return nsetopt_bool_opt(env, descP, SOL_SOCKET, SO_REUSEADDR, eVal);
+ return esock_setopt_bool_opt(env, descP, SOL_SOCKET, SO_REUSEADDR, eVal);
}
#endif
#if defined(SO_REUSEPORT)
static
-ERL_NIF_TERM nsetopt_lvl_sock_reuseport(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_sock_reuseport(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
- return nsetopt_bool_opt(env, descP, SOL_SOCKET, SO_REUSEPORT, eVal);
+ return esock_setopt_bool_opt(env, descP, SOL_SOCKET, SO_REUSEPORT, eVal);
}
#endif
#if defined(SO_SNDBUF)
static
-ERL_NIF_TERM nsetopt_lvl_sock_sndbuf(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_sock_sndbuf(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
- return nsetopt_int_opt(env, descP, SOL_SOCKET, SO_SNDBUF, eVal);
+ return esock_setopt_int_opt(env, descP, SOL_SOCKET, SO_SNDBUF, eVal);
}
#endif
#if defined(SO_SNDLOWAT)
static
-ERL_NIF_TERM nsetopt_lvl_sock_sndlowat(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_sock_sndlowat(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
- return nsetopt_int_opt(env, descP, SOL_SOCKET, SO_SNDLOWAT, eVal);
+ return esock_setopt_int_opt(env, descP, SOL_SOCKET, SO_SNDLOWAT, eVal);
}
#endif
#if defined(SO_SNDTIMEO)
static
-ERL_NIF_TERM nsetopt_lvl_sock_sndtimeo(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_sock_sndtimeo(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
SSDBG( descP,
- ("SOCKET", "nsetopt_lvl_sock_sndtimeo -> entry with"
+ ("SOCKET", "esock_setopt_lvl_sock_sndtimeo -> entry with"
"\r\n eVal: %T"
"\r\n", eVal) );
- return nsetopt_timeval_opt(env, descP, SOL_SOCKET, SO_SNDTIMEO, eVal);
+ return esock_setopt_timeval_opt(env, descP, SOL_SOCKET, SO_SNDTIMEO, eVal);
}
#endif
#if defined(SO_TIMESTAMP)
static
-ERL_NIF_TERM nsetopt_lvl_sock_timestamp(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_sock_timestamp(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
- return nsetopt_bool_opt(env, descP, SOL_SOCKET, SO_TIMESTAMP, eVal);
+ return esock_setopt_bool_opt(env, descP, SOL_SOCKET, SO_TIMESTAMP, eVal);
}
#endif
-/* nsetopt_lvl_ip - Level *IP* option(s)
+/* esock_setopt_lvl_ip - Level *IP* option(s)
*/
static
-ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env,
- ESockDescriptor* descP,
- int eOpt,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_ip(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int eOpt,
+ ERL_NIF_TERM eVal)
{
ERL_NIF_TERM result;
SSDBG( descP,
- ("SOCKET", "nsetopt_lvl_ip -> entry with"
+ ("SOCKET", "esock_setopt_lvl_ip -> entry with"
"\r\n opt: %d"
"\r\n", eOpt) );
switch (eOpt) {
#if defined(IP_ADD_MEMBERSHIP)
- case SOCKET_OPT_IP_ADD_MEMBERSHIP:
- result = nsetopt_lvl_ip_add_membership(env, descP, eVal);
+ case ESOCK_OPT_IP_ADD_MEMBERSHIP:
+ result = esock_setopt_lvl_ip_add_membership(env, descP, eVal);
break;
#endif
#if defined(IP_ADD_SOURCE_MEMBERSHIP)
- case SOCKET_OPT_IP_ADD_SOURCE_MEMBERSHIP:
- result = nsetopt_lvl_ip_add_source_membership(env, descP, eVal);
+ case ESOCK_OPT_IP_ADD_SOURCE_MEMBERSHIP:
+ result = esock_setopt_lvl_ip_add_source_membership(env, descP, eVal);
break;
#endif
#if defined(IP_BLOCK_SOURCE)
- case SOCKET_OPT_IP_BLOCK_SOURCE:
- result = nsetopt_lvl_ip_block_source(env, descP, eVal);
+ case ESOCK_OPT_IP_BLOCK_SOURCE:
+ result = esock_setopt_lvl_ip_block_source(env, descP, eVal);
break;
#endif
#if defined(IP_DROP_MEMBERSHIP)
- case SOCKET_OPT_IP_DROP_MEMBERSHIP:
- result = nsetopt_lvl_ip_drop_membership(env, descP, eVal);
+ case ESOCK_OPT_IP_DROP_MEMBERSHIP:
+ result = esock_setopt_lvl_ip_drop_membership(env, descP, eVal);
break;
#endif
#if defined(IP_DROP_SOURCE_MEMBERSHIP)
- case SOCKET_OPT_IP_DROP_SOURCE_MEMBERSHIP:
- result = nsetopt_lvl_ip_drop_source_membership(env, descP, eVal);
+ case ESOCK_OPT_IP_DROP_SOURCE_MEMBERSHIP:
+ result = esock_setopt_lvl_ip_drop_source_membership(env, descP, eVal);
break;
#endif
#if defined(IP_FREEBIND)
- case SOCKET_OPT_IP_FREEBIND:
- result = nsetopt_lvl_ip_freebind(env, descP, eVal);
+ case ESOCK_OPT_IP_FREEBIND:
+ result = esock_setopt_lvl_ip_freebind(env, descP, eVal);
break;
#endif
#if defined(IP_HDRINCL)
- case SOCKET_OPT_IP_HDRINCL:
- result = nsetopt_lvl_ip_hdrincl(env, descP, eVal);
+ case ESOCK_OPT_IP_HDRINCL:
+ result = esock_setopt_lvl_ip_hdrincl(env, descP, eVal);
break;
#endif
#if defined(IP_MINTTL)
- case SOCKET_OPT_IP_MINTTL:
- result = nsetopt_lvl_ip_minttl(env, descP, eVal);
+ case ESOCK_OPT_IP_MINTTL:
+ result = esock_setopt_lvl_ip_minttl(env, descP, eVal);
break;
#endif
#if defined(IP_MSFILTER) && defined(IP_MSFILTER_SIZE)
- case SOCKET_OPT_IP_MSFILTER:
- result = nsetopt_lvl_ip_msfilter(env, descP, eVal);
+ case ESOCK_OPT_IP_MSFILTER:
+ result = esock_setopt_lvl_ip_msfilter(env, descP, eVal);
break;
#endif
#if defined(IP_MTU_DISCOVER)
- case SOCKET_OPT_IP_MTU_DISCOVER:
- result = nsetopt_lvl_ip_mtu_discover(env, descP, eVal);
+ case ESOCK_OPT_IP_MTU_DISCOVER:
+ result = esock_setopt_lvl_ip_mtu_discover(env, descP, eVal);
break;
#endif
#if defined(IP_MULTICAST_ALL)
- case SOCKET_OPT_IP_MULTICAST_ALL:
- result = nsetopt_lvl_ip_multicast_all(env, descP, eVal);
+ case ESOCK_OPT_IP_MULTICAST_ALL:
+ result = esock_setopt_lvl_ip_multicast_all(env, descP, eVal);
break;
#endif
#if defined(IP_MULTICAST_IF)
- case SOCKET_OPT_IP_MULTICAST_IF:
- result = nsetopt_lvl_ip_multicast_if(env, descP, eVal);
+ case ESOCK_OPT_IP_MULTICAST_IF:
+ result = esock_setopt_lvl_ip_multicast_if(env, descP, eVal);
break;
#endif
#if defined(IP_MULTICAST_LOOP)
- case SOCKET_OPT_IP_MULTICAST_LOOP:
- result = nsetopt_lvl_ip_multicast_loop(env, descP, eVal);
+ case ESOCK_OPT_IP_MULTICAST_LOOP:
+ result = esock_setopt_lvl_ip_multicast_loop(env, descP, eVal);
break;
#endif
#if defined(IP_MULTICAST_TTL)
- case SOCKET_OPT_IP_MULTICAST_TTL:
- result = nsetopt_lvl_ip_multicast_ttl(env, descP, eVal);
+ case ESOCK_OPT_IP_MULTICAST_TTL:
+ result = esock_setopt_lvl_ip_multicast_ttl(env, descP, eVal);
break;
#endif
#if defined(IP_NODEFRAG)
- case SOCKET_OPT_IP_NODEFRAG:
- result = nsetopt_lvl_ip_nodefrag(env, descP, eVal);
+ case ESOCK_OPT_IP_NODEFRAG:
+ result = esock_setopt_lvl_ip_nodefrag(env, descP, eVal);
break;
#endif
#if defined(IP_PKTINFO)
- case SOCKET_OPT_IP_PKTINFO:
- result = nsetopt_lvl_ip_pktinfo(env, descP, eVal);
+ case ESOCK_OPT_IP_PKTINFO:
+ result = esock_setopt_lvl_ip_pktinfo(env, descP, eVal);
break;
#endif
#if defined(IP_RECVDSTADDR)
- case SOCKET_OPT_IP_RECVDSTADDR:
- result = nsetopt_lvl_ip_recvdstaddr(env, descP, eVal);
+ case ESOCK_OPT_IP_RECVDSTADDR:
+ result = esock_setopt_lvl_ip_recvdstaddr(env, descP, eVal);
break;
#endif
#if defined(IP_RECVERR)
- case SOCKET_OPT_IP_RECVERR:
- result = nsetopt_lvl_ip_recverr(env, descP, eVal);
+ case ESOCK_OPT_IP_RECVERR:
+ result = esock_setopt_lvl_ip_recverr(env, descP, eVal);
break;
#endif
#if defined(IP_RECVIF)
- case SOCKET_OPT_IP_RECVIF:
- result = nsetopt_lvl_ip_recvif(env, descP, eVal);
+ case ESOCK_OPT_IP_RECVIF:
+ result = esock_setopt_lvl_ip_recvif(env, descP, eVal);
break;
#endif
#if defined(IP_RECVOPTS)
- case SOCKET_OPT_IP_RECVOPTS:
- result = nsetopt_lvl_ip_recvopts(env, descP, eVal);
+ case ESOCK_OPT_IP_RECVOPTS:
+ result = esock_setopt_lvl_ip_recvopts(env, descP, eVal);
break;
#endif
#if defined(IP_RECVORIGDSTADDR)
- case SOCKET_OPT_IP_RECVORIGDSTADDR:
- result = nsetopt_lvl_ip_recvorigdstaddr(env, descP, eVal);
+ case ESOCK_OPT_IP_RECVORIGDSTADDR:
+ result = esock_setopt_lvl_ip_recvorigdstaddr(env, descP, eVal);
break;
#endif
#if defined(IP_RECVTOS)
- case SOCKET_OPT_IP_RECVTOS:
- result = nsetopt_lvl_ip_recvtos(env, descP, eVal);
+ case ESOCK_OPT_IP_RECVTOS:
+ result = esock_setopt_lvl_ip_recvtos(env, descP, eVal);
break;
#endif
#if defined(IP_RECVTTL)
- case SOCKET_OPT_IP_RECVTTL:
- result = nsetopt_lvl_ip_recvttl(env, descP, eVal);
+ case ESOCK_OPT_IP_RECVTTL:
+ result = esock_setopt_lvl_ip_recvttl(env, descP, eVal);
break;
#endif
#if defined(IP_RETOPTS)
- case SOCKET_OPT_IP_RETOPTS:
- result = nsetopt_lvl_ip_retopts(env, descP, eVal);
+ case ESOCK_OPT_IP_RETOPTS:
+ result = esock_setopt_lvl_ip_retopts(env, descP, eVal);
break;
#endif
#if defined(IP_ROUTER_ALERT)
- case SOCKET_OPT_IP_ROUTER_ALERT:
- result = nsetopt_lvl_ip_router_alert(env, descP, eVal);
+ case ESOCK_OPT_IP_ROUTER_ALERT:
+ result = esock_setopt_lvl_ip_router_alert(env, descP, eVal);
break;
#endif
#if defined(IP_SENDSRCADDR)
- case SOCKET_OPT_IP_SENDSRCADDR:
- result = nsetopt_lvl_ip_sendsrcaddr(env, descP, eVal);
+ case ESOCK_OPT_IP_SENDSRCADDR:
+ result = esock_setopt_lvl_ip_sendsrcaddr(env, descP, eVal);
break;
#endif
#if defined(IP_TOS)
- case SOCKET_OPT_IP_TOS:
- result = nsetopt_lvl_ip_tos(env, descP, eVal);
+ case ESOCK_OPT_IP_TOS:
+ result = esock_setopt_lvl_ip_tos(env, descP, eVal);
break;
#endif
#if defined(IP_TRANSPARENT)
- case SOCKET_OPT_IP_TRANSPARENT:
- result = nsetopt_lvl_ip_transparent(env, descP, eVal);
+ case ESOCK_OPT_IP_TRANSPARENT:
+ result = esock_setopt_lvl_ip_transparent(env, descP, eVal);
break;
#endif
#if defined(IP_TTL)
- case SOCKET_OPT_IP_TTL:
- result = nsetopt_lvl_ip_ttl(env, descP, eVal);
+ case ESOCK_OPT_IP_TTL:
+ result = esock_setopt_lvl_ip_ttl(env, descP, eVal);
break;
#endif
#if defined(IP_UNBLOCK_SOURCE)
- case SOCKET_OPT_IP_UNBLOCK_SOURCE:
- result = nsetopt_lvl_ip_unblock_source(env, descP, eVal);
+ case ESOCK_OPT_IP_UNBLOCK_SOURCE:
+ result = esock_setopt_lvl_ip_unblock_source(env, descP, eVal);
break;
#endif
default:
- SSDBG( descP, ("SOCKET", "nsetopt_lvl_ip -> unknown opt (%d)\r\n", eOpt) );
+ SSDBG( descP, ("SOCKET",
+ "esock_setopt_lvl_ip -> unknown opt (%d)\r\n", eOpt) );
result = esock_make_error(env, esock_atom_einval);
break;
}
SSDBG( descP,
- ("SOCKET", "nsetopt_lvl_ip -> done when"
+ ("SOCKET", "esock_setopt_lvl_ip -> done when"
"\r\n result: %T"
"\r\n", result) );
@@ -8637,7 +8660,7 @@ ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env,
}
-/* nsetopt_lvl_ip_add_membership - Level IP ADD_MEMBERSHIP option
+/* esock_setopt_lvl_ip_add_membership - Level IP ADD_MEMBERSHIP option
*
* The value is a map with two attributes: multiaddr and interface.
* The attribute 'multiaddr' is always a 4-tuple (IPv4 address).
@@ -8646,16 +8669,18 @@ ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env,
*/
#if defined(IP_ADD_MEMBERSHIP)
static
-ERL_NIF_TERM nsetopt_lvl_ip_add_membership(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_ip_add_membership(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
- return nsetopt_lvl_ip_update_membership(env, descP, eVal, IP_ADD_MEMBERSHIP);
+ return esock_setopt_lvl_ip_update_membership(env, descP, eVal,
+ IP_ADD_MEMBERSHIP);
}
#endif
-/* nsetopt_lvl_ip_add_source_membership - Level IP ADD_SOURCE_MEMBERSHIP option
+/* esock_setopt_lvl_ip_add_source_membership -
+ * Level IP ADD_SOURCE_MEMBERSHIP option
*
* The value is a map with three attributes: multiaddr, interface and
* sourceaddr.
@@ -8666,17 +8691,17 @@ ERL_NIF_TERM nsetopt_lvl_ip_add_membership(ErlNifEnv* env,
*/
#if defined(IP_ADD_SOURCE_MEMBERSHIP)
static
-ERL_NIF_TERM nsetopt_lvl_ip_add_source_membership(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_ip_add_source_membership(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
- return nsetopt_lvl_ip_update_source(env, descP, eVal,
- IP_ADD_SOURCE_MEMBERSHIP);
+ return esock_setopt_lvl_ip_update_source(env, descP, eVal,
+ IP_ADD_SOURCE_MEMBERSHIP);
}
#endif
-/* nsetopt_lvl_ip_block_source - Level IP BLOCK_SOURCE option
+/* esock_setopt_lvl_ip_block_source - Level IP BLOCK_SOURCE option
*
* The value is a map with three attributes: multiaddr, interface and
* sourceaddr.
@@ -8687,16 +8712,16 @@ ERL_NIF_TERM nsetopt_lvl_ip_add_source_membership(ErlNifEnv* env,
*/
#if defined(IP_BLOCK_SOURCE)
static
-ERL_NIF_TERM nsetopt_lvl_ip_block_source(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_ip_block_source(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
- return nsetopt_lvl_ip_update_source(env, descP, eVal, IP_BLOCK_SOURCE);
+ return esock_setopt_lvl_ip_update_source(env, descP, eVal, IP_BLOCK_SOURCE);
}
#endif
-/* nsetopt_lvl_ip_drop_membership - Level IP DROP_MEMBERSHIP option
+/* esock_setopt_lvl_ip_drop_membership - Level IP DROP_MEMBERSHIP option
*
* The value is a map with two attributes: multiaddr and interface.
* The attribute 'multiaddr' is always a 4-tuple (IPv4 address).
@@ -8709,18 +8734,19 @@ ERL_NIF_TERM nsetopt_lvl_ip_block_source(ErlNifEnv* env,
*/
#if defined(IP_DROP_MEMBERSHIP)
static
-ERL_NIF_TERM nsetopt_lvl_ip_drop_membership(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_ip_drop_membership(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
- return nsetopt_lvl_ip_update_membership(env, descP, eVal,
- IP_DROP_MEMBERSHIP);
+ return esock_setopt_lvl_ip_update_membership(env, descP, eVal,
+ IP_DROP_MEMBERSHIP);
}
#endif
-/* nsetopt_lvl_ip_drop_source_membership - Level IP DROP_SOURCE_MEMBERSHIP option
+/* esock_setopt_lvl_ip_drop_source_membership -
+ * Level IP DROP_SOURCE_MEMBERSHIP option
*
* The value is a map with three attributes: multiaddr, interface and
* sourceaddr.
@@ -8731,24 +8757,24 @@ ERL_NIF_TERM nsetopt_lvl_ip_drop_membership(ErlNifEnv* env,
*/
#if defined(IP_DROP_SOURCE_MEMBERSHIP)
static
-ERL_NIF_TERM nsetopt_lvl_ip_drop_source_membership(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_ip_drop_source_membership(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
- return nsetopt_lvl_ip_update_source(env, descP, eVal,
- IP_DROP_SOURCE_MEMBERSHIP);
+ return esock_setopt_lvl_ip_update_source(env, descP, eVal,
+ IP_DROP_SOURCE_MEMBERSHIP);
}
#endif
-/* nsetopt_lvl_ip_freebind - Level IP FREEBIND option
+/* esock_setopt_lvl_ip_freebind - Level IP FREEBIND option
*/
#if defined(IP_FREEBIND)
static
-ERL_NIF_TERM nsetopt_lvl_ip_freebind(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_ip_freebind(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
#if defined(SOL_IP)
int level = SOL_IP;
@@ -8756,19 +8782,19 @@ ERL_NIF_TERM nsetopt_lvl_ip_freebind(ErlNifEnv* env,
int level = IPPROTO_IP;
#endif
- return nsetopt_bool_opt(env, descP, level, IP_FREEBIND, eVal);
+ return esock_setopt_bool_opt(env, descP, level, IP_FREEBIND, eVal);
}
#endif
-/* nsetopt_lvl_ip_hdrincl - Level IP HDRINCL option
+/* esock_setopt_lvl_ip_hdrincl - Level IP HDRINCL option
*/
#if defined(IP_HDRINCL)
static
-ERL_NIF_TERM nsetopt_lvl_ip_hdrincl(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_ip_hdrincl(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
#if defined(SOL_IP)
int level = SOL_IP;
@@ -8776,19 +8802,19 @@ ERL_NIF_TERM nsetopt_lvl_ip_hdrincl(ErlNifEnv* env,
int level = IPPROTO_IP;
#endif
- return nsetopt_bool_opt(env, descP, level, IP_HDRINCL, eVal);
+ return esock_setopt_bool_opt(env, descP, level, IP_HDRINCL, eVal);
}
#endif
-/* nsetopt_lvl_ip_minttl - Level IP MINTTL option
+/* esock_setopt_lvl_ip_minttl - Level IP MINTTL option
*/
#if defined(IP_MINTTL)
static
-ERL_NIF_TERM nsetopt_lvl_ip_minttl(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_ip_minttl(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
#if defined(SOL_IP)
int level = SOL_IP;
@@ -8796,26 +8822,26 @@ ERL_NIF_TERM nsetopt_lvl_ip_minttl(ErlNifEnv* env,
int level = IPPROTO_IP;
#endif
- return nsetopt_int_opt(env, descP, level, IP_MINTTL, eVal);
+ return esock_setopt_int_opt(env, descP, level, IP_MINTTL, eVal);
}
#endif
-/* nsetopt_lvl_ip_msfilter - Level IP MSFILTER option
+/* esock_setopt_lvl_ip_msfilter - Level IP MSFILTER option
*
* The value can be *either* the atom 'null' or a map of type ip_msfilter().
*/
#if defined(IP_MSFILTER) && defined(IP_MSFILTER_SIZE)
static
-ERL_NIF_TERM nsetopt_lvl_ip_msfilter(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_ip_msfilter(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
ERL_NIF_TERM result;
if (COMPARE(eVal, atom_null) == 0) {
- return nsetopt_lvl_ip_msfilter_set(env, descP->sock, NULL, 0);
+ return esock_setopt_lvl_ip_msfilter_set(env, descP->sock, NULL, 0);
} else {
struct ip_msfilter* msfP;
Uint32 msfSz;
@@ -8880,7 +8906,8 @@ ERL_NIF_TERM nsetopt_lvl_ip_msfilter(ErlNifEnv* env,
}
/* And now, finally, set the option */
- result = nsetopt_lvl_ip_msfilter_set(env, descP->sock, msfP, msfSz);
+ result = esock_setopt_lvl_ip_msfilter_set(env, descP->sock,
+ msfP, msfSz);
FREE(msfP);
return result;
}
@@ -8910,10 +8937,10 @@ BOOLEAN_T decode_ip_msfilter_mode(ErlNifEnv* env,
static
-ERL_NIF_TERM nsetopt_lvl_ip_msfilter_set(ErlNifEnv* env,
- SOCKET sock,
- struct ip_msfilter* msfP,
- SOCKLEN_T optLen)
+ERL_NIF_TERM esock_setopt_lvl_ip_msfilter_set(ErlNifEnv* env,
+ SOCKET sock,
+ struct ip_msfilter* msfP,
+ SOCKLEN_T optLen)
{
ERL_NIF_TERM result;
int res;
@@ -8935,15 +8962,15 @@ ERL_NIF_TERM nsetopt_lvl_ip_msfilter_set(ErlNifEnv* env,
-/* nsetopt_lvl_ip_mtu_discover - Level IP MTU_DISCOVER option
+/* esock_setopt_lvl_ip_mtu_discover - Level IP MTU_DISCOVER option
*
* The value is an atom of the type ip_pmtudisc().
*/
#if defined(IP_MTU_DISCOVER)
static
-ERL_NIF_TERM nsetopt_lvl_ip_mtu_discover(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_ip_mtu_discover(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
ERL_NIF_TERM result;
int val;
@@ -8976,13 +9003,13 @@ ERL_NIF_TERM nsetopt_lvl_ip_mtu_discover(ErlNifEnv* env,
#endif
-/* nsetopt_lvl_ip_multicast_all - Level IP MULTICAST_ALL option
+/* esock_setopt_lvl_ip_multicast_all - Level IP MULTICAST_ALL option
*/
#if defined(IP_MULTICAST_ALL)
static
-ERL_NIF_TERM nsetopt_lvl_ip_multicast_all(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_ip_multicast_all(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
#if defined(SOL_IP)
int level = SOL_IP;
@@ -8990,20 +9017,20 @@ ERL_NIF_TERM nsetopt_lvl_ip_multicast_all(ErlNifEnv* env,
int level = IPPROTO_IP;
#endif
- return nsetopt_bool_opt(env, descP, level, IP_MULTICAST_ALL, eVal);
+ return esock_setopt_bool_opt(env, descP, level, IP_MULTICAST_ALL, eVal);
}
#endif
-/* nsetopt_lvl_ip_multicast_if - Level IP MULTICAST_IF option
+/* esock_setopt_lvl_ip_multicast_if - Level IP MULTICAST_IF option
*
* The value is either the atom 'any' or a 4-tuple.
*/
#if defined(IP_MULTICAST_IF)
static
-ERL_NIF_TERM nsetopt_lvl_ip_multicast_if(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_ip_multicast_if(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
ERL_NIF_TERM result;
struct in_addr ifAddr;
@@ -9034,13 +9061,13 @@ ERL_NIF_TERM nsetopt_lvl_ip_multicast_if(ErlNifEnv* env,
#endif
-/* nsetopt_lvl_ip_multicast_loop - Level IP MULTICAST_LOOP option
+/* esock_setopt_lvl_ip_multicast_loop - Level IP MULTICAST_LOOP option
*/
#if defined(IP_MULTICAST_LOOP)
static
-ERL_NIF_TERM nsetopt_lvl_ip_multicast_loop(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_ip_multicast_loop(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
#if defined(SOL_IP)
int level = SOL_IP;
@@ -9048,18 +9075,18 @@ ERL_NIF_TERM nsetopt_lvl_ip_multicast_loop(ErlNifEnv* env,
int level = IPPROTO_IP;
#endif
- return nsetopt_bool_opt(env, descP, level, IP_MULTICAST_LOOP, eVal);
+ return esock_setopt_bool_opt(env, descP, level, IP_MULTICAST_LOOP, eVal);
}
#endif
-/* nsetopt_lvl_ip_multicast_ttl - Level IP MULTICAST_TTL option
+/* esock_setopt_lvl_ip_multicast_ttl - Level IP MULTICAST_TTL option
*/
#if defined(IP_MULTICAST_TTL)
static
-ERL_NIF_TERM nsetopt_lvl_ip_multicast_ttl(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_ip_multicast_ttl(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
#if defined(SOL_IP)
int level = SOL_IP;
@@ -9067,18 +9094,18 @@ ERL_NIF_TERM nsetopt_lvl_ip_multicast_ttl(ErlNifEnv* env,
int level = IPPROTO_IP;
#endif
- return nsetopt_int_opt(env, descP, level, IP_MULTICAST_TTL, eVal);
+ return esock_setopt_int_opt(env, descP, level, IP_MULTICAST_TTL, eVal);
}
#endif
-/* nsetopt_lvl_ip_nodefrag - Level IP NODEFRAG option
+/* esock_setopt_lvl_ip_nodefrag - Level IP NODEFRAG option
*/
#if defined(IP_NODEFRAG)
static
-ERL_NIF_TERM nsetopt_lvl_ip_nodefrag(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_ip_nodefrag(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
#if defined(SOL_IP)
int level = SOL_IP;
@@ -9086,18 +9113,18 @@ ERL_NIF_TERM nsetopt_lvl_ip_nodefrag(ErlNifEnv* env,
int level = IPPROTO_IP;
#endif
- return nsetopt_bool_opt(env, descP, level, IP_NODEFRAG, eVal);
+ return esock_setopt_bool_opt(env, descP, level, IP_NODEFRAG, eVal);
}
#endif
-/* nsetopt_lvl_ip_pktinfo - Level IP PKTINFO option
+/* esock_setopt_lvl_ip_pktinfo - Level IP PKTINFO option
*/
#if defined(IP_PKTINFO)
static
-ERL_NIF_TERM nsetopt_lvl_ip_pktinfo(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_ip_pktinfo(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
#if defined(SOL_IP)
int level = SOL_IP;
@@ -9105,18 +9132,18 @@ ERL_NIF_TERM nsetopt_lvl_ip_pktinfo(ErlNifEnv* env,
int level = IPPROTO_IP;
#endif
- return nsetopt_bool_opt(env, descP, level, IP_PKTINFO, eVal);
+ return esock_setopt_bool_opt(env, descP, level, IP_PKTINFO, eVal);
}
#endif
-/* nsetopt_lvl_ip_recvdstaddr - Level IP RECVDSTADDR option
+/* esock_setopt_lvl_ip_recvdstaddr - Level IP RECVDSTADDR option
*/
#if defined(IP_RECVDSTADDR)
static
-ERL_NIF_TERM nsetopt_lvl_ip_recvdstaddr(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_ip_recvdstaddr(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
#if defined(SOL_IP)
int level = SOL_IP;
@@ -9124,18 +9151,18 @@ ERL_NIF_TERM nsetopt_lvl_ip_recvdstaddr(ErlNifEnv* env,
int level = IPPROTO_IP;
#endif
- return nsetopt_bool_opt(env, descP, level, IP_RECVDSTADDR, eVal);
+ return esock_setopt_bool_opt(env, descP, level, IP_RECVDSTADDR, eVal);
}
#endif
-/* nsetopt_lvl_ip_recverr - Level IP RECVERR option
+/* esock_setopt_lvl_ip_recverr - Level IP RECVERR option
*/
#if defined(IP_RECVERR)
static
-ERL_NIF_TERM nsetopt_lvl_ip_recverr(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_ip_recverr(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
#if defined(SOL_IP)
int level = SOL_IP;
@@ -9143,18 +9170,18 @@ ERL_NIF_TERM nsetopt_lvl_ip_recverr(ErlNifEnv* env,
int level = IPPROTO_IP;
#endif
- return nsetopt_bool_opt(env, descP, level, IP_RECVERR, eVal);
+ return esock_setopt_bool_opt(env, descP, level, IP_RECVERR, eVal);
}
#endif
-/* nsetopt_lvl_ip_recvif - Level IP RECVIF option
+/* esock_setopt_lvl_ip_recvif - Level IP RECVIF option
*/
#if defined(IP_RECVIF)
static
-ERL_NIF_TERM nsetopt_lvl_ip_recvif(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_ip_recvif(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
#if defined(SOL_IP)
int level = SOL_IP;
@@ -9162,18 +9189,18 @@ ERL_NIF_TERM nsetopt_lvl_ip_recvif(ErlNifEnv* env,
int level = IPPROTO_IP;
#endif
- return nsetopt_bool_opt(env, descP, level, IP_RECVIF, eVal);
+ return esock_setopt_bool_opt(env, descP, level, IP_RECVIF, eVal);
}
#endif
-/* nsetopt_lvl_ip_recvopts - Level IP RECVOPTS option
+/* esock_setopt_lvl_ip_recvopts - Level IP RECVOPTS option
*/
#if defined(IP_RECVOPTS)
static
-ERL_NIF_TERM nsetopt_lvl_ip_recvopts(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_ip_recvopts(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
#if defined(SOL_IP)
int level = SOL_IP;
@@ -9181,18 +9208,18 @@ ERL_NIF_TERM nsetopt_lvl_ip_recvopts(ErlNifEnv* env,
int level = IPPROTO_IP;
#endif
- return nsetopt_bool_opt(env, descP, level, IP_RECVOPTS, eVal);
+ return esock_setopt_bool_opt(env, descP, level, IP_RECVOPTS, eVal);
}
#endif
-/* nsetopt_lvl_ip_recvorigdstaddr - Level IP RECVORIGDSTADDR option
+/* esock_setopt_lvl_ip_recvorigdstaddr - Level IP RECVORIGDSTADDR option
*/
#if defined(IP_RECVORIGDSTADDR)
static
-ERL_NIF_TERM nsetopt_lvl_ip_recvorigdstaddr(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_ip_recvorigdstaddr(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
#if defined(SOL_IP)
int level = SOL_IP;
@@ -9200,18 +9227,18 @@ ERL_NIF_TERM nsetopt_lvl_ip_recvorigdstaddr(ErlNifEnv* env,
int level = IPPROTO_IP;
#endif
- return nsetopt_bool_opt(env, descP, level, IP_RECVORIGDSTADDR, eVal);
+ return esock_setopt_bool_opt(env, descP, level, IP_RECVORIGDSTADDR, eVal);
}
#endif
-/* nsetopt_lvl_ip_recvtos - Level IP RECVTOS option
+/* esock_setopt_lvl_ip_recvtos - Level IP RECVTOS option
*/
#if defined(IP_RECVTOS)
static
-ERL_NIF_TERM nsetopt_lvl_ip_recvtos(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_ip_recvtos(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
#if defined(SOL_IP)
int level = SOL_IP;
@@ -9219,18 +9246,18 @@ ERL_NIF_TERM nsetopt_lvl_ip_recvtos(ErlNifEnv* env,
int level = IPPROTO_IP;
#endif
- return nsetopt_bool_opt(env, descP, level, IP_RECVTOS, eVal);
+ return esock_setopt_bool_opt(env, descP, level, IP_RECVTOS, eVal);
}
#endif
-/* nsetopt_lvl_ip_recvttl - Level IP RECVTTL option
+/* esock_setopt_lvl_ip_recvttl - Level IP RECVTTL option
*/
#if defined(IP_RECVTTL)
static
-ERL_NIF_TERM nsetopt_lvl_ip_recvttl(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_ip_recvttl(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
#if defined(SOL_IP)
int level = SOL_IP;
@@ -9238,18 +9265,18 @@ ERL_NIF_TERM nsetopt_lvl_ip_recvttl(ErlNifEnv* env,
int level = IPPROTO_IP;
#endif
- return nsetopt_bool_opt(env, descP, level, IP_RECVTTL, eVal);
+ return esock_setopt_bool_opt(env, descP, level, IP_RECVTTL, eVal);
}
#endif
-/* nsetopt_lvl_ip_retopts - Level IP RETOPTS option
+/* esock_setopt_lvl_ip_retopts - Level IP RETOPTS option
*/
#if defined(IP_RETOPTS)
static
-ERL_NIF_TERM nsetopt_lvl_ip_retopts(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_ip_retopts(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
#if defined(SOL_IP)
int level = SOL_IP;
@@ -9257,18 +9284,18 @@ ERL_NIF_TERM nsetopt_lvl_ip_retopts(ErlNifEnv* env,
int level = IPPROTO_IP;
#endif
- return nsetopt_bool_opt(env, descP, level, IP_RETOPTS, eVal);
+ return esock_setopt_bool_opt(env, descP, level, IP_RETOPTS, eVal);
}
#endif
-/* nsetopt_lvl_ip_router_alert - Level IP ROUTER_ALERT option
+/* esock_setopt_lvl_ip_router_alert - Level IP ROUTER_ALERT option
*/
#if defined(IP_ROUTER_ALERT)
static
-ERL_NIF_TERM nsetopt_lvl_ip_router_alert(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_ip_router_alert(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
#if defined(SOL_IP)
int level = SOL_IP;
@@ -9276,18 +9303,18 @@ ERL_NIF_TERM nsetopt_lvl_ip_router_alert(ErlNifEnv* env,
int level = IPPROTO_IP;
#endif
- return nsetopt_int_opt(env, descP, level, IP_ROUTER_ALERT, eVal);
+ return esock_setopt_int_opt(env, descP, level, IP_ROUTER_ALERT, eVal);
}
#endif
-/* nsetopt_lvl_ip_sendsrcaddr - Level IP SENDSRCADDR option
+/* esock_setopt_lvl_ip_sendsrcaddr - Level IP SENDSRCADDR option
*/
#if defined(IP_SENDSRCADDR)
static
-ERL_NIF_TERM nsetopt_lvl_ip_sendsrcaddr(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_ip_sendsrcaddr(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
#if defined(SOL_IP)
int level = SOL_IP;
@@ -9295,18 +9322,18 @@ ERL_NIF_TERM nsetopt_lvl_ip_sendsrcaddr(ErlNifEnv* env,
int level = IPPROTO_IP;
#endif
- return nsetopt_bool_opt(env, descP, level, IP_SENDSRCADDR, eVal);
+ return esock_setopt_bool_opt(env, descP, level, IP_SENDSRCADDR, eVal);
}
#endif
-/* nsetopt_lvl_ip_tos - Level IP TOS option
+/* esock_setopt_lvl_ip_tos - Level IP TOS option
*/
#if defined(IP_TOS)
static
-ERL_NIF_TERM nsetopt_lvl_ip_tos(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_ip_tos(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
#if defined(SOL_IP)
int level = SOL_IP;
@@ -9333,13 +9360,13 @@ ERL_NIF_TERM nsetopt_lvl_ip_tos(ErlNifEnv* env,
#endif
-/* nsetopt_lvl_ip_transparent - Level IP TRANSPARENT option
+/* esock_setopt_lvl_ip_transparent - Level IP TRANSPARENT option
*/
#if defined(IP_TRANSPARENT)
static
-ERL_NIF_TERM nsetopt_lvl_ip_transparent(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_ip_transparent(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
#if defined(SOL_IP)
int level = SOL_IP;
@@ -9347,19 +9374,19 @@ ERL_NIF_TERM nsetopt_lvl_ip_transparent(ErlNifEnv* env,
int level = IPPROTO_IP;
#endif
- return nsetopt_bool_opt(env, descP, level, IP_TRANSPARENT, eVal);
+ return esock_setopt_bool_opt(env, descP, level, IP_TRANSPARENT, eVal);
}
#endif
-/* nsetopt_lvl_ip_ttl - Level IP TTL option
+/* esock_setopt_lvl_ip_ttl - Level IP TTL option
*/
#if defined(IP_TTL)
static
-ERL_NIF_TERM nsetopt_lvl_ip_ttl(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_ip_ttl(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
#if defined(SOL_IP)
int level = SOL_IP;
@@ -9367,13 +9394,13 @@ ERL_NIF_TERM nsetopt_lvl_ip_ttl(ErlNifEnv* env,
int level = IPPROTO_IP;
#endif
- return nsetopt_int_opt(env, descP, level, IP_TTL, eVal);
+ return esock_setopt_int_opt(env, descP, level, IP_TTL, eVal);
}
#endif
-/* nsetopt_lvl_ip_unblock_source - Level IP UNBLOCK_SOURCE option
+/* esock_setopt_lvl_ip_unblock_source - Level IP UNBLOCK_SOURCE option
*
* The value is a map with three attributes: multiaddr, interface and
* sourceaddr.
@@ -9384,11 +9411,12 @@ ERL_NIF_TERM nsetopt_lvl_ip_ttl(ErlNifEnv* env,
*/
#if defined(IP_UNBLOCK_SOURCE)
static
-ERL_NIF_TERM nsetopt_lvl_ip_unblock_source(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_ip_unblock_source(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
- return nsetopt_lvl_ip_update_source(env, descP, eVal, IP_UNBLOCK_SOURCE);
+ return esock_setopt_lvl_ip_update_source(env, descP, eVal,
+ IP_UNBLOCK_SOURCE);
}
#endif
@@ -9396,10 +9424,10 @@ ERL_NIF_TERM nsetopt_lvl_ip_unblock_source(ErlNifEnv* env,
#if defined(IP_ADD_MEMBERSHIP) || defined(IP_DROP_MEMBERSHIP)
static
-ERL_NIF_TERM nsetopt_lvl_ip_update_membership(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal,
- int opt)
+ERL_NIF_TERM esock_setopt_lvl_ip_update_membership(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal,
+ int opt)
{
ERL_NIF_TERM result, eMultiAddr, eInterface;
struct ip_mreq mreq;
@@ -9415,7 +9443,7 @@ ERL_NIF_TERM nsetopt_lvl_ip_update_membership(ErlNifEnv* env,
// It must be a map
if (!IS_MAP(env, eVal)) {
SSDBG( descP,
- ("SOCKET", "nsetopt_lvl_ip_update_membership -> "
+ ("SOCKET", "esock_setopt_lvl_ip_update_membership -> "
"value *not* a map\r\n") );
return enif_make_badarg(env);
}
@@ -9423,21 +9451,21 @@ ERL_NIF_TERM nsetopt_lvl_ip_update_membership(ErlNifEnv* env,
// It must have atleast two attributes
if (!enif_get_map_size(env, eVal, &sz) || (sz < 2)) {
SSDBG( descP,
- ("SOCKET", "nsetopt_lvl_ip_update_membership -> "
+ ("SOCKET", "esock_setopt_lvl_ip_update_membership -> "
"invalid map value: %T\r\n", eVal) );
return enif_make_badarg(env);
}
if (!GET_MAP_VAL(env, eVal, atom_multiaddr, &eMultiAddr)) {
SSDBG( descP,
- ("SOCKET", "nsetopt_lvl_ip_update_membership -> "
+ ("SOCKET", "esock_setopt_lvl_ip_update_membership -> "
"failed get multiaddr (map) attribute\r\n") );
return enif_make_badarg(env);
}
if (!GET_MAP_VAL(env, eVal, atom_interface, &eInterface)) {
SSDBG( descP,
- ("SOCKET", "nsetopt_lvl_ip_update_membership -> "
+ ("SOCKET", "esock_setopt_lvl_ip_update_membership -> "
"failed get interface (map) attribute\r\n") );
return enif_make_badarg(env);
}
@@ -9446,7 +9474,7 @@ ERL_NIF_TERM nsetopt_lvl_ip_update_membership(ErlNifEnv* env,
eMultiAddr,
&mreq.imr_multiaddr)) != NULL) {
SSDBG( descP,
- ("SOCKET", "nsetopt_lvl_ip_update_membership -> "
+ ("SOCKET", "esock_setopt_lvl_ip_update_membership -> "
"failed decode multiaddr %T: %s\r\n", eMultiAddr, xres) );
return esock_make_error_str(env, xres);
}
@@ -9455,7 +9483,7 @@ ERL_NIF_TERM nsetopt_lvl_ip_update_membership(ErlNifEnv* env,
eInterface,
&mreq.imr_interface)) != NULL) {
SSDBG( descP,
- ("SOCKET", "nsetopt_lvl_ip_update_membership -> "
+ ("SOCKET", "esock_setopt_lvl_ip_update_membership -> "
"failed decode interface %T: %s\r\n", eInterface, xres) );
return esock_make_error_str(env, xres);
}
@@ -9468,7 +9496,7 @@ ERL_NIF_TERM nsetopt_lvl_ip_update_membership(ErlNifEnv* env,
result = esock_make_error_errno(env, save_errno);
SSDBG( descP,
- ("SOCKET", "nsetopt_lvl_ip_update_membership -> "
+ ("SOCKET", "esock_setopt_lvl_ip_update_membership -> "
"failed setopt: %T (%d)\r\n", result, save_errno) );
} else {
@@ -9482,10 +9510,10 @@ ERL_NIF_TERM nsetopt_lvl_ip_update_membership(ErlNifEnv* env,
#if defined(IP_ADD_SOURCE_MEMBERSHIP) || defined(IP_DROP_SOURCE_MEMBERSHIP) || defined(IP_BLOCK_SOURCE) || defined(IP_UNBLOCK_SOURCE)
static
-ERL_NIF_TERM nsetopt_lvl_ip_update_source(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal,
- int opt)
+ERL_NIF_TERM esock_setopt_lvl_ip_update_source(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal,
+ int opt)
{
ERL_NIF_TERM result, eMultiAddr, eInterface, eSourceAddr;
struct ip_mreq_source mreq;
@@ -9545,146 +9573,147 @@ ERL_NIF_TERM nsetopt_lvl_ip_update_source(ErlNifEnv* env,
/* *** Handling set of socket options for level = ipv6 *** */
-/* nsetopt_lvl_ipv6 - Level *IPv6* option(s)
+/* esock_setopt_lvl_ipv6 - Level *IPv6* option(s)
*/
#if defined(HAVE_IPV6)
static
-ERL_NIF_TERM nsetopt_lvl_ipv6(ErlNifEnv* env,
- ESockDescriptor* descP,
- int eOpt,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_ipv6(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int eOpt,
+ ERL_NIF_TERM eVal)
{
ERL_NIF_TERM result;
SSDBG( descP,
- ("SOCKET", "nsetopt_lvl_ipv6 -> entry with"
+ ("SOCKET", "esock_setopt_lvl_ipv6 -> entry with"
"\r\n opt: %d"
"\r\n", eOpt) );
switch (eOpt) {
#if defined(IPV6_ADDRFORM)
- case SOCKET_OPT_IPV6_ADDRFORM:
- result = nsetopt_lvl_ipv6_addrform(env, descP, eVal);
+ case ESOCK_OPT_IPV6_ADDRFORM:
+ result = esock_setopt_lvl_ipv6_addrform(env, descP, eVal);
break;
#endif
#if defined(IPV6_ADD_MEMBERSHIP)
- case SOCKET_OPT_IPV6_ADD_MEMBERSHIP:
- result = nsetopt_lvl_ipv6_add_membership(env, descP, eVal);
+ case ESOCK_OPT_IPV6_ADD_MEMBERSHIP:
+ result = esock_setopt_lvl_ipv6_add_membership(env, descP, eVal);
break;
#endif
#if defined(IPV6_AUTHHDR)
- case SOCKET_OPT_IPV6_AUTHHDR:
- result = nsetopt_lvl_ipv6_authhdr(env, descP, eVal);
+ case ESOCK_OPT_IPV6_AUTHHDR:
+ result = esock_setopt_lvl_ipv6_authhdr(env, descP, eVal);
break;
#endif
#if defined(IPV6_DROP_MEMBERSHIP)
- case SOCKET_OPT_IPV6_DROP_MEMBERSHIP:
- result = nsetopt_lvl_ipv6_drop_membership(env, descP, eVal);
+ case ESOCK_OPT_IPV6_DROP_MEMBERSHIP:
+ result = esock_setopt_lvl_ipv6_drop_membership(env, descP, eVal);
break;
#endif
#if defined(IPV6_DSTOPTS)
- case SOCKET_OPT_IPV6_DSTOPTS:
- result = nsetopt_lvl_ipv6_dstopts(env, descP, eVal);
+ case ESOCK_OPT_IPV6_DSTOPTS:
+ result = esock_setopt_lvl_ipv6_dstopts(env, descP, eVal);
break;
#endif
#if defined(IPV6_FLOWINFO)
- case SOCKET_OPT_IPV6_FLOWINFO:
- result = nsetopt_lvl_ipv6_flowinfo(env, descP, eVal);
+ case ESOCK_OPT_IPV6_FLOWINFO:
+ result = esock_setopt_lvl_ipv6_flowinfo(env, descP, eVal);
break;
#endif
#if defined(IPV6_HOPLIMIT)
- case SOCKET_OPT_IPV6_HOPLIMIT:
- result = nsetopt_lvl_ipv6_hoplimit(env, descP, eVal);
+ case ESOCK_OPT_IPV6_HOPLIMIT:
+ result = esock_setopt_lvl_ipv6_hoplimit(env, descP, eVal);
break;
#endif
#if defined(IPV6_HOPOPTS)
- case SOCKET_OPT_IPV6_HOPOPTS:
- result = nsetopt_lvl_ipv6_hopopts(env, descP, eVal);
+ case ESOCK_OPT_IPV6_HOPOPTS:
+ result = esock_setopt_lvl_ipv6_hopopts(env, descP, eVal);
break;
#endif
#if defined(IPV6_MTU)
- case SOCKET_OPT_IPV6_MTU:
- result = nsetopt_lvl_ipv6_mtu(env, descP, eVal);
+ case ESOCK_OPT_IPV6_MTU:
+ result = esock_setopt_lvl_ipv6_mtu(env, descP, eVal);
break;
#endif
#if defined(IPV6_MTU_DISCOVER)
- case SOCKET_OPT_IPV6_MTU_DISCOVER:
- result = nsetopt_lvl_ipv6_mtu_discover(env, descP, eVal);
+ case ESOCK_OPT_IPV6_MTU_DISCOVER:
+ result = esock_setopt_lvl_ipv6_mtu_discover(env, descP, eVal);
break;
#endif
#if defined(IPV6_MULTICAST_HOPS)
- case SOCKET_OPT_IPV6_MULTICAST_HOPS:
- result = nsetopt_lvl_ipv6_multicast_hops(env, descP, eVal);
+ case ESOCK_OPT_IPV6_MULTICAST_HOPS:
+ result = esock_setopt_lvl_ipv6_multicast_hops(env, descP, eVal);
break;
#endif
#if defined(IPV6_MULTICAST_IF)
- case SOCKET_OPT_IPV6_MULTICAST_IF:
- result = nsetopt_lvl_ipv6_multicast_if(env, descP, eVal);
+ case ESOCK_OPT_IPV6_MULTICAST_IF:
+ result = esock_setopt_lvl_ipv6_multicast_if(env, descP, eVal);
break;
#endif
#if defined(IPV6_MULTICAST_LOOP)
- case SOCKET_OPT_IPV6_MULTICAST_LOOP:
- result = nsetopt_lvl_ipv6_multicast_loop(env, descP, eVal);
+ case ESOCK_OPT_IPV6_MULTICAST_LOOP:
+ result = esock_setopt_lvl_ipv6_multicast_loop(env, descP, eVal);
break;
#endif
#if defined(IPV6_RECVERR)
- case SOCKET_OPT_IPV6_RECVERR:
- result = nsetopt_lvl_ipv6_recverr(env, descP, eVal);
+ case ESOCK_OPT_IPV6_RECVERR:
+ result = esock_setopt_lvl_ipv6_recverr(env, descP, eVal);
break;
#endif
#if defined(IPV6_RECVPKTINFO) || defined(IPV6_PKTINFO)
- case SOCKET_OPT_IPV6_RECVPKTINFO:
- result = nsetopt_lvl_ipv6_recvpktinfo(env, descP, eVal);
+ case ESOCK_OPT_IPV6_RECVPKTINFO:
+ result = esock_setopt_lvl_ipv6_recvpktinfo(env, descP, eVal);
break;
#endif
#if defined(IPV6_ROUTER_ALERT)
- case SOCKET_OPT_IPV6_ROUTER_ALERT:
- result = nsetopt_lvl_ipv6_router_alert(env, descP, eVal);
+ case ESOCK_OPT_IPV6_ROUTER_ALERT:
+ result = esock_setopt_lvl_ipv6_router_alert(env, descP, eVal);
break;
#endif
#if defined(IPV6_RTHDR)
- case SOCKET_OPT_IPV6_RTHDR:
- result = nsetopt_lvl_ipv6_rthdr(env, descP, eVal);
+ case ESOCK_OPT_IPV6_RTHDR:
+ result = esock_setopt_lvl_ipv6_rthdr(env, descP, eVal);
break;
#endif
#if defined(IPV6_UNICAST_HOPS)
- case SOCKET_OPT_IPV6_UNICAST_HOPS:
- result = nsetopt_lvl_ipv6_unicast_hops(env, descP, eVal);
+ case ESOCK_OPT_IPV6_UNICAST_HOPS:
+ result = esock_setopt_lvl_ipv6_unicast_hops(env, descP, eVal);
break;
#endif
#if defined(IPV6_V6ONLY)
- case SOCKET_OPT_IPV6_V6ONLY:
- result = nsetopt_lvl_ipv6_v6only(env, descP, eVal);
+ case ESOCK_OPT_IPV6_V6ONLY:
+ result = esock_setopt_lvl_ipv6_v6only(env, descP, eVal);
break;
#endif
default:
SSDBG( descP,
- ("SOCKET", "nsetopt_lvl_ipv6 -> unknown opt (%d)\r\n", eOpt) );
+ ("SOCKET",
+ "esock_setopt_lvl_ipv6 -> unknown opt (%d)\r\n", eOpt) );
result = esock_make_error(env, esock_atom_einval);
break;
}
SSDBG( descP,
- ("SOCKET", "nsetopt_lvl_ipv6 -> done when"
+ ("SOCKET", "esock_setopt_lvl_ipv6 -> done when"
"\r\n result: %T"
"\r\n", result) );
@@ -9694,15 +9723,15 @@ ERL_NIF_TERM nsetopt_lvl_ipv6(ErlNifEnv* env,
#if defined(IPV6_ADDRFORM)
static
-ERL_NIF_TERM nsetopt_lvl_ipv6_addrform(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_ipv6_addrform(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
ERL_NIF_TERM result;
int res, edomain, domain;
SSDBG( descP,
- ("SOCKET", "nsetopt_lvl_ipv6_addrform -> entry with"
+ ("SOCKET", "esock_setopt_lvl_ipv6_addrform -> entry with"
"\r\n eVal: %T"
"\r\n", eVal) );
@@ -9710,14 +9739,15 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_addrform(ErlNifEnv* env,
return esock_make_error(env, esock_atom_einval);
SSDBG( descP,
- ("SOCKET", "nsetopt_lvl_ipv6_addrform -> decode"
+ ("SOCKET", "esock_setopt_lvl_ipv6_addrform -> decode"
"\r\n edomain: %d"
"\r\n", edomain) );
if (!edomain2domain(edomain, &domain))
return esock_make_error(env, esock_atom_einval);
- SSDBG( descP, ("SOCKET", "nsetopt_lvl_ipv6_addrform -> try set opt to %d\r\n",
+ SSDBG( descP, ("SOCKET",
+ "esock_setopt_lvl_ipv6_addrform -> try set opt to %d\r\n",
domain) );
res = socket_setopt(descP->sock,
@@ -9740,11 +9770,11 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_addrform(ErlNifEnv* env,
#if defined(IPV6_ADD_MEMBERSHIP)
static
-ERL_NIF_TERM nsetopt_lvl_ipv6_add_membership(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_ipv6_add_membership(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
- return nsetopt_lvl_ipv6_update_membership(env, descP, eVal,
+ return esock_setopt_lvl_ipv6_update_membership(env, descP, eVal,
IPV6_ADD_MEMBERSHIP);
}
#endif
@@ -9752,28 +9782,29 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_add_membership(ErlNifEnv* env,
#if defined(IPV6_AUTHHDR)
static
-ERL_NIF_TERM nsetopt_lvl_ipv6_authhdr(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_ipv6_authhdr(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
- return nsetopt_bool_opt(env, descP,
#if defined(SOL_IPV6)
- SOL_IPV6,
+ int level = SOL_IPV6;
#else
- IPPROTO_IPV6,
+ int level = IPPROTO_IPV6;
#endif
- IPV6_AUTHHDR, eVal);
+
+
+ return esock_setopt_bool_opt(env, descP, level, IPV6_AUTHHDR, eVal);
}
#endif
#if defined(IPV6_DROP_MEMBERSHIP)
static
-ERL_NIF_TERM nsetopt_lvl_ipv6_drop_membership(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_ipv6_drop_membership(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
- return nsetopt_lvl_ipv6_update_membership(env, descP, eVal,
+ return esock_setopt_lvl_ipv6_update_membership(env, descP, eVal,
IPV6_DROP_MEMBERSHIP);
}
#endif
@@ -9781,9 +9812,9 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_drop_membership(ErlNifEnv* env,
#if defined(IPV6_DSTOPTS)
static
-ERL_NIF_TERM nsetopt_lvl_ipv6_dstopts(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_ipv6_dstopts(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
#if defined(SOL_IPV6)
int level = SOL_IPV6;
@@ -9791,16 +9822,16 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_dstopts(ErlNifEnv* env,
int level = IPPROTO_IPV6;
#endif
- return nsetopt_bool_opt(env, descP, level, IPV6_DSTOPTS, eVal);
+ return esock_setopt_bool_opt(env, descP, level, IPV6_DSTOPTS, eVal);
}
#endif
#if defined(IPV6_FLOWINFO)
static
-ERL_NIF_TERM nsetopt_lvl_ipv6_flowinfo(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_ipv6_flowinfo(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
#if defined(SOL_IPV6)
int level = SOL_IPV6;
@@ -9808,16 +9839,16 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_flowinfo(ErlNifEnv* env,
int level = IPPROTO_IPV6;
#endif
- return nsetopt_bool_opt(env, descP, level, IPV6_FLOWINFO, eVal);
+ return esock_setopt_bool_opt(env, descP, level, IPV6_FLOWINFO, eVal);
}
#endif
#if defined(IPV6_HOPLIMIT)
static
-ERL_NIF_TERM nsetopt_lvl_ipv6_hoplimit(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_ipv6_hoplimit(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
#if defined(SOL_IPV6)
int level = SOL_IPV6;
@@ -9825,16 +9856,16 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_hoplimit(ErlNifEnv* env,
int level = IPPROTO_IPV6;
#endif
- return nsetopt_bool_opt(env, descP, level, IPV6_HOPLIMIT, eVal);
+ return esock_setopt_bool_opt(env, descP, level, IPV6_HOPLIMIT, eVal);
}
#endif
#if defined(IPV6_HOPOPTS)
static
-ERL_NIF_TERM nsetopt_lvl_ipv6_hopopts(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_ipv6_hopopts(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
#if defined(SOL_IPV6)
int level = SOL_IPV6;
@@ -9842,14 +9873,14 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_hopopts(ErlNifEnv* env,
int level = IPPROTO_IPV6;
#endif
- return nsetopt_bool_opt(env, descP, level, IPV6_HOPOPTS, eVal);
+ return esock_setopt_bool_opt(env, descP, level, IPV6_HOPOPTS, eVal);
}
#endif
#if defined(IPV6_MTU)
static
-ERL_NIF_TERM nsetopt_lvl_ipv6_mtu(ErlNifEnv* env,
+ERL_NIF_TERM esock_setopt_lvl_ipv6_mtu(ErlNifEnv* env,
ESockDescriptor* descP,
ERL_NIF_TERM eVal)
{
@@ -9859,20 +9890,20 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_mtu(ErlNifEnv* env,
int level = IPPROTO_IPV6;
#endif
- return nsetopt_int_opt(env, descP, level, IPV6_MTU, eVal);
+ return esock_setopt_int_opt(env, descP, level, IPV6_MTU, eVal);
}
#endif
-/* nsetopt_lvl_ipv6_mtu_discover - Level IPv6 MTU_DISCOVER option
+/* esock_setopt_lvl_ipv6_mtu_discover - Level IPv6 MTU_DISCOVER option
*
* The value is an atom of the type ipv6_pmtudisc().
*/
#if defined(IPV6_MTU_DISCOVER)
static
-ERL_NIF_TERM nsetopt_lvl_ipv6_mtu_discover(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_ipv6_mtu_discover(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
ERL_NIF_TERM result;
int val;
@@ -9908,9 +9939,9 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_mtu_discover(ErlNifEnv* env,
#if defined(IPV6_MULTICAST_HOPS)
static
-ERL_NIF_TERM nsetopt_lvl_ipv6_multicast_hops(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_ipv6_multicast_hops(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
#if defined(SOL_IPV6)
int level = SOL_IPV6;
@@ -9918,7 +9949,7 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_multicast_hops(ErlNifEnv* env,
int level = IPPROTO_IPV6;
#endif
- return nsetopt_int_opt(env, descP, level, IPV6_MULTICAST_HOPS, eVal);
+ return esock_setopt_int_opt(env, descP, level, IPV6_MULTICAST_HOPS, eVal);
}
#endif
@@ -9926,9 +9957,9 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_multicast_hops(ErlNifEnv* env,
#if defined(IPV6_MULTICAST_IF)
static
-ERL_NIF_TERM nsetopt_lvl_ipv6_multicast_if(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_ipv6_multicast_if(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
#if defined(SOL_IPV6)
int level = SOL_IPV6;
@@ -9936,7 +9967,7 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_multicast_if(ErlNifEnv* env,
int level = IPPROTO_IPV6;
#endif
- return nsetopt_int_opt(env, descP, level, IPV6_MULTICAST_IF, eVal);
+ return esock_setopt_int_opt(env, descP, level, IPV6_MULTICAST_IF, eVal);
}
#endif
@@ -9944,9 +9975,9 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_multicast_if(ErlNifEnv* env,
#if defined(IPV6_MULTICAST_LOOP)
static
-ERL_NIF_TERM nsetopt_lvl_ipv6_multicast_loop(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_ipv6_multicast_loop(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
#if defined(SOL_IPV6)
int level = SOL_IPV6;
@@ -9954,16 +9985,16 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_multicast_loop(ErlNifEnv* env,
int level = IPPROTO_IPV6;
#endif
- return nsetopt_bool_opt(env, descP, level, IPV6_MULTICAST_LOOP, eVal);
+ return esock_setopt_bool_opt(env, descP, level, IPV6_MULTICAST_LOOP, eVal);
}
#endif
#if defined(IPV6_RECVERR)
static
-ERL_NIF_TERM nsetopt_lvl_ipv6_recverr(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_ipv6_recverr(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
#if defined(SOL_IPV6)
int level = SOL_IPV6;
@@ -9971,16 +10002,16 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_recverr(ErlNifEnv* env,
int level = IPPROTO_IPV6;
#endif
- return nsetopt_bool_opt(env, descP, level, IPV6_RECVERR, eVal);
+ return esock_setopt_bool_opt(env, descP, level, IPV6_RECVERR, eVal);
}
#endif
#if defined(IPV6_RECVPKTINFO) || defined(IPV6_PKTINFO)
static
-ERL_NIF_TERM nsetopt_lvl_ipv6_recvpktinfo(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_ipv6_recvpktinfo(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
#if defined(SOL_IPV6)
int level = SOL_IPV6;
@@ -9993,16 +10024,16 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_recvpktinfo(ErlNifEnv* env,
int opt = IPV6_PKTINFO;
#endif
- return nsetopt_bool_opt(env, descP, level, opt, eVal);
+ return esock_setopt_bool_opt(env, descP, level, opt, eVal);
}
#endif
#if defined(IPV6_ROUTER_ALERT)
static
-ERL_NIF_TERM nsetopt_lvl_ipv6_router_alert(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_ipv6_router_alert(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
#if defined(SOL_IPV6)
int level = SOL_IPV6;
@@ -10010,7 +10041,7 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_router_alert(ErlNifEnv* env,
int level = IPPROTO_IPV6;
#endif
- return nsetopt_int_opt(env, descP, level, IPV6_ROUTER_ALERT, eVal);
+ return esock_setopt_int_opt(env, descP, level, IPV6_ROUTER_ALERT, eVal);
}
#endif
@@ -10018,9 +10049,9 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_router_alert(ErlNifEnv* env,
#if defined(IPV6_RTHDR)
static
-ERL_NIF_TERM nsetopt_lvl_ipv6_rthdr(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_ipv6_rthdr(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
#if defined(SOL_IPV6)
int level = SOL_IPV6;
@@ -10028,16 +10059,16 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_rthdr(ErlNifEnv* env,
int level = IPPROTO_IPV6;
#endif
- return nsetopt_bool_opt(env, descP, level, IPV6_RTHDR, eVal);
+ return esock_setopt_bool_opt(env, descP, level, IPV6_RTHDR, eVal);
}
#endif
#if defined(IPV6_UNICAST_HOPS)
static
-ERL_NIF_TERM nsetopt_lvl_ipv6_unicast_hops(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_ipv6_unicast_hops(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
#if defined(SOL_IPV6)
int level = SOL_IPV6;
@@ -10045,7 +10076,7 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_unicast_hops(ErlNifEnv* env,
int level = IPPROTO_IPV6;
#endif
- return nsetopt_int_opt(env, descP, level, IPV6_UNICAST_HOPS, eVal);
+ return esock_setopt_int_opt(env, descP, level, IPV6_UNICAST_HOPS, eVal);
}
#endif
@@ -10053,9 +10084,9 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_unicast_hops(ErlNifEnv* env,
#if defined(IPV6_V6ONLY)
static
-ERL_NIF_TERM nsetopt_lvl_ipv6_v6only(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_ipv6_v6only(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
#if defined(SOL_IPV6)
int level = SOL_IPV6;
@@ -10063,17 +10094,17 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_v6only(ErlNifEnv* env,
int level = IPPROTO_IPV6;
#endif
- return nsetopt_bool_opt(env, descP, level, IPV6_V6ONLY, eVal);
+ return esock_setopt_bool_opt(env, descP, level, IPV6_V6ONLY, eVal);
}
#endif
#if defined(IPV6_ADD_MEMBERSHIP) || defined(IPV6_DROP_MEMBERSHIP)
static
-ERL_NIF_TERM nsetopt_lvl_ipv6_update_membership(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal,
- int opt)
+ERL_NIF_TERM esock_setopt_lvl_ipv6_update_membership(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal,
+ int opt)
{
ERL_NIF_TERM result, eMultiAddr, eInterface;
struct ipv6_mreq mreq;
@@ -10089,7 +10120,7 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_update_membership(ErlNifEnv* env,
// It must be a map
if (!IS_MAP(env, eVal)) {
SSDBG( descP,
- ("SOCKET", "nsetopt_lvl_ipv6_update_membership -> "
+ ("SOCKET", "esock_setopt_lvl_ipv6_update_membership -> "
"value *not* a map\r\n") );
return enif_make_badarg(env);
}
@@ -10097,21 +10128,21 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_update_membership(ErlNifEnv* env,
// It must have atleast two attributes
if (!enif_get_map_size(env, eVal, &sz) || (sz < 2)) {
SSDBG( descP,
- ("SOCKET", "nsetopt_lvl_ipv6_update_membership -> "
+ ("SOCKET", "esock_setopt_lvl_ipv6_update_membership -> "
"invalid map value: %T\r\n", eVal) );
return enif_make_badarg(env);
}
if (!GET_MAP_VAL(env, eVal, atom_multiaddr, &eMultiAddr)) {
SSDBG( descP,
- ("SOCKET", "nsetopt_lvl_ipv6_update_membership -> "
+ ("SOCKET", "esock_setopt_lvl_ipv6_update_membership -> "
"failed get multiaddr (map) attribute\r\n") );
return enif_make_badarg(env);
}
if (!GET_MAP_VAL(env, eVal, atom_interface, &eInterface)) {
SSDBG( descP,
- ("SOCKET", "nsetopt_lvl_ipv6_update_membership -> "
+ ("SOCKET", "esock_setopt_lvl_ipv6_update_membership -> "
"failed get interface (map) attribute\r\n") );
return enif_make_badarg(env);
}
@@ -10120,14 +10151,14 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_update_membership(ErlNifEnv* env,
eMultiAddr,
&mreq.ipv6mr_multiaddr)) != NULL) {
SSDBG( descP,
- ("SOCKET", "nsetopt_lvl_ipv6_update_membership -> "
+ ("SOCKET", "esock_setopt_lvl_ipv6_update_membership -> "
"failed decode multiaddr %T: %s\r\n", eMultiAddr, xres) );
return esock_make_error_str(env, xres);
}
if (!GET_UINT(env, eInterface, &mreq.ipv6mr_interface)) {
SSDBG( descP,
- ("SOCKET", "nsetopt_lvl_ip_update_membership -> "
+ ("SOCKET", "esock_setopt_lvl_ip_update_membership -> "
"failed decode interface %T: %s\r\n", eInterface, xres) );
return esock_make_error(env, esock_atom_einval);
}
@@ -10140,7 +10171,7 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_update_membership(ErlNifEnv* env,
result = esock_make_error_errno(env, save_errno);
SSDBG( descP,
- ("SOCKET", "nsetopt_lvl_ipv6_update_membership -> "
+ ("SOCKET", "esock_setopt_lvl_ipv6_update_membership -> "
"failed setopt: %T (%d)\r\n", result, save_errno) );
} else {
@@ -10157,37 +10188,37 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_update_membership(ErlNifEnv* env,
-/* nsetopt_lvl_tcp - Level *TCP* option(s)
+/* esock_setopt_lvl_tcp - Level *TCP* option(s)
*/
static
-ERL_NIF_TERM nsetopt_lvl_tcp(ErlNifEnv* env,
- ESockDescriptor* descP,
- int eOpt,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_tcp(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int eOpt,
+ ERL_NIF_TERM eVal)
{
ERL_NIF_TERM result;
SSDBG( descP,
- ("SOCKET", "nsetopt_lvl_tcp -> entry with"
+ ("SOCKET", "esock_setopt_lvl_tcp -> entry with"
"\r\n opt: %d"
"\r\n", eOpt) );
switch (eOpt) {
#if defined(TCP_CONGESTION)
- case SOCKET_OPT_TCP_CONGESTION:
- result = nsetopt_lvl_tcp_congestion(env, descP, eVal);
+ case ESOCK_OPT_TCP_CONGESTION:
+ result = esock_setopt_lvl_tcp_congestion(env, descP, eVal);
break;
#endif
#if defined(TCP_MAXSEG)
- case SOCKET_OPT_TCP_MAXSEG:
- result = nsetopt_lvl_tcp_maxseg(env, descP, eVal);
+ case ESOCK_OPT_TCP_MAXSEG:
+ result = esock_setopt_lvl_tcp_maxseg(env, descP, eVal);
break;
#endif
#if defined(TCP_NODELAY)
- case SOCKET_OPT_TCP_NODELAY:
- result = nsetopt_lvl_tcp_nodelay(env, descP, eVal);
+ case ESOCK_OPT_TCP_NODELAY:
+ result = esock_setopt_lvl_tcp_nodelay(env, descP, eVal);
break;
#endif
@@ -10200,67 +10231,68 @@ ERL_NIF_TERM nsetopt_lvl_tcp(ErlNifEnv* env,
}
-/* nsetopt_lvl_tcp_congestion - Level TCP CONGESTION option
+/* esock_setopt_lvl_tcp_congestion - Level TCP CONGESTION option
*/
#if defined(TCP_CONGESTION)
static
-ERL_NIF_TERM nsetopt_lvl_tcp_congestion(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_tcp_congestion(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
- int max = SOCKET_OPT_TCP_CONGESTION_NAME_MAX+1;
+ int max = ESOCK_OPT_TCP_CONGESTION_NAME_MAX+1;
- return nsetopt_str_opt(env, descP, IPPROTO_TCP, TCP_CONGESTION, max, eVal);
+ return esock_setopt_str_opt(env, descP,
+ IPPROTO_TCP, TCP_CONGESTION, max, eVal);
}
#endif
-/* nsetopt_lvl_tcp_maxseg - Level TCP MAXSEG option
+/* esock_setopt_lvl_tcp_maxseg - Level TCP MAXSEG option
*/
#if defined(TCP_MAXSEG)
static
-ERL_NIF_TERM nsetopt_lvl_tcp_maxseg(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_tcp_maxseg(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
- return nsetopt_int_opt(env, descP, IPPROTO_TCP, TCP_MAXSEG, eVal);
+ return esock_setopt_int_opt(env, descP, IPPROTO_TCP, TCP_MAXSEG, eVal);
}
#endif
-/* nsetopt_lvl_tcp_nodelay - Level TCP NODELAY option
+/* esock_setopt_lvl_tcp_nodelay - Level TCP NODELAY option
*/
#if defined(TCP_NODELAY)
static
-ERL_NIF_TERM nsetopt_lvl_tcp_nodelay(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_tcp_nodelay(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
- return nsetopt_bool_opt(env, descP, IPPROTO_TCP, TCP_NODELAY, eVal);
+ return esock_setopt_bool_opt(env, descP, IPPROTO_TCP, TCP_NODELAY, eVal);
}
#endif
-/* nsetopt_lvl_udp - Level *UDP* option(s)
+/* esock_setopt_lvl_udp - Level *UDP* option(s)
*/
static
-ERL_NIF_TERM nsetopt_lvl_udp(ErlNifEnv* env,
- ESockDescriptor* descP,
- int eOpt,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_udp(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int eOpt,
+ ERL_NIF_TERM eVal)
{
ERL_NIF_TERM result;
SSDBG( descP,
- ("SOCKET", "nsetopt_lvl_udp -> entry with"
+ ("SOCKET", "esock_setopt_lvl_udp -> entry with"
"\r\n opt: %d"
"\r\n", eOpt) );
switch (eOpt) {
#if defined(UDP_CORK)
- case SOCKET_OPT_UDP_CORK:
- result = nsetopt_lvl_udp_cork(env, descP, eVal);
+ case ESOCK_OPT_UDP_CORK:
+ result = esock_setopt_lvl_udp_cork(env, descP, eVal);
break;
#endif
@@ -10273,83 +10305,83 @@ ERL_NIF_TERM nsetopt_lvl_udp(ErlNifEnv* env,
}
-/* nsetopt_lvl_udp_cork - Level UDP CORK option
+/* esock_setopt_lvl_udp_cork - Level UDP CORK option
*/
#if defined(UDP_CORK)
static
-ERL_NIF_TERM nsetopt_lvl_udp_cork(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_udp_cork(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
- return nsetopt_bool_opt(env, descP, IPPROTO_UDP, UDP_CORK, eVal);
+ return esock_setopt_bool_opt(env, descP, IPPROTO_UDP, UDP_CORK, eVal);
}
#endif
-/* nsetopt_lvl_sctp - Level *SCTP* option(s)
+/* esock_setopt_lvl_sctp - Level *SCTP* option(s)
*/
#if defined(HAVE_SCTP)
static
-ERL_NIF_TERM nsetopt_lvl_sctp(ErlNifEnv* env,
- ESockDescriptor* descP,
- int eOpt,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_sctp(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int eOpt,
+ ERL_NIF_TERM eVal)
{
ERL_NIF_TERM result;
SSDBG( descP,
- ("SOCKET", "nsetopt_lvl_sctp -> entry with"
+ ("SOCKET", "esock_setopt_lvl_sctp -> entry with"
"\r\n opt: %d"
"\r\n", eOpt) );
switch (eOpt) {
#if defined(SCTP_ASSOCINFO)
- case SOCKET_OPT_SCTP_ASSOCINFO:
- result = nsetopt_lvl_sctp_associnfo(env, descP, eVal);
+ case ESOCK_OPT_SCTP_ASSOCINFO:
+ result = esock_setopt_lvl_sctp_associnfo(env, descP, eVal);
break;
#endif
#if defined(SCTP_AUTOCLOSE)
- case SOCKET_OPT_SCTP_AUTOCLOSE:
- result = nsetopt_lvl_sctp_autoclose(env, descP, eVal);
+ case ESOCK_OPT_SCTP_AUTOCLOSE:
+ result = esock_setopt_lvl_sctp_autoclose(env, descP, eVal);
break;
#endif
#if defined(SCTP_DISABLE_FRAGMENTS)
- case SOCKET_OPT_SCTP_DISABLE_FRAGMENTS:
- result = nsetopt_lvl_sctp_disable_fragments(env, descP, eVal);
+ case ESOCK_OPT_SCTP_DISABLE_FRAGMENTS:
+ result = esock_setopt_lvl_sctp_disable_fragments(env, descP, eVal);
break;
#endif
#if defined(SCTP_EVENTS)
- case SOCKET_OPT_SCTP_EVENTS:
- result = nsetopt_lvl_sctp_events(env, descP, eVal);
+ case ESOCK_OPT_SCTP_EVENTS:
+ result = esock_setopt_lvl_sctp_events(env, descP, eVal);
break;
#endif
#if defined(SCTP_INITMSG)
- case SOCKET_OPT_SCTP_INITMSG:
- result = nsetopt_lvl_sctp_initmsg(env, descP, eVal);
+ case ESOCK_OPT_SCTP_INITMSG:
+ result = esock_setopt_lvl_sctp_initmsg(env, descP, eVal);
break;
#endif
#if defined(SCTP_MAXSEG)
- case SOCKET_OPT_SCTP_MAXSEG:
- result = nsetopt_lvl_sctp_maxseg(env, descP, eVal);
+ case ESOCK_OPT_SCTP_MAXSEG:
+ result = esock_setopt_lvl_sctp_maxseg(env, descP, eVal);
break;
#endif
#if defined(SCTP_NODELAY)
- case SOCKET_OPT_SCTP_NODELAY:
- result = nsetopt_lvl_sctp_nodelay(env, descP, eVal);
+ case ESOCK_OPT_SCTP_NODELAY:
+ result = esock_setopt_lvl_sctp_nodelay(env, descP, eVal);
break;
#endif
#if defined(SCTP_RTOINFO)
- case SOCKET_OPT_SCTP_RTOINFO:
- result = nsetopt_lvl_sctp_rtoinfo(env, descP, eVal);
+ case ESOCK_OPT_SCTP_RTOINFO:
+ result = esock_setopt_lvl_sctp_rtoinfo(env, descP, eVal);
break;
#endif
@@ -10362,13 +10394,13 @@ ERL_NIF_TERM nsetopt_lvl_sctp(ErlNifEnv* env,
}
-/* nsetopt_lvl_sctp_associnfo - Level SCTP ASSOCINFO option
+/* esock_setopt_lvl_sctp_associnfo - Level SCTP ASSOCINFO option
*/
#if defined(SCTP_ASSOCINFO)
static
-ERL_NIF_TERM nsetopt_lvl_sctp_associnfo(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_sctp_associnfo(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
ERL_NIF_TERM result;
ERL_NIF_TERM eAssocId, eMaxRxt, eNumPeerDests;
@@ -10379,7 +10411,7 @@ ERL_NIF_TERM nsetopt_lvl_sctp_associnfo(ErlNifEnv* env,
unsigned int tmp;
SSDBG( descP,
- ("SOCKET", "nsetopt_lvl_sctp_associnfo -> entry with"
+ ("SOCKET", "esock_setopt_lvl_sctp_associnfo -> entry with"
"\r\n eVal: %T"
"\r\n", eVal) );
@@ -10392,7 +10424,8 @@ ERL_NIF_TERM nsetopt_lvl_sctp_associnfo(ErlNifEnv* env,
return esock_make_error(env, esock_atom_einval);
SSDBG( descP,
- ("SOCKET", "nsetopt_lvl_sctp_associnfo -> extract attributes\r\n") );
+ ("SOCKET",
+ "esock_setopt_lvl_sctp_associnfo -> extract attributes\r\n") );
if (!GET_MAP_VAL(env, eVal, atom_assoc_id, &eAssocId))
return esock_make_error(env, esock_atom_einval);
@@ -10413,7 +10446,8 @@ ERL_NIF_TERM nsetopt_lvl_sctp_associnfo(ErlNifEnv* env,
return esock_make_error(env, esock_atom_einval);
SSDBG( descP,
- ("SOCKET", "nsetopt_lvl_sctp_associnfo -> decode attributes\r\n") );
+ ("SOCKET",
+ "esock_setopt_lvl_sctp_associnfo -> decode attributes\r\n") );
/* On some platforms the assoc id is typed as an unsigned integer (uint32)
* So, to avoid warnings there, we always make an explicit cast...
@@ -10466,7 +10500,8 @@ ERL_NIF_TERM nsetopt_lvl_sctp_associnfo(ErlNifEnv* env,
return esock_make_error(env, esock_atom_einval);
SSDBG( descP,
- ("SOCKET", "nsetopt_lvl_sctp_associnfo -> set associnfo option\r\n") );
+ ("SOCKET",
+ "esock_setopt_lvl_sctp_associnfo -> set associnfo option\r\n") );
res = socket_setopt(descP->sock, IPPROTO_SCTP, SCTP_ASSOCINFO,
&assocParams, sizeof(assocParams));
@@ -10477,7 +10512,7 @@ ERL_NIF_TERM nsetopt_lvl_sctp_associnfo(ErlNifEnv* env,
result = esock_atom_ok;
SSDBG( descP,
- ("SOCKET", "nsetopt_lvl_sctp_associnfo -> done with"
+ ("SOCKET", "esock_setopt_lvl_sctp_associnfo -> done with"
"\r\n result: %T"
"\r\n", result) );
@@ -10487,39 +10522,42 @@ ERL_NIF_TERM nsetopt_lvl_sctp_associnfo(ErlNifEnv* env,
#endif
-/* nsetopt_lvl_sctp_autoclose - Level SCTP AUTOCLOSE option
+/* esock_setopt_lvl_sctp_autoclose - Level SCTP AUTOCLOSE option
*/
#if defined(SCTP_AUTOCLOSE)
static
-ERL_NIF_TERM nsetopt_lvl_sctp_autoclose(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_sctp_autoclose(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
- return nsetopt_int_opt(env, descP, IPPROTO_SCTP, SCTP_AUTOCLOSE, eVal);
+ return esock_setopt_int_opt(env, descP,
+ IPPROTO_SCTP, SCTP_AUTOCLOSE, eVal);
}
#endif
-/* nsetopt_lvl_sctp_disable_fragments - Level SCTP DISABLE_FRAGMENTS option
+/* esock_setopt_lvl_sctp_disable_fragments -
+ * Level SCTP DISABLE_FRAGMENTS option
*/
#if defined(SCTP_DISABLE_FRAGMENTS)
static
-ERL_NIF_TERM nsetopt_lvl_sctp_disable_fragments(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_sctp_disable_fragments(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
- return nsetopt_bool_opt(env, descP, IPPROTO_SCTP, SCTP_DISABLE_FRAGMENTS, eVal);
+ return esock_setopt_bool_opt(env, descP,
+ IPPROTO_SCTP, SCTP_DISABLE_FRAGMENTS, eVal);
}
#endif
-/* nsetopt_lvl_sctp_events - Level SCTP EVENTS option
+/* esock_setopt_lvl_sctp_events - Level SCTP EVENTS option
*/
#if defined(SCTP_EVENTS)
static
-ERL_NIF_TERM nsetopt_lvl_sctp_events(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_sctp_events(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
ERL_NIF_TERM result;
ERL_NIF_TERM eDataIn, eAssoc, eAddr, eSndFailure;
@@ -10536,7 +10574,7 @@ ERL_NIF_TERM nsetopt_lvl_sctp_events(ErlNifEnv* env,
size_t sz;
SSDBG( descP,
- ("SOCKET", "nsetopt_lvl_sctp_events -> entry with"
+ ("SOCKET", "esock_setopt_lvl_sctp_events -> entry with"
"\r\n eVal: %T"
"\r\n", eVal) );
@@ -10549,7 +10587,8 @@ ERL_NIF_TERM nsetopt_lvl_sctp_events(ErlNifEnv* env,
return esock_make_error(env, esock_atom_einval);
SSDBG( descP,
- ("SOCKET", "nsetopt_lvl_sctp_events -> extract attributes\r\n") );
+ ("SOCKET",
+ "esock_setopt_lvl_sctp_events -> extract attributes\r\n") );
if (!GET_MAP_VAL(env, eVal, atom_data_in, &eDataIn))
return esock_make_error(env, esock_atom_einval);
@@ -10586,7 +10625,8 @@ ERL_NIF_TERM nsetopt_lvl_sctp_events(ErlNifEnv* env,
#endif
SSDBG( descP,
- ("SOCKET", "nsetopt_lvl_sctp_events -> decode attributes\r\n") );
+ ("SOCKET",
+ "esock_setopt_lvl_sctp_events -> decode attributes\r\n") );
events.sctp_data_io_event = esock_decode_bool(eDataIn);
events.sctp_association_event = esock_decode_bool(eAssoc);
@@ -10604,7 +10644,8 @@ ERL_NIF_TERM nsetopt_lvl_sctp_events(ErlNifEnv* env,
#endif
SSDBG( descP,
- ("SOCKET", "nsetopt_lvl_sctp_events -> set events option\r\n") );
+ ("SOCKET",
+ "esock_setopt_lvl_sctp_events -> set events option\r\n") );
res = socket_setopt(descP->sock, IPPROTO_SCTP, SCTP_EVENTS,
&events, sizeof(events));
@@ -10615,7 +10656,7 @@ ERL_NIF_TERM nsetopt_lvl_sctp_events(ErlNifEnv* env,
result = esock_atom_ok;
SSDBG( descP,
- ("SOCKET", "nsetopt_lvl_sctp_events -> done with"
+ ("SOCKET", "esock_setopt_lvl_sctp_events -> done with"
"\r\n result: %T"
"\r\n", result) );
@@ -10625,13 +10666,13 @@ ERL_NIF_TERM nsetopt_lvl_sctp_events(ErlNifEnv* env,
#endif
-/* nsetopt_lvl_sctp_initmsg - Level SCTP INITMSG option
+/* esock_setopt_lvl_sctp_initmsg - Level SCTP INITMSG option
*/
#if defined(SCTP_INITMSG)
static
-ERL_NIF_TERM nsetopt_lvl_sctp_initmsg(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_sctp_initmsg(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
ERL_NIF_TERM result;
ERL_NIF_TERM eNumOut, eMaxIn, eMaxAttempts, eMaxInitTO;
@@ -10641,7 +10682,7 @@ ERL_NIF_TERM nsetopt_lvl_sctp_initmsg(ErlNifEnv* env,
unsigned int tmp;
SSDBG( descP,
- ("SOCKET", "nsetopt_lvl_sctp_initmsg -> entry with"
+ ("SOCKET", "esock_setopt_lvl_sctp_initmsg -> entry with"
"\r\n eVal: %T"
"\r\n", eVal) );
@@ -10654,7 +10695,8 @@ ERL_NIF_TERM nsetopt_lvl_sctp_initmsg(ErlNifEnv* env,
return esock_make_error(env, esock_atom_einval);
SSDBG( descP,
- ("SOCKET", "nsetopt_lvl_sctp_initmsg -> extract attributes\r\n") );
+ ("SOCKET",
+ "esock_setopt_lvl_sctp_initmsg -> extract attributes\r\n") );
if (!GET_MAP_VAL(env, eVal, atom_num_outstreams, &eNumOut))
return esock_make_error(env, esock_atom_einval);
@@ -10669,7 +10711,8 @@ ERL_NIF_TERM nsetopt_lvl_sctp_initmsg(ErlNifEnv* env,
return esock_make_error(env, esock_atom_einval);
SSDBG( descP,
- ("SOCKET", "nsetopt_lvl_sctp_initmsg -> decode attributes\r\n") );
+ ("SOCKET",
+ "esock_setopt_lvl_sctp_initmsg -> decode attributes\r\n") );
if (!GET_UINT(env, eNumOut, &tmp))
return esock_make_error(env, esock_atom_einval);
@@ -10688,7 +10731,8 @@ ERL_NIF_TERM nsetopt_lvl_sctp_initmsg(ErlNifEnv* env,
initMsg.sinit_max_init_timeo = (Uint16) tmp;
SSDBG( descP,
- ("SOCKET", "nsetopt_lvl_sctp_initmsg -> set initmsg option\r\n") );
+ ("SOCKET",
+ "esock_setopt_lvl_sctp_initmsg -> set initmsg option\r\n") );
res = socket_setopt(descP->sock, IPPROTO_SCTP, SCTP_INITMSG,
&initMsg, sizeof(initMsg));
@@ -10699,7 +10743,7 @@ ERL_NIF_TERM nsetopt_lvl_sctp_initmsg(ErlNifEnv* env,
result = esock_atom_ok;
SSDBG( descP,
- ("SOCKET", "nsetopt_lvl_sctp_initmsg -> done with"
+ ("SOCKET", "esock_setopt_lvl_sctp_initmsg -> done with"
"\r\n result: %T"
"\r\n", result) );
@@ -10709,39 +10753,39 @@ ERL_NIF_TERM nsetopt_lvl_sctp_initmsg(ErlNifEnv* env,
#endif
-/* nsetopt_lvl_sctp_maxseg - Level SCTP MAXSEG option
+/* esock_setopt_lvl_sctp_maxseg - Level SCTP MAXSEG option
*/
#if defined(SCTP_MAXSEG)
static
-ERL_NIF_TERM nsetopt_lvl_sctp_maxseg(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_sctp_maxseg(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
- return nsetopt_int_opt(env, descP, IPPROTO_SCTP, SCTP_MAXSEG, eVal);
+ return esock_setopt_int_opt(env, descP, IPPROTO_SCTP, SCTP_MAXSEG, eVal);
}
#endif
-/* nsetopt_lvl_sctp_nodelay - Level SCTP NODELAY option
+/* esock_setopt_lvl_sctp_nodelay - Level SCTP NODELAY option
*/
#if defined(SCTP_NODELAY)
static
-ERL_NIF_TERM nsetopt_lvl_sctp_nodelay(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_sctp_nodelay(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
- return nsetopt_bool_opt(env, descP, IPPROTO_SCTP, SCTP_NODELAY, eVal);
+ return esock_setopt_bool_opt(env, descP, IPPROTO_SCTP, SCTP_NODELAY, eVal);
}
#endif
-/* nsetopt_lvl_sctp_rtoinfo - Level SCTP RTOINFO option
+/* esock_setopt_lvl_sctp_rtoinfo - Level SCTP RTOINFO option
*/
#if defined(SCTP_RTOINFO)
static
-ERL_NIF_TERM nsetopt_lvl_sctp_rtoinfo(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_lvl_sctp_rtoinfo(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM eVal)
{
ERL_NIF_TERM result;
ERL_NIF_TERM eAssocId, eInitial, eMax, eMin;
@@ -10750,7 +10794,7 @@ ERL_NIF_TERM nsetopt_lvl_sctp_rtoinfo(ErlNifEnv* env,
size_t sz;
SSDBG( descP,
- ("SOCKET", "nsetopt_lvl_sctp_rtoinfo -> entry with"
+ ("SOCKET", "esock_setopt_lvl_sctp_rtoinfo -> entry with"
"\r\n eVal: %T"
"\r\n", eVal) );
@@ -10763,7 +10807,8 @@ ERL_NIF_TERM nsetopt_lvl_sctp_rtoinfo(ErlNifEnv* env,
return esock_make_error(env, esock_atom_einval);
SSDBG( descP,
- ("SOCKET", "nsetopt_lvl_sctp_rtoinfo -> extract attributes\r\n") );
+ ("SOCKET",
+ "esock_setopt_lvl_sctp_rtoinfo -> extract attributes\r\n") );
if (!GET_MAP_VAL(env, eVal, atom_assoc_id, &eAssocId))
return esock_make_error(env, esock_atom_einval);
@@ -10778,7 +10823,8 @@ ERL_NIF_TERM nsetopt_lvl_sctp_rtoinfo(ErlNifEnv* env,
return esock_make_error(env, esock_atom_einval);
SSDBG( descP,
- ("SOCKET", "nsetopt_lvl_sctp_rtoinfo -> decode attributes\r\n") );
+ ("SOCKET",
+ "esock_setopt_lvl_sctp_rtoinfo -> decode attributes\r\n") );
/* On some platforms the assoc id is typed as an unsigned integer (uint32)
* So, to avoid warnings there, we always make an explicit cast...
@@ -10813,7 +10859,8 @@ ERL_NIF_TERM nsetopt_lvl_sctp_rtoinfo(ErlNifEnv* env,
return esock_make_error(env, esock_atom_einval);
SSDBG( descP,
- ("SOCKET", "nsetopt_lvl_sctp_rtoinfo -> set associnfo option\r\n") );
+ ("SOCKET",
+ "esock_setopt_lvl_sctp_rtoinfo -> set associnfo option\r\n") );
res = socket_setopt(descP->sock, IPPROTO_SCTP, SCTP_RTOINFO,
&rtoInfo, sizeof(rtoInfo));
@@ -10824,7 +10871,7 @@ ERL_NIF_TERM nsetopt_lvl_sctp_rtoinfo(ErlNifEnv* env,
result = esock_atom_ok;
SSDBG( descP,
- ("SOCKET", "nsetopt_lvl_sctp_rtoinfo -> done with"
+ ("SOCKET", "esock_setopt_lvl_sctp_rtoinfo -> done with"
"\r\n result: %T"
"\r\n", result) );
@@ -10840,14 +10887,14 @@ ERL_NIF_TERM nsetopt_lvl_sctp_rtoinfo(ErlNifEnv* env,
-/* nsetopt_bool_opt - set an option that has an (integer) bool value
+/* esock_setopt_bool_opt - set an option that has an (integer) bool value
*/
static
-ERL_NIF_TERM nsetopt_bool_opt(ErlNifEnv* env,
- ESockDescriptor* descP,
- int level,
- int opt,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_bool_opt(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int level,
+ int opt,
+ ERL_NIF_TERM eVal)
{
ERL_NIF_TERM result;
BOOLEAN_T val;
@@ -10867,14 +10914,14 @@ ERL_NIF_TERM nsetopt_bool_opt(ErlNifEnv* env,
}
-/* nsetopt_int_opt - set an option that has an integer value
+/* esock_setopt_int_opt - set an option that has an integer value
*/
static
-ERL_NIF_TERM nsetopt_int_opt(ErlNifEnv* env,
- ESockDescriptor* descP,
- int level,
- int opt,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_int_opt(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int level,
+ int opt,
+ ERL_NIF_TERM eVal)
{
ERL_NIF_TERM result;
int val;
@@ -10884,7 +10931,7 @@ ERL_NIF_TERM nsetopt_int_opt(ErlNifEnv* env,
/*
SSDBG( descP,
- ("SOCKET", "nsetopt_int_opt -> set option"
+ ("SOCKET", "esock_setopt_int_opt -> set option"
"\r\n opt: %d"
"\r\n val: %d"
"\r\n", opt, val) );
@@ -10905,16 +10952,16 @@ ERL_NIF_TERM nsetopt_int_opt(ErlNifEnv* env,
}
-/* nsetopt_str_opt - set an option that has an string value
+/* esock_setopt_str_opt - set an option that has an string value
*/
#if defined(USE_SETOPT_STR_OPT)
static
-ERL_NIF_TERM nsetopt_str_opt(ErlNifEnv* env,
- ESockDescriptor* descP,
- int level,
- int opt,
- int max,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_str_opt(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int level,
+ int opt,
+ int max,
+ ERL_NIF_TERM eVal)
{
ERL_NIF_TERM result;
char* val = MALLOC(max);
@@ -10939,14 +10986,14 @@ ERL_NIF_TERM nsetopt_str_opt(ErlNifEnv* env,
#endif
-/* nsetopt_timeval_opt - set an option that has an (timeval) bool value
+/* esock_setopt_timeval_opt - set an option that has an (timeval) bool value
*/
static
-ERL_NIF_TERM nsetopt_timeval_opt(ErlNifEnv* env,
- ESockDescriptor* descP,
- int level,
- int opt,
- ERL_NIF_TERM eVal)
+ERL_NIF_TERM esock_setopt_timeval_opt(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int level,
+ int opt,
+ ERL_NIF_TERM eVal)
{
ERL_NIF_TERM result;
struct timeval timeVal;
@@ -10954,7 +11001,7 @@ ERL_NIF_TERM nsetopt_timeval_opt(ErlNifEnv* env,
char* xres;
SSDBG( descP,
- ("SOCKET", "nsetopt_timeval_opt -> entry with"
+ ("SOCKET", "esock_setopt_timeval_opt -> entry with"
"\r\n eVal: %T"
"\r\n", eVal) );
@@ -10962,7 +11009,7 @@ ERL_NIF_TERM nsetopt_timeval_opt(ErlNifEnv* env,
return esock_make_error_str(env, xres);
SSDBG( descP,
- ("SOCKET", "nsetopt_timeval_opt -> set timeval option\r\n") );
+ ("SOCKET", "esock_setopt_timeval_opt -> set timeval option\r\n") );
res = socket_setopt(descP->sock, level, opt, &timeVal, sizeof(timeVal));
@@ -10972,7 +11019,7 @@ ERL_NIF_TERM nsetopt_timeval_opt(ErlNifEnv* env,
result = esock_atom_ok;
SSDBG( descP,
- ("SOCKET", "nsetopt_timeval_opt -> done with"
+ ("SOCKET", "esock_setopt_timeval_opt -> done with"
"\r\n result: %T"
"\r\n", result) );
@@ -10992,19 +11039,19 @@ BOOLEAN_T elevel2level(BOOLEAN_T isEncoded,
if (isEncoded) {
switch (eLevel) {
- case SOCKET_OPT_LEVEL_OTP:
+ case ESOCK_OPT_LEVEL_OTP:
*isOTP = TRUE;
*level = -1;
result = TRUE;
break;
- case SOCKET_OPT_LEVEL_SOCKET:
+ case ESOCK_OPT_LEVEL_SOCKET:
*isOTP = FALSE;
*level = SOL_SOCKET;
result = TRUE;
break;
- case SOCKET_OPT_LEVEL_IP:
+ case ESOCK_OPT_LEVEL_IP:
*isOTP = FALSE;
#if defined(SOL_IP)
*level = SOL_IP;
@@ -11015,7 +11062,7 @@ BOOLEAN_T elevel2level(BOOLEAN_T isEncoded,
break;
#if defined(HAVE_IPV6)
- case SOCKET_OPT_LEVEL_IPV6:
+ case ESOCK_OPT_LEVEL_IPV6:
*isOTP = FALSE;
#if defined(SOL_IPV6)
*level = SOL_IPV6;
@@ -11026,20 +11073,20 @@ BOOLEAN_T elevel2level(BOOLEAN_T isEncoded,
break;
#endif
- case SOCKET_OPT_LEVEL_TCP:
+ case ESOCK_OPT_LEVEL_TCP:
*isOTP = FALSE;
*level = IPPROTO_TCP;
result = TRUE;
break;
- case SOCKET_OPT_LEVEL_UDP:
+ case ESOCK_OPT_LEVEL_UDP:
*isOTP = FALSE;
*level = IPPROTO_UDP;
result = TRUE;
break;
#ifdef HAVE_SCTP
- case SOCKET_OPT_LEVEL_SCTP:
+ case ESOCK_OPT_LEVEL_SCTP:
*isOTP = FALSE;
*level = IPPROTO_SCTP;
result = TRUE;
@@ -11184,7 +11231,7 @@ ERL_NIF_TERM nif_getopt(ErlNifEnv* env,
SGDBG( ("SOCKET", "nif_getopt -> entry with argc: %d\r\n", argc) );
if ((argc != 4) ||
- !enif_get_resource(env, argv[0], sockets, (void**) &descP) ||
+ !ESOCK_GET_RESOURCE(env, argv[0], (void**) &descP) ||
!GET_INT(env, argv[2], &eLevel)) {
SGDBG( ("SOCKET", "nif_getopt -> failed processing args\r\n") );
return enif_make_badarg(env);
@@ -11210,7 +11257,7 @@ ERL_NIF_TERM nif_getopt(ErlNifEnv* env,
MLOCK(descP->cfgMtx);
- result = ngetopt(env, descP, isEncoded, isOTP, level, eOpt);
+ result = esock_getopt(env, descP, isEncoded, isOTP, level, eOpt);
MUNLOCK(descP->cfgMtx);
@@ -11223,18 +11270,18 @@ ERL_NIF_TERM nif_getopt(ErlNifEnv* env,
#if !defined(__WIN32__)
static
-ERL_NIF_TERM ngetopt(ErlNifEnv* env,
- ESockDescriptor* descP,
- BOOLEAN_T isEncoded,
- BOOLEAN_T isOTP,
- int level,
- ERL_NIF_TERM eOpt)
+ERL_NIF_TERM esock_getopt(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ BOOLEAN_T isEncoded,
+ BOOLEAN_T isOTP,
+ int level,
+ ERL_NIF_TERM eOpt)
{
ERL_NIF_TERM result;
int opt;
SSDBG( descP,
- ("SOCKET", "ngetopt -> entry with"
+ ("SOCKET", "esock_getopt -> entry with"
"\r\n isEncoded: %s"
"\r\n isOTP: %s"
"\r\n level: %d"
@@ -11246,20 +11293,20 @@ ERL_NIF_TERM ngetopt(ErlNifEnv* env,
* but options for our implementation.
*/
if (GET_INT(env, eOpt, &opt))
- result = ngetopt_otp(env, descP, opt);
+ result = esock_getopt_otp(env, descP, opt);
else
result = esock_make_error(env, esock_atom_einval);
} else if (!isEncoded) {
- result = ngetopt_native(env, descP, level, eOpt);
+ result = esock_getopt_native(env, descP, level, eOpt);
} else {
if (GET_INT(env, eOpt, &opt))
- result = ngetopt_level(env, descP, level, opt);
+ result = esock_getopt_level(env, descP, level, opt);
else
result = esock_make_error(env, esock_atom_einval);
}
SSDBG( descP,
- ("SOCKET", "ngetopt -> done when"
+ ("SOCKET", "esock_getopt -> done when"
"\r\n result: %T"
"\r\n", result) );
@@ -11268,60 +11315,60 @@ ERL_NIF_TERM ngetopt(ErlNifEnv* env,
-/* ngetopt_otp - Handle OTP (level) options
+/* esock_getopt_otp - Handle OTP (level) options
*/
static
-ERL_NIF_TERM ngetopt_otp(ErlNifEnv* env,
- ESockDescriptor* descP,
- int eOpt)
+ERL_NIF_TERM esock_getopt_otp(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int eOpt)
{
ERL_NIF_TERM result;
SSDBG( descP,
- ("SOCKET", "ngetopt_otp -> entry with"
+ ("SOCKET", "esock_getopt_otp -> entry with"
"\r\n eOpt: %d"
"\r\n", eOpt) );
switch (eOpt) {
- case SOCKET_OPT_OTP_DEBUG:
- result = ngetopt_otp_debug(env, descP);
+ case ESOCK_OPT_OTP_DEBUG:
+ result = esock_getopt_otp_debug(env, descP);
break;
- case SOCKET_OPT_OTP_IOW:
- result = ngetopt_otp_iow(env, descP);
+ case ESOCK_OPT_OTP_IOW:
+ result = esock_getopt_otp_iow(env, descP);
break;
- case SOCKET_OPT_OTP_CTRL_PROC:
- result = ngetopt_otp_ctrl_proc(env, descP);
+ case ESOCK_OPT_OTP_CTRL_PROC:
+ result = esock_getopt_otp_ctrl_proc(env, descP);
break;
- case SOCKET_OPT_OTP_RCVBUF:
- result = ngetopt_otp_rcvbuf(env, descP);
+ case ESOCK_OPT_OTP_RCVBUF:
+ result = esock_getopt_otp_rcvbuf(env, descP);
break;
- case SOCKET_OPT_OTP_RCVCTRLBUF:
- result = ngetopt_otp_rcvctrlbuf(env, descP);
+ case ESOCK_OPT_OTP_RCVCTRLBUF:
+ result = esock_getopt_otp_rcvctrlbuf(env, descP);
break;
- case SOCKET_OPT_OTP_SNDCTRLBUF:
- result = ngetopt_otp_sndctrlbuf(env, descP);
+ case ESOCK_OPT_OTP_SNDCTRLBUF:
+ result = esock_getopt_otp_sndctrlbuf(env, descP);
break;
- case SOCKET_OPT_OTP_FD:
- result = ngetopt_otp_fd(env, descP);
+ case ESOCK_OPT_OTP_FD:
+ result = esock_getopt_otp_fd(env, descP);
break;
/* *** INTERNAL *** */
- case SOCKET_OPT_OTP_DOMAIN:
- result = ngetopt_otp_domain(env, descP);
+ case ESOCK_OPT_OTP_DOMAIN:
+ result = esock_getopt_otp_domain(env, descP);
break;
- case SOCKET_OPT_OTP_TYPE:
- result = ngetopt_otp_type(env, descP);
+ case ESOCK_OPT_OTP_TYPE:
+ result = esock_getopt_otp_type(env, descP);
break;
- case SOCKET_OPT_OTP_PROTOCOL:
- result = ngetopt_otp_protocol(env, descP);
+ case ESOCK_OPT_OTP_PROTOCOL:
+ result = esock_getopt_otp_protocol(env, descP);
break;
default:
@@ -11330,7 +11377,7 @@ ERL_NIF_TERM ngetopt_otp(ErlNifEnv* env,
}
SSDBG( descP,
- ("SOCKET", "ngetopt_otp -> done when"
+ ("SOCKET", "esock_getopt_otp -> done when"
"\r\n result: %T"
"\r\n", result) );
@@ -11338,11 +11385,11 @@ ERL_NIF_TERM ngetopt_otp(ErlNifEnv* env,
}
-/* ngetopt_otp_debug - Handle the OTP (level) debug option
+/* esock_getopt_otp_debug - Handle the OTP (level) debug option
*/
static
-ERL_NIF_TERM ngetopt_otp_debug(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_otp_debug(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
ERL_NIF_TERM eVal = esock_encode_bool(descP->dbg);
@@ -11350,11 +11397,11 @@ ERL_NIF_TERM ngetopt_otp_debug(ErlNifEnv* env,
}
-/* ngetopt_otp_iow - Handle the OTP (level) iow option
+/* esock_getopt_otp_iow - Handle the OTP (level) iow option
*/
static
-ERL_NIF_TERM ngetopt_otp_iow(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_otp_iow(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
ERL_NIF_TERM eVal = esock_encode_bool(descP->iow);
@@ -11362,11 +11409,11 @@ ERL_NIF_TERM ngetopt_otp_iow(ErlNifEnv* env,
}
-/* ngetopt_otp_ctrl_proc - Handle the OTP (level) controlling_process option
+/* esock_getopt_otp_ctrl_proc - Handle the OTP (level) controlling_process option
*/
static
-ERL_NIF_TERM ngetopt_otp_ctrl_proc(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_otp_ctrl_proc(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
ERL_NIF_TERM eVal = MKPID(env, &descP->ctrlPid);
@@ -11375,11 +11422,11 @@ ERL_NIF_TERM ngetopt_otp_ctrl_proc(ErlNifEnv* env,
-/* ngetopt_otp_rcvbuf - Handle the OTP (level) rcvbuf option
+/* esock_getopt_otp_rcvbuf - Handle the OTP (level) rcvbuf option
*/
static
-ERL_NIF_TERM ngetopt_otp_rcvbuf(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_otp_rcvbuf(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
ERL_NIF_TERM eVal;
@@ -11393,11 +11440,11 @@ ERL_NIF_TERM ngetopt_otp_rcvbuf(ErlNifEnv* env,
}
-/* ngetopt_otp_rcvctrlbuf - Handle the OTP (level) rcvctrlbuf option
+/* esock_getopt_otp_rcvctrlbuf - Handle the OTP (level) rcvctrlbuf option
*/
static
-ERL_NIF_TERM ngetopt_otp_rcvctrlbuf(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_otp_rcvctrlbuf(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
ERL_NIF_TERM eVal = MKI(env, descP->rCtrlSz);
@@ -11405,11 +11452,11 @@ ERL_NIF_TERM ngetopt_otp_rcvctrlbuf(ErlNifEnv* env,
}
-/* ngetopt_otp_sndctrlbuf - Handle the OTP (level) sndctrlbuf option
+/* esock_getopt_otp_sndctrlbuf - Handle the OTP (level) sndctrlbuf option
*/
static
-ERL_NIF_TERM ngetopt_otp_sndctrlbuf(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_otp_sndctrlbuf(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
ERL_NIF_TERM eVal = MKI(env, descP->wCtrlSz);
@@ -11417,11 +11464,11 @@ ERL_NIF_TERM ngetopt_otp_sndctrlbuf(ErlNifEnv* env,
}
-/* ngetopt_otp_fd - Handle the OTP (level) fd option
+/* esock_getopt_otp_fd - Handle the OTP (level) fd option
*/
static
-ERL_NIF_TERM ngetopt_otp_fd(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_otp_fd(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
ERL_NIF_TERM eVal = MKI(env, descP->sock);
@@ -11429,11 +11476,11 @@ ERL_NIF_TERM ngetopt_otp_fd(ErlNifEnv* env,
}
-/* ngetopt_otp_domain - Handle the OTP (level) domain option
+/* esock_getopt_otp_domain - Handle the OTP (level) domain option
*/
static
-ERL_NIF_TERM ngetopt_otp_domain(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_otp_domain(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
ERL_NIF_TERM result, reason;
int val = descP->domain;
@@ -11465,11 +11512,11 @@ ERL_NIF_TERM ngetopt_otp_domain(ErlNifEnv* env,
}
-/* ngetopt_otp_type - Handle the OTP (level) type options.
+/* esock_getopt_otp_type - Handle the OTP (level) type options.
*/
static
-ERL_NIF_TERM ngetopt_otp_type(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_otp_type(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
ERL_NIF_TERM result, reason;
int val = descP->type;
@@ -11506,11 +11553,11 @@ ERL_NIF_TERM ngetopt_otp_type(ErlNifEnv* env,
}
-/* ngetopt_otp_protocol - Handle the OTP (level) protocol options.
+/* esock_getopt_otp_protocol - Handle the OTP (level) protocol options.
*/
static
-ERL_NIF_TERM ngetopt_otp_protocol(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_otp_protocol(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
ERL_NIF_TERM result, reason;
int val = descP->protocol;
@@ -11558,10 +11605,10 @@ ERL_NIF_TERM ngetopt_otp_protocol(ErlNifEnv* env,
* format: {NativeOpt :: integer(), ValueSize :: non_neg_integer()}
*/
static
-ERL_NIF_TERM ngetopt_native(ErlNifEnv* env,
- ESockDescriptor* descP,
- int level,
- ERL_NIF_TERM eOpt)
+ERL_NIF_TERM esock_getopt_native(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int level,
+ ERL_NIF_TERM eOpt)
{
ERL_NIF_TERM result = enif_make_badarg(env);
int opt;
@@ -11569,7 +11616,7 @@ ERL_NIF_TERM ngetopt_native(ErlNifEnv* env,
SOCKOPTLEN_T valueSz;
SSDBG( descP,
- ("SOCKET", "ngetopt_native -> entry with"
+ ("SOCKET", "esock_getopt_native -> entry with"
"\r\n level: %d"
"\r\n eOpt: %T"
"\r\n", level, eOpt) );
@@ -11583,20 +11630,21 @@ ERL_NIF_TERM ngetopt_native(ErlNifEnv* env,
if (decode_native_get_opt(env, eOpt, &opt, &valueType, (int*) &valueSz)) {
SSDBG( descP,
- ("SOCKET", "ngetopt_native -> decoded opt"
+ ("SOCKET", "esock_getopt_native -> decoded opt"
"\r\n valueType: %d (%s)"
"\r\n ValueSize: %d"
"\r\n", valueType, VT2S(valueType), valueSz) );
switch (valueType) {
- case SOCKET_OPT_VALUE_TYPE_UNSPEC:
- result = ngetopt_native_unspec(env, descP, level, opt, valueSz);
+ case ESOCK_OPT_VALUE_TYPE_UNSPEC:
+ result = esock_getopt_native_unspec(env, descP,
+ level, opt, valueSz);
break;
- case SOCKET_OPT_VALUE_TYPE_INT:
- result = ngetopt_int_opt(env, descP, level, opt);
+ case ESOCK_OPT_VALUE_TYPE_INT:
+ result = esock_getopt_int_opt(env, descP, level, opt);
break;
- case SOCKET_OPT_VALUE_TYPE_BOOL:
- result = ngetopt_bool_opt(env, descP, level, opt);
+ case ESOCK_OPT_VALUE_TYPE_BOOL:
+ result = esock_getopt_bool_opt(env, descP, level, opt);
break;
default:
result = esock_make_error(env, esock_atom_einval);
@@ -11607,7 +11655,7 @@ ERL_NIF_TERM ngetopt_native(ErlNifEnv* env,
}
SSDBG( descP,
- ("SOCKET", "ngetopt_native -> done when"
+ ("SOCKET", "esock_getopt_native -> done when"
"\r\n result: %T"
"\r\n", result) );
@@ -11616,17 +11664,17 @@ ERL_NIF_TERM ngetopt_native(ErlNifEnv* env,
static
-ERL_NIF_TERM ngetopt_native_unspec(ErlNifEnv* env,
- ESockDescriptor* descP,
- int level,
- int opt,
- SOCKOPTLEN_T valueSz)
+ERL_NIF_TERM esock_getopt_native_unspec(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int level,
+ int opt,
+ SOCKOPTLEN_T valueSz)
{
ERL_NIF_TERM result = esock_make_error(env, esock_atom_einval);
int res;
SSDBG( descP,
- ("SOCKET", "ngetopt_native_unspec -> entry with"
+ ("SOCKET", "esock_getopt_native_unspec -> entry with"
"\r\n level: %d"
"\r\n opt: %d"
"\r\n valueSz: %d"
@@ -11642,7 +11690,8 @@ ERL_NIF_TERM ngetopt_native_unspec(ErlNifEnv* env,
SOCKOPTLEN_T vsz = valueSz;
ErlNifBinary val;
- SSDBG( descP, ("SOCKET", "ngetopt_native_unspec -> try alloc buffer\r\n") );
+ SSDBG( descP, ("SOCKET",
+ "esock_getopt_native_unspec -> try alloc buffer\r\n") );
if (ALLOC_BIN(vsz, &val)) {
int saveErrno;
@@ -11674,7 +11723,7 @@ ERL_NIF_TERM ngetopt_native_unspec(ErlNifEnv* env,
}
SSDBG( descP,
- ("SOCKET", "ngetopt_native_unspec -> done when"
+ ("SOCKET", "esock_getopt_native_unspec -> done when"
"\r\n result: %T"
"\r\n", result) );
@@ -11683,25 +11732,25 @@ ERL_NIF_TERM ngetopt_native_unspec(ErlNifEnv* env,
-/* ngetopt_level - A "proper" level (option) has been specified
+/* esock_getopt_level - A "proper" level (option) has been specified
*/
static
-ERL_NIF_TERM ngetopt_level(ErlNifEnv* env,
- ESockDescriptor* descP,
- int level,
- int eOpt)
+ERL_NIF_TERM esock_getopt_level(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int level,
+ int eOpt)
{
ERL_NIF_TERM result;
SSDBG( descP,
- ("SOCKET", "ngetopt_level -> entry with"
+ ("SOCKET", "esock_getopt_level -> entry with"
"\r\n level: %d"
"\r\n eOpt: %d"
"\r\n", level, eOpt) );
switch (level) {
case SOL_SOCKET:
- result = ngetopt_lvl_socket(env, descP, eOpt);
+ result = esock_getopt_lvl_socket(env, descP, eOpt);
break;
#if defined(SOL_IP)
@@ -11709,7 +11758,7 @@ ERL_NIF_TERM ngetopt_level(ErlNifEnv* env,
#else
case IPPROTO_IP:
#endif
- result = ngetopt_lvl_ip(env, descP, eOpt);
+ result = esock_getopt_lvl_ip(env, descP, eOpt);
break;
#if defined(HAVE_IPV6)
@@ -11718,21 +11767,21 @@ ERL_NIF_TERM ngetopt_level(ErlNifEnv* env,
#else
case IPPROTO_IPV6:
#endif
- result = ngetopt_lvl_ipv6(env, descP, eOpt);
+ result = esock_getopt_lvl_ipv6(env, descP, eOpt);
break;
#endif
case IPPROTO_TCP:
- result = ngetopt_lvl_tcp(env, descP, eOpt);
+ result = esock_getopt_lvl_tcp(env, descP, eOpt);
break;
case IPPROTO_UDP:
- result = ngetopt_lvl_udp(env, descP, eOpt);
+ result = esock_getopt_lvl_udp(env, descP, eOpt);
break;
#if defined(HAVE_SCTP)
case IPPROTO_SCTP:
- result = ngetopt_lvl_sctp(env, descP, eOpt);
+ result = esock_getopt_lvl_sctp(env, descP, eOpt);
break;
#endif
@@ -11742,7 +11791,7 @@ ERL_NIF_TERM ngetopt_level(ErlNifEnv* env,
}
SSDBG( descP,
- ("SOCKET", "ngetopt_level -> done when"
+ ("SOCKET", "esock_getopt_level -> done when"
"\r\n result: %T"
"\r\n", result) );
@@ -11750,150 +11799,150 @@ ERL_NIF_TERM ngetopt_level(ErlNifEnv* env,
}
-/* ngetopt_lvl_socket - Level *SOCKET* option
+/* esock_getopt_lvl_socket - Level *SOCKET* option
*/
static
-ERL_NIF_TERM ngetopt_lvl_socket(ErlNifEnv* env,
- ESockDescriptor* descP,
- int eOpt)
+ERL_NIF_TERM esock_getopt_lvl_socket(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int eOpt)
{
ERL_NIF_TERM result;
SSDBG( descP,
- ("SOCKET", "ngetopt_lvl_socket -> entry with"
+ ("SOCKET", "esock_getopt_lvl_socket -> entry with"
"\r\n eOpt: %d"
"\r\n", eOpt) );
switch (eOpt) {
#if defined(SO_ACCEPTCONN)
- case SOCKET_OPT_SOCK_ACCEPTCONN:
- result = ngetopt_lvl_sock_acceptconn(env, descP);
+ case ESOCK_OPT_SOCK_ACCEPTCONN:
+ result = esock_getopt_lvl_sock_acceptconn(env, descP);
break;
#endif
#if defined(SO_BINDTODEVICE)
- case SOCKET_OPT_SOCK_BINDTODEVICE:
- result = ngetopt_lvl_sock_bindtodevice(env, descP);
+ case ESOCK_OPT_SOCK_BINDTODEVICE:
+ result = esock_getopt_lvl_sock_bindtodevice(env, descP);
break;
#endif
#if defined(SO_BROADCAST)
- case SOCKET_OPT_SOCK_BROADCAST:
- result = ngetopt_lvl_sock_broadcast(env, descP);
+ case ESOCK_OPT_SOCK_BROADCAST:
+ result = esock_getopt_lvl_sock_broadcast(env, descP);
break;
#endif
#if defined(SO_DEBUG)
- case SOCKET_OPT_SOCK_DEBUG:
- result = ngetopt_lvl_sock_debug(env, descP);
+ case ESOCK_OPT_SOCK_DEBUG:
+ result = esock_getopt_lvl_sock_debug(env, descP);
break;
#endif
#if defined(SO_DOMAIN)
- case SOCKET_OPT_SOCK_DOMAIN:
- result = ngetopt_lvl_sock_domain(env, descP);
+ case ESOCK_OPT_SOCK_DOMAIN:
+ result = esock_getopt_lvl_sock_domain(env, descP);
break;
#endif
#if defined(SO_DONTROUTE)
- case SOCKET_OPT_SOCK_DONTROUTE:
- result = ngetopt_lvl_sock_dontroute(env, descP);
+ case ESOCK_OPT_SOCK_DONTROUTE:
+ result = esock_getopt_lvl_sock_dontroute(env, descP);
break;
#endif
#if defined(SO_KEEPALIVE)
- case SOCKET_OPT_SOCK_KEEPALIVE:
- result = ngetopt_lvl_sock_keepalive(env, descP);
+ case ESOCK_OPT_SOCK_KEEPALIVE:
+ result = esock_getopt_lvl_sock_keepalive(env, descP);
break;
#endif
#if defined(SO_LINGER)
- case SOCKET_OPT_SOCK_LINGER:
- result = ngetopt_lvl_sock_linger(env, descP);
+ case ESOCK_OPT_SOCK_LINGER:
+ result = esock_getopt_lvl_sock_linger(env, descP);
break;
#endif
#if defined(SO_OOBINLINE)
- case SOCKET_OPT_SOCK_OOBINLINE:
- result = ngetopt_lvl_sock_oobinline(env, descP);
+ case ESOCK_OPT_SOCK_OOBINLINE:
+ result = esock_getopt_lvl_sock_oobinline(env, descP);
break;
#endif
#if defined(SO_PEEK_OFF)
- case SOCKET_OPT_SOCK_PEEK_OFF:
- result = ngetopt_lvl_sock_peek_off(env, descP);
+ case ESOCK_OPT_SOCK_PEEK_OFF:
+ result = esock_getopt_lvl_sock_peek_off(env, descP);
break;
#endif
#if defined(SO_PRIORITY)
- case SOCKET_OPT_SOCK_PRIORITY:
- result = ngetopt_lvl_sock_priority(env, descP);
+ case ESOCK_OPT_SOCK_PRIORITY:
+ result = esock_getopt_lvl_sock_priority(env, descP);
break;
#endif
#if defined(SO_PROTOCOL)
- case SOCKET_OPT_SOCK_PROTOCOL:
- result = ngetopt_lvl_sock_protocol(env, descP);
+ case ESOCK_OPT_SOCK_PROTOCOL:
+ result = esock_getopt_lvl_sock_protocol(env, descP);
break;
#endif
#if defined(SO_RCVBUF)
- case SOCKET_OPT_SOCK_RCVBUF:
- result = ngetopt_lvl_sock_rcvbuf(env, descP);
+ case ESOCK_OPT_SOCK_RCVBUF:
+ result = esock_getopt_lvl_sock_rcvbuf(env, descP);
break;
#endif
#if defined(SO_RCVLOWAT)
- case SOCKET_OPT_SOCK_RCVLOWAT:
- result = ngetopt_lvl_sock_rcvlowat(env, descP);
+ case ESOCK_OPT_SOCK_RCVLOWAT:
+ result = esock_getopt_lvl_sock_rcvlowat(env, descP);
break;
#endif
#if defined(SO_RCVTIMEO)
- case SOCKET_OPT_SOCK_RCVTIMEO:
- result = ngetopt_lvl_sock_rcvtimeo(env, descP);
+ case ESOCK_OPT_SOCK_RCVTIMEO:
+ result = esock_getopt_lvl_sock_rcvtimeo(env, descP);
break;
#endif
#if defined(SO_REUSEADDR)
- case SOCKET_OPT_SOCK_REUSEADDR:
- result = ngetopt_lvl_sock_reuseaddr(env, descP);
+ case ESOCK_OPT_SOCK_REUSEADDR:
+ result = esock_getopt_lvl_sock_reuseaddr(env, descP);
break;
#endif
#if defined(SO_REUSEPORT)
- case SOCKET_OPT_SOCK_REUSEPORT:
- result = ngetopt_lvl_sock_reuseport(env, descP);
+ case ESOCK_OPT_SOCK_REUSEPORT:
+ result = esock_getopt_lvl_sock_reuseport(env, descP);
break;
#endif
#if defined(SO_SNDBUF)
- case SOCKET_OPT_SOCK_SNDBUF:
- result = ngetopt_lvl_sock_sndbuf(env, descP);
+ case ESOCK_OPT_SOCK_SNDBUF:
+ result = esock_getopt_lvl_sock_sndbuf(env, descP);
break;
#endif
#if defined(SO_SNDLOWAT)
- case SOCKET_OPT_SOCK_SNDLOWAT:
- result = ngetopt_lvl_sock_sndlowat(env, descP);
+ case ESOCK_OPT_SOCK_SNDLOWAT:
+ result = esock_getopt_lvl_sock_sndlowat(env, descP);
break;
#endif
#if defined(SO_SNDTIMEO)
- case SOCKET_OPT_SOCK_SNDTIMEO:
- result = ngetopt_lvl_sock_sndtimeo(env, descP);
+ case ESOCK_OPT_SOCK_SNDTIMEO:
+ result = esock_getopt_lvl_sock_sndtimeo(env, descP);
break;
#endif
#if defined(SO_TIMESTAMP)
- case SOCKET_OPT_SOCK_TIMESTAMP:
- result = ngetopt_lvl_sock_timestamp(env, descP);
+ case ESOCK_OPT_SOCK_TIMESTAMP:
+ result = esock_getopt_lvl_sock_timestamp(env, descP);
break;
#endif
#if defined(SO_TYPE)
- case SOCKET_OPT_SOCK_TYPE:
- result = ngetopt_lvl_sock_type(env, descP);
+ case ESOCK_OPT_SOCK_TYPE:
+ result = esock_getopt_lvl_sock_type(env, descP);
break;
#endif
@@ -11903,7 +11952,7 @@ ERL_NIF_TERM ngetopt_lvl_socket(ErlNifEnv* env,
}
SSDBG( descP,
- ("SOCKET", "ngetopt_lvl_socket -> done when"
+ ("SOCKET", "esock_getopt_lvl_socket -> done when"
"\r\n result: %T"
"\r\n", result) );
@@ -11913,51 +11962,52 @@ ERL_NIF_TERM ngetopt_lvl_socket(ErlNifEnv* env,
#if defined(SO_ACCEPTCONN)
static
-ERL_NIF_TERM ngetopt_lvl_sock_acceptconn(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_sock_acceptconn(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
- return ngetopt_bool_opt(env, descP, SOL_SOCKET, SO_ACCEPTCONN);
+ return esock_getopt_bool_opt(env, descP, SOL_SOCKET, SO_ACCEPTCONN);
}
#endif
#if defined(SO_BINDTODEVICE)
static
-ERL_NIF_TERM ngetopt_lvl_sock_bindtodevice(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_sock_bindtodevice(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
SSDBG( descP,
- ("SOCKET", "ngetopt_lvl_sock_bindtodevice -> entry with\r\n") );
+ ("SOCKET", "esock_getopt_lvl_sock_bindtodevice -> entry with\r\n") );
- return ngetopt_str_opt(env, descP, SOL_SOCKET, SO_BROADCAST, IFNAMSIZ+1);
+ return esock_getopt_str_opt(env, descP,
+ SOL_SOCKET, SO_BINDTODEVICE, IFNAMSIZ+1);
}
#endif
#if defined(SO_BROADCAST)
static
-ERL_NIF_TERM ngetopt_lvl_sock_broadcast(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_sock_broadcast(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
- return ngetopt_bool_opt(env, descP, SOL_SOCKET, SO_BROADCAST);
+ return esock_getopt_bool_opt(env, descP, SOL_SOCKET, SO_BROADCAST);
}
#endif
#if defined(SO_DEBUG)
static
-ERL_NIF_TERM ngetopt_lvl_sock_debug(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_sock_debug(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
- return ngetopt_int_opt(env, descP, SOL_SOCKET, SO_DEBUG);
+ return esock_getopt_int_opt(env, descP, SOL_SOCKET, SO_DEBUG);
}
#endif
#if defined(SO_DOMAIN)
static
-ERL_NIF_TERM ngetopt_lvl_sock_domain(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_sock_domain(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
ERL_NIF_TERM result, reason;
int val;
@@ -12001,28 +12051,28 @@ ERL_NIF_TERM ngetopt_lvl_sock_domain(ErlNifEnv* env,
#if defined(SO_DONTROUTE)
static
-ERL_NIF_TERM ngetopt_lvl_sock_dontroute(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_sock_dontroute(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
- return ngetopt_bool_opt(env, descP, SOL_SOCKET, SO_DONTROUTE);
+ return esock_getopt_bool_opt(env, descP, SOL_SOCKET, SO_DONTROUTE);
}
#endif
#if defined(SO_KEEPALIVE)
static
-ERL_NIF_TERM ngetopt_lvl_sock_keepalive(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_sock_keepalive(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
- return ngetopt_bool_opt(env, descP, SOL_SOCKET, SO_KEEPALIVE);
+ return esock_getopt_bool_opt(env, descP, SOL_SOCKET, SO_KEEPALIVE);
}
#endif
#if defined(SO_LINGER)
static
-ERL_NIF_TERM ngetopt_lvl_sock_linger(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_sock_linger(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
ERL_NIF_TERM result;
struct linger val;
@@ -12051,38 +12101,38 @@ ERL_NIF_TERM ngetopt_lvl_sock_linger(ErlNifEnv* env,
#if defined(SO_OOBINLINE)
static
-ERL_NIF_TERM ngetopt_lvl_sock_oobinline(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_sock_oobinline(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
- return ngetopt_bool_opt(env, descP, SOL_SOCKET, SO_OOBINLINE);
+ return esock_getopt_bool_opt(env, descP, SOL_SOCKET, SO_OOBINLINE);
}
#endif
#if defined(SO_PEEK_OFF)
static
-ERL_NIF_TERM ngetopt_lvl_sock_peek_off(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_sock_peek_off(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
- return ngetopt_int_opt(env, descP, SOL_SOCKET, SO_PEEK_OFF);
+ return esock_getopt_int_opt(env, descP, SOL_SOCKET, SO_PEEK_OFF);
}
#endif
#if defined(SO_PRIORITY)
static
-ERL_NIF_TERM ngetopt_lvl_sock_priority(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_sock_priority(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
- return ngetopt_int_opt(env, descP, SOL_SOCKET, SO_PRIORITY);
+ return esock_getopt_int_opt(env, descP, SOL_SOCKET, SO_PRIORITY);
}
#endif
#if defined(SO_PROTOCOL)
static
-ERL_NIF_TERM ngetopt_lvl_sock_protocol(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_sock_protocol(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
ERL_NIF_TERM result, reason;
int val;
@@ -12135,98 +12185,98 @@ ERL_NIF_TERM ngetopt_lvl_sock_protocol(ErlNifEnv* env,
#if defined(SO_RCVBUF)
static
-ERL_NIF_TERM ngetopt_lvl_sock_rcvbuf(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_sock_rcvbuf(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
- return ngetopt_int_opt(env, descP, SOL_SOCKET, SO_RCVBUF);
+ return esock_getopt_int_opt(env, descP, SOL_SOCKET, SO_RCVBUF);
}
#endif
#if defined(SO_RCVLOWAT)
static
-ERL_NIF_TERM ngetopt_lvl_sock_rcvlowat(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_sock_rcvlowat(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
- return ngetopt_int_opt(env, descP, SOL_SOCKET, SO_RCVLOWAT);
+ return esock_getopt_int_opt(env, descP, SOL_SOCKET, SO_RCVLOWAT);
}
#endif
#if defined(SO_RCVTIMEO)
static
-ERL_NIF_TERM ngetopt_lvl_sock_rcvtimeo(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_sock_rcvtimeo(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
- return ngetopt_timeval_opt(env, descP, SOL_SOCKET, SO_RCVTIMEO);
+ return esock_getopt_timeval_opt(env, descP, SOL_SOCKET, SO_RCVTIMEO);
}
#endif
#if defined(SO_REUSEADDR)
static
-ERL_NIF_TERM ngetopt_lvl_sock_reuseaddr(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_sock_reuseaddr(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
- return ngetopt_bool_opt(env, descP, SOL_SOCKET, SO_REUSEADDR);
+ return esock_getopt_bool_opt(env, descP, SOL_SOCKET, SO_REUSEADDR);
}
#endif
#if defined(SO_REUSEPORT)
static
-ERL_NIF_TERM ngetopt_lvl_sock_reuseport(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_sock_reuseport(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
- return ngetopt_bool_opt(env, descP, SOL_SOCKET, SO_REUSEPORT);
+ return esock_getopt_bool_opt(env, descP, SOL_SOCKET, SO_REUSEPORT);
}
#endif
#if defined(SO_SNDBUF)
static
-ERL_NIF_TERM ngetopt_lvl_sock_sndbuf(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_sock_sndbuf(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
- return ngetopt_int_opt(env, descP, SOL_SOCKET, SO_SNDBUF);
+ return esock_getopt_int_opt(env, descP, SOL_SOCKET, SO_SNDBUF);
}
#endif
#if defined(SO_SNDLOWAT)
static
-ERL_NIF_TERM ngetopt_lvl_sock_sndlowat(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_sock_sndlowat(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
- return ngetopt_int_opt(env, descP, SOL_SOCKET, SO_SNDLOWAT);
+ return esock_getopt_int_opt(env, descP, SOL_SOCKET, SO_SNDLOWAT);
}
#endif
#if defined(SO_SNDTIMEO)
static
-ERL_NIF_TERM ngetopt_lvl_sock_sndtimeo(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_sock_sndtimeo(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
- return ngetopt_timeval_opt(env, descP, SOL_SOCKET, SO_SNDTIMEO);
+ return esock_getopt_timeval_opt(env, descP, SOL_SOCKET, SO_SNDTIMEO);
}
#endif
#if defined(SO_TIMESTAMP)
static
-ERL_NIF_TERM ngetopt_lvl_sock_timestamp(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_sock_timestamp(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
- return ngetopt_bool_opt(env, descP, SOL_SOCKET, SO_TIMESTAMP);
+ return esock_getopt_bool_opt(env, descP, SOL_SOCKET, SO_TIMESTAMP);
}
#endif
#if defined(SO_TYPE)
static
-ERL_NIF_TERM ngetopt_lvl_sock_type(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_sock_type(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
ERL_NIF_TERM result, reason;
int val;
@@ -12268,174 +12318,174 @@ ERL_NIF_TERM ngetopt_lvl_sock_type(ErlNifEnv* env,
#endif
-/* ngetopt_lvl_ip - Level *IP* option(s)
+/* esock_getopt_lvl_ip - Level *IP* option(s)
*/
static
-ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env,
- ESockDescriptor* descP,
- int eOpt)
+ERL_NIF_TERM esock_getopt_lvl_ip(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int eOpt)
{
ERL_NIF_TERM result;
SSDBG( descP,
- ("SOCKET", "ngetopt_lvl_ip -> entry with"
+ ("SOCKET", "esock_getopt_lvl_ip -> entry with"
"\r\n eOpt: %d"
"\r\n", eOpt) );
switch (eOpt) {
#if defined(IP_FREEBIND)
- case SOCKET_OPT_IP_FREEBIND:
- result = ngetopt_lvl_ip_freebind(env, descP);
+ case ESOCK_OPT_IP_FREEBIND:
+ result = esock_getopt_lvl_ip_freebind(env, descP);
break;
#endif
#if defined(IP_HDRINCL)
- case SOCKET_OPT_IP_HDRINCL:
- result = ngetopt_lvl_ip_hdrincl(env, descP);
+ case ESOCK_OPT_IP_HDRINCL:
+ result = esock_getopt_lvl_ip_hdrincl(env, descP);
break;
#endif
#if defined(IP_MINTTL)
- case SOCKET_OPT_IP_MINTTL:
- result = ngetopt_lvl_ip_minttl(env, descP);
+ case ESOCK_OPT_IP_MINTTL:
+ result = esock_getopt_lvl_ip_minttl(env, descP);
break;
#endif
#if defined(IP_MTU)
- case SOCKET_OPT_IP_MTU:
- result = ngetopt_lvl_ip_mtu(env, descP);
+ case ESOCK_OPT_IP_MTU:
+ result = esock_getopt_lvl_ip_mtu(env, descP);
break;
#endif
#if defined(IP_MTU_DISCOVER)
- case SOCKET_OPT_IP_MTU_DISCOVER:
- result = ngetopt_lvl_ip_mtu_discover(env, descP);
+ case ESOCK_OPT_IP_MTU_DISCOVER:
+ result = esock_getopt_lvl_ip_mtu_discover(env, descP);
break;
#endif
#if defined(IP_MULTICAST_ALL)
- case SOCKET_OPT_IP_MULTICAST_ALL:
- result = ngetopt_lvl_ip_multicast_all(env, descP);
+ case ESOCK_OPT_IP_MULTICAST_ALL:
+ result = esock_getopt_lvl_ip_multicast_all(env, descP);
break;
#endif
#if defined(IP_MULTICAST_IF)
- case SOCKET_OPT_IP_MULTICAST_IF:
- result = ngetopt_lvl_ip_multicast_if(env, descP);
+ case ESOCK_OPT_IP_MULTICAST_IF:
+ result = esock_getopt_lvl_ip_multicast_if(env, descP);
break;
#endif
#if defined(IP_MULTICAST_LOOP)
- case SOCKET_OPT_IP_MULTICAST_LOOP:
- result = ngetopt_lvl_ip_multicast_loop(env, descP);
+ case ESOCK_OPT_IP_MULTICAST_LOOP:
+ result = esock_getopt_lvl_ip_multicast_loop(env, descP);
break;
#endif
#if defined(IP_MULTICAST_TTL)
- case SOCKET_OPT_IP_MULTICAST_TTL:
- result = ngetopt_lvl_ip_multicast_ttl(env, descP);
+ case ESOCK_OPT_IP_MULTICAST_TTL:
+ result = esock_getopt_lvl_ip_multicast_ttl(env, descP);
break;
#endif
#if defined(IP_NODEFRAG)
- case SOCKET_OPT_IP_NODEFRAG:
- result = ngetopt_lvl_ip_nodefrag(env, descP);
+ case ESOCK_OPT_IP_NODEFRAG:
+ result = esock_getopt_lvl_ip_nodefrag(env, descP);
break;
#endif
#if defined(IP_PKTINFO)
- case SOCKET_OPT_IP_PKTINFO:
- result = ngetopt_lvl_ip_pktinfo(env, descP);
+ case ESOCK_OPT_IP_PKTINFO:
+ result = esock_getopt_lvl_ip_pktinfo(env, descP);
break;
#endif
#if defined(IP_RECVDSTADDR)
- case SOCKET_OPT_IP_RECVDSTADDR:
- result = ngetopt_lvl_ip_recvdstaddr(env, descP);
+ case ESOCK_OPT_IP_RECVDSTADDR:
+ result = esock_getopt_lvl_ip_recvdstaddr(env, descP);
break;
#endif
#if defined(IP_RECVERR)
- case SOCKET_OPT_IP_RECVERR:
- result = ngetopt_lvl_ip_recverr(env, descP);
+ case ESOCK_OPT_IP_RECVERR:
+ result = esock_getopt_lvl_ip_recverr(env, descP);
break;
#endif
#if defined(IP_RECVIF)
- case SOCKET_OPT_IP_RECVIF:
- result = ngetopt_lvl_ip_recvif(env, descP);
+ case ESOCK_OPT_IP_RECVIF:
+ result = esock_getopt_lvl_ip_recvif(env, descP);
break;
#endif
#if defined(IP_RECVOPTS)
- case SOCKET_OPT_IP_RECVOPTS:
- result = ngetopt_lvl_ip_recvopts(env, descP);
+ case ESOCK_OPT_IP_RECVOPTS:
+ result = esock_getopt_lvl_ip_recvopts(env, descP);
break;
#endif
#if defined(IP_RECVORIGDSTADDR)
- case SOCKET_OPT_IP_RECVORIGDSTADDR:
- result = ngetopt_lvl_ip_recvorigdstaddr(env, descP);
+ case ESOCK_OPT_IP_RECVORIGDSTADDR:
+ result = esock_getopt_lvl_ip_recvorigdstaddr(env, descP);
break;
#endif
#if defined(IP_RECVTOS)
- case SOCKET_OPT_IP_RECVTOS:
- result = ngetopt_lvl_ip_recvtos(env, descP);
+ case ESOCK_OPT_IP_RECVTOS:
+ result = esock_getopt_lvl_ip_recvtos(env, descP);
break;
#endif
#if defined(IP_RECVTTL)
- case SOCKET_OPT_IP_RECVTTL:
- result = ngetopt_lvl_ip_recvttl(env, descP);
+ case ESOCK_OPT_IP_RECVTTL:
+ result = esock_getopt_lvl_ip_recvttl(env, descP);
break;
#endif
#if defined(IP_RETOPTS)
- case SOCKET_OPT_IP_RETOPTS:
- result = ngetopt_lvl_ip_retopts(env, descP);
+ case ESOCK_OPT_IP_RETOPTS:
+ result = esock_getopt_lvl_ip_retopts(env, descP);
break;
#endif
#if defined(IP_ROUTER_ALERT)
- case SOCKET_OPT_IP_ROUTER_ALERT:
- result = ngetopt_lvl_ip_router_alert(env, descP);
+ case ESOCK_OPT_IP_ROUTER_ALERT:
+ result = esock_getopt_lvl_ip_router_alert(env, descP);
break;
#endif
#if defined(IP_SENDSRCADDR)
- case SOCKET_OPT_IP_SENDSRCADDR:
- result = ngetopt_lvl_ip_sendsrcaddr(env, descP);
+ case ESOCK_OPT_IP_SENDSRCADDR:
+ result = esock_getopt_lvl_ip_sendsrcaddr(env, descP);
break;
#endif
#if defined(IP_TOS)
- case SOCKET_OPT_IP_TOS:
- result = ngetopt_lvl_ip_tos(env, descP);
+ case ESOCK_OPT_IP_TOS:
+ result = esock_getopt_lvl_ip_tos(env, descP);
break;
#endif
#if defined(IP_TRANSPARENT)
- case SOCKET_OPT_IP_TRANSPARENT:
- result = ngetopt_lvl_ip_transparent(env, descP);
+ case ESOCK_OPT_IP_TRANSPARENT:
+ result = esock_getopt_lvl_ip_transparent(env, descP);
break;
#endif
#if defined(IP_TTL)
- case SOCKET_OPT_IP_TTL:
- result = ngetopt_lvl_ip_ttl(env, descP);
+ case ESOCK_OPT_IP_TTL:
+ result = esock_getopt_lvl_ip_ttl(env, descP);
break;
#endif
default:
SSDBG( descP,
- ("SOCKET", "ngetopt_lvl_ip -> unknown opt %d\r\n", eOpt) );
+ ("SOCKET", "esock_getopt_lvl_ip -> unknown opt %d\r\n", eOpt) );
result = esock_make_error(env, esock_atom_einval);
break;
}
SSDBG( descP,
- ("SOCKET", "ngetopt_lvl_ip -> done when"
+ ("SOCKET", "esock_getopt_lvl_ip -> done when"
"\r\n result: %T"
"\r\n", result) );
@@ -12443,12 +12493,12 @@ ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env,
}
-/* ngetopt_lvl_ip_minttl - Level IP MINTTL option
+/* esock_getopt_lvl_ip_minttl - Level IP MINTTL option
*/
#if defined(IP_MINTTL)
static
-ERL_NIF_TERM ngetopt_lvl_ip_minttl(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_ip_minttl(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
#if defined(SOL_IP)
int level = SOL_IP;
@@ -12456,17 +12506,17 @@ ERL_NIF_TERM ngetopt_lvl_ip_minttl(ErlNifEnv* env,
int level = IPPROTO_IP;
#endif
- return ngetopt_int_opt(env, descP, level, IP_MINTTL);
+ return esock_getopt_int_opt(env, descP, level, IP_MINTTL);
}
#endif
-/* ngetopt_lvl_ip_freebind - Level IP FREEBIND option
+/* esock_getopt_lvl_ip_freebind - Level IP FREEBIND option
*/
#if defined(IP_FREEBIND)
static
-ERL_NIF_TERM ngetopt_lvl_ip_freebind(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_ip_freebind(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
#if defined(SOL_IP)
int level = SOL_IP;
@@ -12474,17 +12524,17 @@ ERL_NIF_TERM ngetopt_lvl_ip_freebind(ErlNifEnv* env,
int level = IPPROTO_IP;
#endif
- return ngetopt_bool_opt(env, descP, level, IP_FREEBIND);
+ return esock_getopt_bool_opt(env, descP, level, IP_FREEBIND);
}
#endif
-/* ngetopt_lvl_ip_hdrincl - Level IP HDRINCL option
+/* esock_getopt_lvl_ip_hdrincl - Level IP HDRINCL option
*/
#if defined(IP_HDRINCL)
static
-ERL_NIF_TERM ngetopt_lvl_ip_hdrincl(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_ip_hdrincl(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
#if defined(SOL_IP)
int level = SOL_IP;
@@ -12492,17 +12542,17 @@ ERL_NIF_TERM ngetopt_lvl_ip_hdrincl(ErlNifEnv* env,
int level = IPPROTO_IP;
#endif
- return ngetopt_bool_opt(env, descP, level, IP_HDRINCL);
+ return esock_getopt_bool_opt(env, descP, level, IP_HDRINCL);
}
#endif
-/* ngetopt_lvl_ip_mtu - Level IP MTU option
+/* esock_getopt_lvl_ip_mtu - Level IP MTU option
*/
#if defined(IP_MTU)
static
-ERL_NIF_TERM ngetopt_lvl_ip_mtu(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_ip_mtu(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
#if defined(SOL_IP)
int level = SOL_IP;
@@ -12510,17 +12560,17 @@ ERL_NIF_TERM ngetopt_lvl_ip_mtu(ErlNifEnv* env,
int level = IPPROTO_IP;
#endif
- return ngetopt_int_opt(env, descP, level, IP_MTU);
+ return esock_getopt_int_opt(env, descP, level, IP_MTU);
}
#endif
-/* ngetopt_lvl_ip_mtu_discover - Level IP MTU_DISCOVER option
+/* esock_getopt_lvl_ip_mtu_discover - Level IP MTU_DISCOVER option
*/
#if defined(IP_MTU_DISCOVER)
static
-ERL_NIF_TERM ngetopt_lvl_ip_mtu_discover(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_ip_mtu_discover(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
ERL_NIF_TERM result;
ERL_NIF_TERM eMtuDisc;
@@ -12549,12 +12599,12 @@ ERL_NIF_TERM ngetopt_lvl_ip_mtu_discover(ErlNifEnv* env,
#endif
-/* ngetopt_lvl_ip_multicast_all - Level IP MULTICAST_ALL option
+/* esock_getopt_lvl_ip_multicast_all - Level IP MULTICAST_ALL option
*/
#if defined(IP_MULTICAST_ALL)
static
-ERL_NIF_TERM ngetopt_lvl_ip_multicast_all(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_ip_multicast_all(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
#if defined(SOL_IP)
int level = SOL_IP;
@@ -12562,17 +12612,17 @@ ERL_NIF_TERM ngetopt_lvl_ip_multicast_all(ErlNifEnv* env,
int level = IPPROTO_IP;
#endif
- return ngetopt_bool_opt(env, descP, level, IP_MULTICAST_ALL);
+ return esock_getopt_bool_opt(env, descP, level, IP_MULTICAST_ALL);
}
#endif
-/* ngetopt_lvl_ip_multicast_if - Level IP MULTICAST_IF option
+/* esock_getopt_lvl_ip_multicast_if - Level IP MULTICAST_IF option
*/
#if defined(IP_MULTICAST_IF)
static
-ERL_NIF_TERM ngetopt_lvl_ip_multicast_if(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_ip_multicast_if(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
ERL_NIF_TERM result;
ERL_NIF_TERM eAddr;
@@ -12604,12 +12654,12 @@ ERL_NIF_TERM ngetopt_lvl_ip_multicast_if(ErlNifEnv* env,
#endif
-/* ngetopt_lvl_ip_multicast_loop - Level IP MULTICAST_LOOP option
+/* esock_getopt_lvl_ip_multicast_loop - Level IP MULTICAST_LOOP option
*/
#if defined(IP_MULTICAST_LOOP)
static
-ERL_NIF_TERM ngetopt_lvl_ip_multicast_loop(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_ip_multicast_loop(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
#if defined(SOL_IP)
int level = SOL_IP;
@@ -12617,17 +12667,17 @@ ERL_NIF_TERM ngetopt_lvl_ip_multicast_loop(ErlNifEnv* env,
int level = IPPROTO_IP;
#endif
- return ngetopt_bool_opt(env, descP, level, IP_MULTICAST_LOOP);
+ return esock_getopt_bool_opt(env, descP, level, IP_MULTICAST_LOOP);
}
#endif
-/* ngetopt_lvl_ip_multicast_ttl - Level IP MULTICAST_TTL option
+/* esock_getopt_lvl_ip_multicast_ttl - Level IP MULTICAST_TTL option
*/
#if defined(IP_MULTICAST_TTL)
static
-ERL_NIF_TERM ngetopt_lvl_ip_multicast_ttl(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_ip_multicast_ttl(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
#if defined(SOL_IP)
int level = SOL_IP;
@@ -12635,17 +12685,17 @@ ERL_NIF_TERM ngetopt_lvl_ip_multicast_ttl(ErlNifEnv* env,
int level = IPPROTO_IP;
#endif
- return ngetopt_int_opt(env, descP, level, IP_MULTICAST_TTL);
+ return esock_getopt_int_opt(env, descP, level, IP_MULTICAST_TTL);
}
#endif
-/* ngetopt_lvl_ip_nodefrag - Level IP NODEFRAG option
+/* esock_getopt_lvl_ip_nodefrag - Level IP NODEFRAG option
*/
#if defined(IP_NODEFRAG)
static
-ERL_NIF_TERM ngetopt_lvl_ip_nodefrag(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_ip_nodefrag(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
#if defined(SOL_IP)
int level = SOL_IP;
@@ -12653,17 +12703,17 @@ ERL_NIF_TERM ngetopt_lvl_ip_nodefrag(ErlNifEnv* env,
int level = IPPROTO_IP;
#endif
- return ngetopt_bool_opt(env, descP, level, IP_NODEFRAG);
+ return esock_getopt_bool_opt(env, descP, level, IP_NODEFRAG);
}
#endif
-/* ngetopt_lvl_ip_pktinfo - Level IP PKTINFO option
+/* esock_getopt_lvl_ip_pktinfo - Level IP PKTINFO option
*/
#if defined(IP_PKTINFO)
static
-ERL_NIF_TERM ngetopt_lvl_ip_pktinfo(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_ip_pktinfo(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
#if defined(SOL_IP)
int level = SOL_IP;
@@ -12671,17 +12721,17 @@ ERL_NIF_TERM ngetopt_lvl_ip_pktinfo(ErlNifEnv* env,
int level = IPPROTO_IP;
#endif
- return ngetopt_bool_opt(env, descP, level, IP_PKTINFO);
+ return esock_getopt_bool_opt(env, descP, level, IP_PKTINFO);
}
#endif
-/* ngetopt_lvl_ip_recvtos - Level IP RECVTOS option
+/* esock_getopt_lvl_ip_recvtos - Level IP RECVTOS option
*/
#if defined(IP_RECVTOS)
static
-ERL_NIF_TERM ngetopt_lvl_ip_recvtos(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_ip_recvtos(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
#if defined(SOL_IP)
int level = SOL_IP;
@@ -12689,17 +12739,17 @@ ERL_NIF_TERM ngetopt_lvl_ip_recvtos(ErlNifEnv* env,
int level = IPPROTO_IP;
#endif
- return ngetopt_bool_opt(env, descP, level, IP_RECVTOS);
+ return esock_getopt_bool_opt(env, descP, level, IP_RECVTOS);
}
#endif
-/* ngetopt_lvl_ip_recvdstaddr - Level IP RECVDSTADDR option
+/* esock_getopt_lvl_ip_recvdstaddr - Level IP RECVDSTADDR option
*/
#if defined(IP_RECVDSTADDR)
static
-ERL_NIF_TERM ngetopt_lvl_ip_recvdstaddr(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_ip_recvdstaddr(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
#if defined(SOL_IP)
int level = SOL_IP;
@@ -12707,17 +12757,17 @@ ERL_NIF_TERM ngetopt_lvl_ip_recvdstaddr(ErlNifEnv* env,
int level = IPPROTO_IP;
#endif
- return ngetopt_bool_opt(env, descP, level, IP_RECVDSTADDR);
+ return esock_getopt_bool_opt(env, descP, level, IP_RECVDSTADDR);
}
#endif
-/* ngetopt_lvl_ip_recverr - Level IP RECVERR option
+/* esock_getopt_lvl_ip_recverr - Level IP RECVERR option
*/
#if defined(IP_RECVERR)
static
-ERL_NIF_TERM ngetopt_lvl_ip_recverr(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_ip_recverr(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
#if defined(SOL_IP)
int level = SOL_IP;
@@ -12725,17 +12775,17 @@ ERL_NIF_TERM ngetopt_lvl_ip_recverr(ErlNifEnv* env,
int level = IPPROTO_IP;
#endif
- return ngetopt_bool_opt(env, descP, level, IP_RECVERR);
+ return esock_getopt_bool_opt(env, descP, level, IP_RECVERR);
}
#endif
-/* ngetopt_lvl_ip_recvif - Level IP RECVIF option
+/* esock_getopt_lvl_ip_recvif - Level IP RECVIF option
*/
#if defined(IP_RECVIF)
static
-ERL_NIF_TERM ngetopt_lvl_ip_recvif(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_ip_recvif(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
#if defined(SOL_IP)
int level = SOL_IP;
@@ -12743,17 +12793,17 @@ ERL_NIF_TERM ngetopt_lvl_ip_recvif(ErlNifEnv* env,
int level = IPPROTO_IP;
#endif
- return ngetopt_bool_opt(env, descP, level, IP_RECVIF);
+ return esock_getopt_bool_opt(env, descP, level, IP_RECVIF);
}
#endif
-/* ngetopt_lvl_ip_recvopt - Level IP RECVOPTS option
+/* esock_getopt_lvl_ip_recvopt - Level IP RECVOPTS option
*/
#if defined(IP_RECVOPTS)
static
-ERL_NIF_TERM ngetopt_lvl_ip_recvopts(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_ip_recvopts(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
#if defined(SOL_IP)
int level = SOL_IP;
@@ -12761,17 +12811,17 @@ ERL_NIF_TERM ngetopt_lvl_ip_recvopts(ErlNifEnv* env,
int level = IPPROTO_IP;
#endif
- return ngetopt_bool_opt(env, descP, level, IP_RECVOPTS);
+ return esock_getopt_bool_opt(env, descP, level, IP_RECVOPTS);
}
#endif
-/* ngetopt_lvl_ip_recvorigdstaddr - Level IP RECVORIGDSTADDR option
+/* esock_getopt_lvl_ip_recvorigdstaddr - Level IP RECVORIGDSTADDR option
*/
#if defined(IP_RECVORIGDSTADDR)
static
-ERL_NIF_TERM ngetopt_lvl_ip_recvorigdstaddr(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_ip_recvorigdstaddr(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
#if defined(SOL_IP)
int level = SOL_IP;
@@ -12779,17 +12829,17 @@ ERL_NIF_TERM ngetopt_lvl_ip_recvorigdstaddr(ErlNifEnv* env,
int level = IPPROTO_IP;
#endif
- return ngetopt_bool_opt(env, descP, level, IP_RECVORIGDSTADDR);
+ return esock_getopt_bool_opt(env, descP, level, IP_RECVORIGDSTADDR);
}
#endif
-/* ngetopt_lvl_ip_recvttl - Level IP RECVTTL option
+/* esock_getopt_lvl_ip_recvttl - Level IP RECVTTL option
*/
#if defined(IP_RECVTTL)
static
-ERL_NIF_TERM ngetopt_lvl_ip_recvttl(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_ip_recvttl(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
#if defined(SOL_IP)
int level = SOL_IP;
@@ -12797,17 +12847,17 @@ ERL_NIF_TERM ngetopt_lvl_ip_recvttl(ErlNifEnv* env,
int level = IPPROTO_IP;
#endif
- return ngetopt_bool_opt(env, descP, level, IP_RECVTTL);
+ return esock_getopt_bool_opt(env, descP, level, IP_RECVTTL);
}
#endif
-/* ngetopt_lvl_ip_retopts - Level IP RETOPTS option
+/* esock_getopt_lvl_ip_retopts - Level IP RETOPTS option
*/
#if defined(IP_RETOPTS)
static
-ERL_NIF_TERM ngetopt_lvl_ip_retopts(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_ip_retopts(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
#if defined(SOL_IP)
int level = SOL_IP;
@@ -12815,17 +12865,17 @@ ERL_NIF_TERM ngetopt_lvl_ip_retopts(ErlNifEnv* env,
int level = IPPROTO_IP;
#endif
- return ngetopt_bool_opt(env, descP, level, IP_RETOPTS);
+ return esock_getopt_bool_opt(env, descP, level, IP_RETOPTS);
}
#endif
-/* ngetopt_lvl_ip_router_alert - Level IP ROUTER_ALERT option
+/* esock_getopt_lvl_ip_router_alert - Level IP ROUTER_ALERT option
*/
#if defined(IP_ROUTER_ALERT)
static
-ERL_NIF_TERM ngetopt_lvl_ip_router_alert(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_ip_router_alert(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
#if defined(SOL_IP)
int level = SOL_IP;
@@ -12833,17 +12883,17 @@ ERL_NIF_TERM ngetopt_lvl_ip_router_alert(ErlNifEnv* env,
int level = IPPROTO_IP;
#endif
- return ngetopt_int_opt(env, descP, level, IP_ROUTER_ALERT);
+ return esock_getopt_int_opt(env, descP, level, IP_ROUTER_ALERT);
}
#endif
-/* ngetopt_lvl_ip_sendsrcaddr - Level IP SENDSRCADDR option
+/* esock_getopt_lvl_ip_sendsrcaddr - Level IP SENDSRCADDR option
*/
#if defined(IP_SENDSRCADDR)
static
-ERL_NIF_TERM ngetopt_lvl_ip_sendsrcaddr(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_ip_sendsrcaddr(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
#if defined(SOL_IP)
int level = SOL_IP;
@@ -12851,17 +12901,17 @@ ERL_NIF_TERM ngetopt_lvl_ip_sendsrcaddr(ErlNifEnv* env,
int level = IPPROTO_IP;
#endif
- return ngetopt_bool_opt(env, descP, level, IP_SENDSRCADDR);
+ return esock_getopt_bool_opt(env, descP, level, IP_SENDSRCADDR);
}
#endif
-/* ngetopt_lvl_ip_tos - Level IP TOS option
+/* esock_getopt_lvl_ip_tos - Level IP TOS option
*/
#if defined(IP_TOS)
static
-ERL_NIF_TERM ngetopt_lvl_ip_tos(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_ip_tos(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
#if defined(SOL_IP)
int level = SOL_IP;
@@ -12886,12 +12936,12 @@ ERL_NIF_TERM ngetopt_lvl_ip_tos(ErlNifEnv* env,
#endif
-/* ngetopt_lvl_ip_transparent - Level IP TRANSPARENT option
+/* esock_getopt_lvl_ip_transparent - Level IP TRANSPARENT option
*/
#if defined(IP_TRANSPARENT)
static
-ERL_NIF_TERM ngetopt_lvl_ip_transparent(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_ip_transparent(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
#if defined(SOL_IP)
int level = SOL_IP;
@@ -12899,18 +12949,18 @@ ERL_NIF_TERM ngetopt_lvl_ip_transparent(ErlNifEnv* env,
int level = IPPROTO_IP;
#endif
- return ngetopt_bool_opt(env, descP, level, IP_TRANSPARENT);
+ return esock_getopt_bool_opt(env, descP, level, IP_TRANSPARENT);
}
#endif
-/* ngetopt_lvl_ip_ttl - Level IP TTL option
+/* esock_getopt_lvl_ip_ttl - Level IP TTL option
*/
#if defined(IP_TTL)
static
-ERL_NIF_TERM ngetopt_lvl_ip_ttl(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_ip_ttl(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
#if defined(SOL_IP)
int level = SOL_IP;
@@ -12918,121 +12968,121 @@ ERL_NIF_TERM ngetopt_lvl_ip_ttl(ErlNifEnv* env,
int level = IPPROTO_IP;
#endif
- return ngetopt_int_opt(env, descP, level, IP_TTL);
+ return esock_getopt_int_opt(env, descP, level, IP_TTL);
}
#endif
-/* ngetopt_lvl_ipv6 - Level *IPv6* option(s)
+/* esock_getopt_lvl_ipv6 - Level *IPv6* option(s)
*/
#if defined(HAVE_IPV6)
static
-ERL_NIF_TERM ngetopt_lvl_ipv6(ErlNifEnv* env,
- ESockDescriptor* descP,
- int eOpt)
+ERL_NIF_TERM esock_getopt_lvl_ipv6(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int eOpt)
{
ERL_NIF_TERM result;
SSDBG( descP,
- ("SOCKET", "ngetopt_lvl_ipv6 -> entry with"
+ ("SOCKET", "esock_getopt_lvl_ipv6 -> entry with"
"\r\n eOpt: %d"
"\r\n", eOpt) );
switch (eOpt) {
#if defined(IPV6_AUTHHDR)
- case SOCKET_OPT_IPV6_AUTHHDR:
- result = ngetopt_lvl_ipv6_authhdr(env, descP);
+ case ESOCK_OPT_IPV6_AUTHHDR:
+ result = esock_getopt_lvl_ipv6_authhdr(env, descP);
break;
#endif
#if defined(IPV6_DSTOPTS)
- case SOCKET_OPT_IPV6_DSTOPTS:
- result = ngetopt_lvl_ipv6_dstopts(env, descP);
+ case ESOCK_OPT_IPV6_DSTOPTS:
+ result = esock_getopt_lvl_ipv6_dstopts(env, descP);
break;
#endif
#if defined(IPV6_FLOWINFO)
- case SOCKET_OPT_IPV6_FLOWINFO:
- result = ngetopt_lvl_ipv6_flowinfo(env, descP);
+ case ESOCK_OPT_IPV6_FLOWINFO:
+ result = esock_getopt_lvl_ipv6_flowinfo(env, descP);
break;
#endif
#if defined(IPV6_HOPLIMIT)
- case SOCKET_OPT_IPV6_HOPLIMIT:
- result = ngetopt_lvl_ipv6_hoplimit(env, descP);
+ case ESOCK_OPT_IPV6_HOPLIMIT:
+ result = esock_getopt_lvl_ipv6_hoplimit(env, descP);
break;
#endif
#if defined(IPV6_HOPOPTS)
- case SOCKET_OPT_IPV6_HOPOPTS:
- result = ngetopt_lvl_ipv6_hopopts(env, descP);
+ case ESOCK_OPT_IPV6_HOPOPTS:
+ result = esock_getopt_lvl_ipv6_hopopts(env, descP);
break;
#endif
#if defined(IPV6_MTU)
- case SOCKET_OPT_IPV6_MTU:
- result = ngetopt_lvl_ipv6_mtu(env, descP);
+ case ESOCK_OPT_IPV6_MTU:
+ result = esock_getopt_lvl_ipv6_mtu(env, descP);
break;
#endif
#if defined(IPV6_MTU_DISCOVER)
- case SOCKET_OPT_IPV6_MTU_DISCOVER:
- result = ngetopt_lvl_ipv6_mtu_discover(env, descP);
+ case ESOCK_OPT_IPV6_MTU_DISCOVER:
+ result = esock_getopt_lvl_ipv6_mtu_discover(env, descP);
break;
#endif
#if defined(IPV6_MULTICAST_HOPS)
- case SOCKET_OPT_IPV6_MULTICAST_HOPS:
- result = ngetopt_lvl_ipv6_multicast_hops(env, descP);
+ case ESOCK_OPT_IPV6_MULTICAST_HOPS:
+ result = esock_getopt_lvl_ipv6_multicast_hops(env, descP);
break;
#endif
#if defined(IPV6_MULTICAST_IF)
- case SOCKET_OPT_IPV6_MULTICAST_IF:
- result = ngetopt_lvl_ipv6_multicast_if(env, descP);
+ case ESOCK_OPT_IPV6_MULTICAST_IF:
+ result = esock_getopt_lvl_ipv6_multicast_if(env, descP);
break;
#endif
#if defined(IPV6_MULTICAST_LOOP)
- case SOCKET_OPT_IPV6_MULTICAST_LOOP:
- result = ngetopt_lvl_ipv6_multicast_loop(env, descP);
+ case ESOCK_OPT_IPV6_MULTICAST_LOOP:
+ result = esock_getopt_lvl_ipv6_multicast_loop(env, descP);
break;
#endif
#if defined(IPV6_RECVERR)
- case SOCKET_OPT_IPV6_RECVERR:
- result = ngetopt_lvl_ipv6_recverr(env, descP);
+ case ESOCK_OPT_IPV6_RECVERR:
+ result = esock_getopt_lvl_ipv6_recverr(env, descP);
break;
#endif
#if defined(IPV6_RECVPKTINFO) || defined(IPV6_PKTINFO)
- case SOCKET_OPT_IPV6_RECVPKTINFO:
- result = ngetopt_lvl_ipv6_recvpktinfo(env, descP);
+ case ESOCK_OPT_IPV6_RECVPKTINFO:
+ result = esock_getopt_lvl_ipv6_recvpktinfo(env, descP);
break;
#endif
#if defined(IPV6_ROUTER_ALERT)
- case SOCKET_OPT_IPV6_ROUTER_ALERT:
- result = ngetopt_lvl_ipv6_router_alert(env, descP);
+ case ESOCK_OPT_IPV6_ROUTER_ALERT:
+ result = esock_getopt_lvl_ipv6_router_alert(env, descP);
break;
#endif
#if defined(IPV6_RTHDR)
- case SOCKET_OPT_IPV6_RTHDR:
- result = ngetopt_lvl_ipv6_rthdr(env, descP);
+ case ESOCK_OPT_IPV6_RTHDR:
+ result = esock_getopt_lvl_ipv6_rthdr(env, descP);
break;
#endif
#if defined(IPV6_UNICAST_HOPS)
- case SOCKET_OPT_IPV6_UNICAST_HOPS:
- result = ngetopt_lvl_ipv6_unicast_hops(env, descP);
+ case ESOCK_OPT_IPV6_UNICAST_HOPS:
+ result = esock_getopt_lvl_ipv6_unicast_hops(env, descP);
break;
#endif
#if defined(IPV6_V6ONLY)
- case SOCKET_OPT_IPV6_V6ONLY:
- result = ngetopt_lvl_ipv6_v6only(env, descP);
+ case ESOCK_OPT_IPV6_V6ONLY:
+ result = esock_getopt_lvl_ipv6_v6only(env, descP);
break;
#endif
@@ -13042,7 +13092,7 @@ ERL_NIF_TERM ngetopt_lvl_ipv6(ErlNifEnv* env,
}
SSDBG( descP,
- ("SOCKET", "ngetopt_lvl_ipv6 -> done when"
+ ("SOCKET", "esock_getopt_lvl_ipv6 -> done when"
"\r\n result: %T"
"\r\n", result) );
@@ -13052,33 +13102,33 @@ ERL_NIF_TERM ngetopt_lvl_ipv6(ErlNifEnv* env,
#if defined(IPV6_AUTHHDR)
static
-ERL_NIF_TERM ngetopt_lvl_ipv6_authhdr(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_ipv6_authhdr(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
- return ngetopt_bool_opt(env, descP, SOL_IPV6, IPV6_AUTHHDR);
+ return esock_getopt_bool_opt(env, descP, SOL_IPV6, IPV6_AUTHHDR);
}
#endif
#if defined(IPV6_DSTOPTS)
static
-ERL_NIF_TERM ngetopt_lvl_ipv6_dstopts(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_ipv6_dstopts(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
#if defined(SOL_IPV6)
int level = SOL_IPV6;
#else
int level = IPPROTO_IPV6;
#endif
- return ngetopt_bool_opt(env, descP, level, IPV6_DSTOPTS);
+ return esock_getopt_bool_opt(env, descP, level, IPV6_DSTOPTS);
}
#endif
#if defined(IPV6_FLOWINFO)
static
-ERL_NIF_TERM ngetopt_lvl_ipv6_flowinfo(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_ipv6_flowinfo(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
#if defined(SOL_IPV6)
int level = SOL_IPV6;
@@ -13086,15 +13136,15 @@ ERL_NIF_TERM ngetopt_lvl_ipv6_flowinfo(ErlNifEnv* env,
int level = IPPROTO_IPV6;
#endif
- return ngetopt_bool_opt(env, descP, level, IPV6_FLOWINFO);
+ return esock_getopt_bool_opt(env, descP, level, IPV6_FLOWINFO);
}
#endif
#if defined(IPV6_HOPLIMIT)
static
-ERL_NIF_TERM ngetopt_lvl_ipv6_hoplimit(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_ipv6_hoplimit(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
#if defined(SOL_IPV6)
int level = SOL_IPV6;
@@ -13102,15 +13152,15 @@ ERL_NIF_TERM ngetopt_lvl_ipv6_hoplimit(ErlNifEnv* env,
int level = IPPROTO_IPV6;
#endif
- return ngetopt_bool_opt(env, descP, level, IPV6_HOPLIMIT);
+ return esock_getopt_bool_opt(env, descP, level, IPV6_HOPLIMIT);
}
#endif
#if defined(IPV6_HOPOPTS)
static
-ERL_NIF_TERM ngetopt_lvl_ipv6_hopopts(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_ipv6_hopopts(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
#if defined(SOL_IPV6)
int level = SOL_IPV6;
@@ -13118,15 +13168,15 @@ ERL_NIF_TERM ngetopt_lvl_ipv6_hopopts(ErlNifEnv* env,
int level = IPPROTO_IPV6;
#endif
- return ngetopt_bool_opt(env, descP, level, IPV6_HOPOPTS);
+ return esock_getopt_bool_opt(env, descP, level, IPV6_HOPOPTS);
}
#endif
#if defined(IPV6_MTU)
static
-ERL_NIF_TERM ngetopt_lvl_ipv6_mtu(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_ipv6_mtu(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
#if defined(SOL_IPV6)
int level = SOL_IPV6;
@@ -13134,17 +13184,17 @@ ERL_NIF_TERM ngetopt_lvl_ipv6_mtu(ErlNifEnv* env,
int level = IPPROTO_IPV6;
#endif
- return ngetopt_int_opt(env, descP, level, IPV6_MTU);
+ return esock_getopt_int_opt(env, descP, level, IPV6_MTU);
}
#endif
-/* ngetopt_lvl_ipv6_mtu_discover - Level IPv6 MTU_DISCOVER option
+/* esock_getopt_lvl_ipv6_mtu_discover - Level IPv6 MTU_DISCOVER option
*/
#if defined(IPV6_MTU_DISCOVER)
static
-ERL_NIF_TERM ngetopt_lvl_ipv6_mtu_discover(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_ipv6_mtu_discover(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
ERL_NIF_TERM result;
ERL_NIF_TERM eMtuDisc;
@@ -13175,8 +13225,8 @@ ERL_NIF_TERM ngetopt_lvl_ipv6_mtu_discover(ErlNifEnv* env,
#if defined(IPV6_MULTICAST_HOPS)
static
-ERL_NIF_TERM ngetopt_lvl_ipv6_multicast_hops(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_ipv6_multicast_hops(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
#if defined(SOL_IPV6)
int level = SOL_IPV6;
@@ -13184,15 +13234,15 @@ ERL_NIF_TERM ngetopt_lvl_ipv6_multicast_hops(ErlNifEnv* env,
int level = IPPROTO_IPV6;
#endif
- return ngetopt_int_opt(env, descP, level, IPV6_MULTICAST_HOPS);
+ return esock_getopt_int_opt(env, descP, level, IPV6_MULTICAST_HOPS);
}
#endif
#if defined(IPV6_MULTICAST_IF)
static
-ERL_NIF_TERM ngetopt_lvl_ipv6_multicast_if(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_ipv6_multicast_if(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
#if defined(SOL_IPV6)
int level = SOL_IPV6;
@@ -13200,15 +13250,15 @@ ERL_NIF_TERM ngetopt_lvl_ipv6_multicast_if(ErlNifEnv* env,
int level = IPPROTO_IPV6;
#endif
- return ngetopt_int_opt(env, descP, level, IPV6_MULTICAST_IF);
+ return esock_getopt_int_opt(env, descP, level, IPV6_MULTICAST_IF);
}
#endif
#if defined(IPV6_MULTICAST_LOOP)
static
-ERL_NIF_TERM ngetopt_lvl_ipv6_multicast_loop(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_ipv6_multicast_loop(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
#if defined(SOL_IPV6)
int level = SOL_IPV6;
@@ -13216,15 +13266,15 @@ ERL_NIF_TERM ngetopt_lvl_ipv6_multicast_loop(ErlNifEnv* env,
int level = IPPROTO_IPV6;
#endif
- return ngetopt_bool_opt(env, descP, level, IPV6_MULTICAST_LOOP);
+ return esock_getopt_bool_opt(env, descP, level, IPV6_MULTICAST_LOOP);
}
#endif
#if defined(IPV6_RECVERR)
static
-ERL_NIF_TERM ngetopt_lvl_ipv6_recverr(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_ipv6_recverr(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
#if defined(SOL_IPV6)
int level = SOL_IPV6;
@@ -13232,15 +13282,15 @@ ERL_NIF_TERM ngetopt_lvl_ipv6_recverr(ErlNifEnv* env,
int level = IPPROTO_IPV6;
#endif
- return ngetopt_bool_opt(env, descP, level, IPV6_RECVERR);
+ return esock_getopt_bool_opt(env, descP, level, IPV6_RECVERR);
}
#endif
#if defined(IPV6_RECVPKTINFO) || defined(IPV6_PKTINFO)
static
-ERL_NIF_TERM ngetopt_lvl_ipv6_recvpktinfo(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_ipv6_recvpktinfo(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
#if defined(SOL_IPV6)
int level = SOL_IPV6;
@@ -13253,15 +13303,15 @@ ERL_NIF_TERM ngetopt_lvl_ipv6_recvpktinfo(ErlNifEnv* env,
int opt = IPV6_PKTINFO;
#endif
- return ngetopt_bool_opt(env, descP, level, opt);
+ return esock_getopt_bool_opt(env, descP, level, opt);
}
#endif
#if defined(IPV6_ROUTER_ALERT)
static
-ERL_NIF_TERM ngetopt_lvl_ipv6_router_alert(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_ipv6_router_alert(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
#if defined(SOL_IPV6)
int level = SOL_IPV6;
@@ -13269,15 +13319,15 @@ ERL_NIF_TERM ngetopt_lvl_ipv6_router_alert(ErlNifEnv* env,
int level = IPPROTO_IPV6;
#endif
- return ngetopt_int_opt(env, descP, level, IPV6_ROUTER_ALERT);
+ return esock_getopt_int_opt(env, descP, level, IPV6_ROUTER_ALERT);
}
#endif
#if defined(IPV6_RTHDR)
static
-ERL_NIF_TERM ngetopt_lvl_ipv6_rthdr(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_ipv6_rthdr(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
#if defined(SOL_IPV6)
int level = SOL_IPV6;
@@ -13285,15 +13335,15 @@ ERL_NIF_TERM ngetopt_lvl_ipv6_rthdr(ErlNifEnv* env,
int level = IPPROTO_IPV6;
#endif
- return ngetopt_bool_opt(env, descP, level, IPV6_RTHDR);
+ return esock_getopt_bool_opt(env, descP, level, IPV6_RTHDR);
}
#endif
#if defined(IPV6_UNICAST_HOPS)
static
-ERL_NIF_TERM ngetopt_lvl_ipv6_unicast_hops(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_ipv6_unicast_hops(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
#if defined(SOL_IPV6)
int level = SOL_IPV6;
@@ -13301,15 +13351,15 @@ ERL_NIF_TERM ngetopt_lvl_ipv6_unicast_hops(ErlNifEnv* env,
int level = IPPROTO_IPV6;
#endif
- return ngetopt_int_opt(env, descP, level, IPV6_UNICAST_HOPS);
+ return esock_getopt_int_opt(env, descP, level, IPV6_UNICAST_HOPS);
}
#endif
#if defined(IPV6_V6ONLY)
static
-ERL_NIF_TERM ngetopt_lvl_ipv6_v6only(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_ipv6_v6only(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
#if defined(SOL_IPV6)
int level = SOL_IPV6;
@@ -13317,7 +13367,7 @@ ERL_NIF_TERM ngetopt_lvl_ipv6_v6only(ErlNifEnv* env,
int level = IPPROTO_IPV6;
#endif
- return ngetopt_bool_opt(env, descP, level, IPV6_V6ONLY);
+ return esock_getopt_bool_opt(env, descP, level, IPV6_V6ONLY);
}
#endif
@@ -13326,31 +13376,31 @@ ERL_NIF_TERM ngetopt_lvl_ipv6_v6only(ErlNifEnv* env,
-/* ngetopt_lvl_tcp - Level *TCP* option(s)
+/* esock_getopt_lvl_tcp - Level *TCP* option(s)
*/
static
-ERL_NIF_TERM ngetopt_lvl_tcp(ErlNifEnv* env,
- ESockDescriptor* descP,
- int eOpt)
+ERL_NIF_TERM esock_getopt_lvl_tcp(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int eOpt)
{
ERL_NIF_TERM result;
switch (eOpt) {
#if defined(TCP_CONGESTION)
- case SOCKET_OPT_TCP_CONGESTION:
- result = ngetopt_lvl_tcp_congestion(env, descP);
+ case ESOCK_OPT_TCP_CONGESTION:
+ result = esock_getopt_lvl_tcp_congestion(env, descP);
break;
#endif
#if defined(TCP_MAXSEG)
- case SOCKET_OPT_TCP_MAXSEG:
- result = ngetopt_lvl_tcp_maxseg(env, descP);
+ case ESOCK_OPT_TCP_MAXSEG:
+ result = esock_getopt_lvl_tcp_maxseg(env, descP);
break;
#endif
#if defined(TCP_NODELAY)
- case SOCKET_OPT_TCP_NODELAY:
- result = ngetopt_lvl_tcp_nodelay(env, descP);
+ case ESOCK_OPT_TCP_NODELAY:
+ result = esock_getopt_lvl_tcp_nodelay(env, descP);
break;
#endif
@@ -13363,58 +13413,58 @@ ERL_NIF_TERM ngetopt_lvl_tcp(ErlNifEnv* env,
}
-/* ngetopt_lvl_tcp_congestion - Level TCP CONGESTION option
+/* esock_getopt_lvl_tcp_congestion - Level TCP CONGESTION option
*/
#if defined(TCP_CONGESTION)
static
-ERL_NIF_TERM ngetopt_lvl_tcp_congestion(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_tcp_congestion(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
- int max = SOCKET_OPT_TCP_CONGESTION_NAME_MAX+1;
+ int max = ESOCK_OPT_TCP_CONGESTION_NAME_MAX+1;
- return ngetopt_str_opt(env, descP, IPPROTO_TCP, TCP_CONGESTION, max);
+ return esock_getopt_str_opt(env, descP, IPPROTO_TCP, TCP_CONGESTION, max);
}
#endif
-/* ngetopt_lvl_tcp_maxseg - Level TCP MAXSEG option
+/* esock_getopt_lvl_tcp_maxseg - Level TCP MAXSEG option
*/
#if defined(TCP_MAXSEG)
static
-ERL_NIF_TERM ngetopt_lvl_tcp_maxseg(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_tcp_maxseg(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
- return ngetopt_int_opt(env, descP, IPPROTO_TCP, TCP_MAXSEG);
+ return esock_getopt_int_opt(env, descP, IPPROTO_TCP, TCP_MAXSEG);
}
#endif
-/* ngetopt_lvl_tcp_nodelay - Level TCP NODELAY option
+/* esock_getopt_lvl_tcp_nodelay - Level TCP NODELAY option
*/
#if defined(TCP_NODELAY)
static
-ERL_NIF_TERM ngetopt_lvl_tcp_nodelay(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_tcp_nodelay(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
- return ngetopt_bool_opt(env, descP, IPPROTO_TCP, TCP_NODELAY);
+ return esock_getopt_bool_opt(env, descP, IPPROTO_TCP, TCP_NODELAY);
}
#endif
-/* ngetopt_lvl_udp - Level *UDP* option(s)
+/* esock_getopt_lvl_udp - Level *UDP* option(s)
*/
static
-ERL_NIF_TERM ngetopt_lvl_udp(ErlNifEnv* env,
- ESockDescriptor* descP,
- int eOpt)
+ERL_NIF_TERM esock_getopt_lvl_udp(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int eOpt)
{
ERL_NIF_TERM result;
switch (eOpt) {
#if defined(UDP_CORK)
- case SOCKET_OPT_UDP_CORK:
- result = ngetopt_lvl_udp_cork(env, descP);
+ case ESOCK_OPT_UDP_CORK:
+ result = esock_getopt_lvl_udp_cork(env, descP);
break;
#endif
@@ -13427,74 +13477,74 @@ ERL_NIF_TERM ngetopt_lvl_udp(ErlNifEnv* env,
}
-/* ngetopt_lvl_udp_cork - Level UDP CORK option
+/* esock_getopt_lvl_udp_cork - Level UDP CORK option
*/
#if defined(UDP_CORK)
static
-ERL_NIF_TERM ngetopt_lvl_udp_cork(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_udp_cork(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
- return ngetopt_bool_opt(env, descP, IPPROTO_UDP, UDP_CORK);
+ return esock_getopt_bool_opt(env, descP, IPPROTO_UDP, UDP_CORK);
}
#endif
-/* ngetopt_lvl_sctp - Level *SCTP* option(s)
+/* esock_getopt_lvl_sctp - Level *SCTP* option(s)
*/
#if defined(HAVE_SCTP)
static
-ERL_NIF_TERM ngetopt_lvl_sctp(ErlNifEnv* env,
- ESockDescriptor* descP,
- int eOpt)
+ERL_NIF_TERM esock_getopt_lvl_sctp(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int eOpt)
{
ERL_NIF_TERM result;
SSDBG( descP,
- ("SOCKET", "ngetopt_lvl_sctp -> entry with"
+ ("SOCKET", "esock_getopt_lvl_sctp -> entry with"
"\r\n opt: %d"
"\r\n", eOpt) );
switch (eOpt) {
#if defined(SCTP_ASSOCINFO)
- case SOCKET_OPT_SCTP_ASSOCINFO:
- result = ngetopt_lvl_sctp_associnfo(env, descP);
+ case ESOCK_OPT_SCTP_ASSOCINFO:
+ result = esock_getopt_lvl_sctp_associnfo(env, descP);
break;
#endif
#if defined(SCTP_AUTOCLOSE)
- case SOCKET_OPT_SCTP_AUTOCLOSE:
- result = ngetopt_lvl_sctp_autoclose(env, descP);
+ case ESOCK_OPT_SCTP_AUTOCLOSE:
+ result = esock_getopt_lvl_sctp_autoclose(env, descP);
break;
#endif
#if defined(SCTP_DISABLE_FRAGMENTS)
- case SOCKET_OPT_SCTP_DISABLE_FRAGMENTS:
- result = ngetopt_lvl_sctp_disable_fragments(env, descP);
+ case ESOCK_OPT_SCTP_DISABLE_FRAGMENTS:
+ result = esock_getopt_lvl_sctp_disable_fragments(env, descP);
break;
#endif
#if defined(SCTP_INITMSG)
- case SOCKET_OPT_SCTP_INITMSG:
- result = ngetopt_lvl_sctp_initmsg(env, descP);
+ case ESOCK_OPT_SCTP_INITMSG:
+ result = esock_getopt_lvl_sctp_initmsg(env, descP);
break;
#endif
#if defined(SCTP_MAXSEG)
- case SOCKET_OPT_SCTP_MAXSEG:
- result = ngetopt_lvl_sctp_maxseg(env, descP);
+ case ESOCK_OPT_SCTP_MAXSEG:
+ result = esock_getopt_lvl_sctp_maxseg(env, descP);
break;
#endif
#if defined(SCTP_NODELAY)
- case SOCKET_OPT_SCTP_NODELAY:
- result = ngetopt_lvl_sctp_nodelay(env, descP);
+ case ESOCK_OPT_SCTP_NODELAY:
+ result = esock_getopt_lvl_sctp_nodelay(env, descP);
break;
#endif
#if defined(SCTP_RTOINFO)
- case SOCKET_OPT_SCTP_RTOINFO:
- result = ngetopt_lvl_sctp_rtoinfo(env, descP);
+ case ESOCK_OPT_SCTP_RTOINFO:
+ result = esock_getopt_lvl_sctp_rtoinfo(env, descP);
break;
#endif
@@ -13504,7 +13554,7 @@ ERL_NIF_TERM ngetopt_lvl_sctp(ErlNifEnv* env,
}
SSDBG( descP,
- ("SOCKET", "ngetopt_lvl_sctp -> done when"
+ ("SOCKET", "esock_getopt_lvl_sctp -> done when"
"\r\n result: %T"
"\r\n", result) );
@@ -13512,7 +13562,7 @@ ERL_NIF_TERM ngetopt_lvl_sctp(ErlNifEnv* env,
}
-/* ngetopt_lvl_sctp_associnfo - Level SCTP ASSOCINFO option
+/* esock_getopt_lvl_sctp_associnfo - Level SCTP ASSOCINFO option
*
* <KOLLA>
*
@@ -13527,15 +13577,15 @@ ERL_NIF_TERM ngetopt_lvl_sctp(ErlNifEnv* env,
*/
#if defined(SCTP_ASSOCINFO)
static
-ERL_NIF_TERM ngetopt_lvl_sctp_associnfo(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_sctp_associnfo(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
ERL_NIF_TERM result;
struct sctp_assocparams val;
SOCKOPTLEN_T valSz = sizeof(val);
int res;
- SSDBG( descP, ("SOCKET", "ngetopt_lvl_sctp_associnfo -> entry\r\n") );
+ SSDBG( descP, ("SOCKET", "esock_getopt_lvl_sctp_associnfo -> entry\r\n") );
sys_memzero((char*) &val, valSz);
res = sock_getopt(descP->sock, IPPROTO_SCTP, SCTP_ASSOCINFO, &val, &valSz);
@@ -13564,7 +13614,7 @@ ERL_NIF_TERM ngetopt_lvl_sctp_associnfo(ErlNifEnv* env,
}
SSDBG( descP,
- ("SOCKET", "ngetopt_lvl_sctp_associnfo -> done with"
+ ("SOCKET", "esock_getopt_lvl_sctp_associnfo -> done with"
"\r\n res: %d"
"\r\n result: %T"
"\r\n", res, result) );
@@ -13574,44 +13624,45 @@ ERL_NIF_TERM ngetopt_lvl_sctp_associnfo(ErlNifEnv* env,
#endif
-/* ngetopt_lvl_sctp_autoclose - Level SCTP AUTOCLOSE option
+/* esock_getopt_lvl_sctp_autoclose - Level SCTP AUTOCLOSE option
*/
#if defined(SCTP_AUTOCLOSE)
static
-ERL_NIF_TERM ngetopt_lvl_sctp_autoclose(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_sctp_autoclose(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
- return ngetopt_int_opt(env, descP, IPPROTO_SCTP, SCTP_AUTOCLOSE);
+ return esock_getopt_int_opt(env, descP, IPPROTO_SCTP, SCTP_AUTOCLOSE);
}
#endif
-/* ngetopt_lvl_sctp_disable_fragments - Level SCTP DISABLE:FRAGMENTS option
+/* esock_getopt_lvl_sctp_disable_fragments - Level SCTP DISABLE:FRAGMENTS option
*/
#if defined(SCTP_DISABLE_FRAGMENTS)
static
-ERL_NIF_TERM ngetopt_lvl_sctp_disable_fragments(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_sctp_disable_fragments(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
- return ngetopt_bool_opt(env, descP, IPPROTO_SCTP, SCTP_DISABLE_FRAGMENTS);
+ return esock_getopt_bool_opt(env, descP,
+ IPPROTO_SCTP, SCTP_DISABLE_FRAGMENTS);
}
#endif
-/* ngetopt_lvl_sctp_initmsg - Level SCTP INITMSG option
+/* esock_getopt_lvl_sctp_initmsg - Level SCTP INITMSG option
*
*/
#if defined(SCTP_INITMSG)
static
-ERL_NIF_TERM ngetopt_lvl_sctp_initmsg(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_sctp_initmsg(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
ERL_NIF_TERM result;
struct sctp_initmsg val;
SOCKOPTLEN_T valSz = sizeof(val);
int res;
- SSDBG( descP, ("SOCKET", "ngetopt_lvl_sctp_initmsg -> entry\r\n") );
+ SSDBG( descP, ("SOCKET", "esock_getopt_lvl_sctp_initmsg -> entry\r\n") );
sys_memzero((char*) &val, valSz);
res = sock_getopt(descP->sock, IPPROTO_SCTP, SCTP_INITMSG, &val, &valSz);
@@ -13638,7 +13689,7 @@ ERL_NIF_TERM ngetopt_lvl_sctp_initmsg(ErlNifEnv* env,
}
SSDBG( descP,
- ("SOCKET", "ngetopt_lvl_sctp_initmsg -> done with"
+ ("SOCKET", "esock_getopt_lvl_sctp_initmsg -> done with"
"\r\n res: %d"
"\r\n result: %T"
"\r\n", res, result) );
@@ -13648,31 +13699,31 @@ ERL_NIF_TERM ngetopt_lvl_sctp_initmsg(ErlNifEnv* env,
#endif
-/* ngetopt_lvl_sctp_maxseg - Level SCTP MAXSEG option
+/* esock_getopt_lvl_sctp_maxseg - Level SCTP MAXSEG option
*/
#if defined(SCTP_MAXSEG)
static
-ERL_NIF_TERM ngetopt_lvl_sctp_maxseg(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_sctp_maxseg(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
- return ngetopt_int_opt(env, descP, IPPROTO_SCTP, SCTP_MAXSEG);
+ return esock_getopt_int_opt(env, descP, IPPROTO_SCTP, SCTP_MAXSEG);
}
#endif
-/* ngetopt_lvl_sctp_nodelay - Level SCTP NODELAY option
+/* esock_getopt_lvl_sctp_nodelay - Level SCTP NODELAY option
*/
#if defined(SCTP_NODELAY)
static
-ERL_NIF_TERM ngetopt_lvl_sctp_nodelay(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_sctp_nodelay(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
- return ngetopt_bool_opt(env, descP, IPPROTO_SCTP, SCTP_NODELAY);
+ return esock_getopt_bool_opt(env, descP, IPPROTO_SCTP, SCTP_NODELAY);
}
#endif
-/* ngetopt_lvl_sctp_associnfo - Level SCTP ASSOCINFO option
+/* esock_getopt_lvl_sctp_associnfo - Level SCTP ASSOCINFO option
*
* <KOLLA>
*
@@ -13687,15 +13738,15 @@ ERL_NIF_TERM ngetopt_lvl_sctp_nodelay(ErlNifEnv* env,
*/
#if defined(SCTP_RTOINFO)
static
-ERL_NIF_TERM ngetopt_lvl_sctp_rtoinfo(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_getopt_lvl_sctp_rtoinfo(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
ERL_NIF_TERM result;
struct sctp_rtoinfo val;
SOCKOPTLEN_T valSz = sizeof(val);
int res;
- SSDBG( descP, ("SOCKET", "ngetopt_lvl_sctp_rtoinfo -> entry\r\n") );
+ SSDBG( descP, ("SOCKET", "esock_getopt_lvl_sctp_rtoinfo -> entry\r\n") );
sys_memzero((char*) &val, valSz);
res = sock_getopt(descP->sock, IPPROTO_SCTP, SCTP_RTOINFO, &val, &valSz);
@@ -13721,7 +13772,7 @@ ERL_NIF_TERM ngetopt_lvl_sctp_rtoinfo(ErlNifEnv* env,
}
SSDBG( descP,
- ("SOCKET", "ngetopt_lvl_sctp_rtoinfo -> done with"
+ ("SOCKET", "esock_getopt_lvl_sctp_rtoinfo -> done with"
"\r\n res: %d"
"\r\n result: %T"
"\r\n", res, result) );
@@ -13736,13 +13787,13 @@ ERL_NIF_TERM ngetopt_lvl_sctp_rtoinfo(ErlNifEnv* env,
-/* ngetopt_bool_opt - get an (integer) bool option
+/* esock_getopt_bool_opt - get an (integer) bool option
*/
static
-ERL_NIF_TERM ngetopt_bool_opt(ErlNifEnv* env,
- ESockDescriptor* descP,
- int level,
- int opt)
+ERL_NIF_TERM esock_getopt_bool_opt(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int level,
+ int opt)
{
ERL_NIF_TERM result;
int val;
@@ -13750,7 +13801,7 @@ ERL_NIF_TERM ngetopt_bool_opt(ErlNifEnv* env,
int res;
/*
- SSDBG( descP, ("SOCKET", "ngetopt_bool_opt -> entry with"
+ SSDBG( descP, ("SOCKET", "esock_getopt_bool_opt -> entry with"
"\r\n: level: %d"
"\r\n: opt: %d"
"\r\n", level, opt) );
@@ -13767,7 +13818,7 @@ ERL_NIF_TERM ngetopt_bool_opt(ErlNifEnv* env,
}
/*
- SSDBG( descP, ("SOCKET", "ngetopt_bool_opt -> done when"
+ SSDBG( descP, ("SOCKET", "esock_getopt_bool_opt -> done when"
"\r\n: res: %d"
"\r\n: result: %T"
"\r\n", res, result) );
@@ -13777,13 +13828,13 @@ ERL_NIF_TERM ngetopt_bool_opt(ErlNifEnv* env,
}
-/* ngetopt_int_opt - get an integer option
+/* esock_getopt_int_opt - get an integer option
*/
static
-ERL_NIF_TERM ngetopt_int_opt(ErlNifEnv* env,
- ESockDescriptor* descP,
- int level,
- int opt)
+ERL_NIF_TERM esock_getopt_int_opt(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int level,
+ int opt)
{
ERL_NIF_TERM result;
int val;
@@ -13803,13 +13854,13 @@ ERL_NIF_TERM ngetopt_int_opt(ErlNifEnv* env,
-/* ngetopt_timeval_opt - get an timeval option
+/* esock_getopt_timeval_opt - get an timeval option
*/
static
-ERL_NIF_TERM ngetopt_timeval_opt(ErlNifEnv* env,
- ESockDescriptor* descP,
- int level,
- int opt)
+ERL_NIF_TERM esock_getopt_timeval_opt(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int level,
+ int opt)
{
ERL_NIF_TERM result;
struct timeval val;
@@ -13817,7 +13868,7 @@ ERL_NIF_TERM ngetopt_timeval_opt(ErlNifEnv* env,
int res;
SSDBG( descP,
- ("SOCKET", "ngetopt_timeval_opt -> entry with"
+ ("SOCKET", "esock_getopt_timeval_opt -> entry with"
"\r\n level: %d"
"\r\n opt: %d"
"\r\n", level, opt) );
@@ -13838,7 +13889,7 @@ ERL_NIF_TERM ngetopt_timeval_opt(ErlNifEnv* env,
}
SSDBG( descP,
- ("SOCKET", "ngetopt_timeval_opt -> done when"
+ ("SOCKET", "esock_getopt_timeval_opt -> done when"
"\r\n result: %T"
"\r\n", result) );
@@ -13847,7 +13898,7 @@ ERL_NIF_TERM ngetopt_timeval_opt(ErlNifEnv* env,
-/* ngetopt_str_opt - get an string option
+/* esock_getopt_str_opt - get an string option
*
* We provide the max size of the string. This is the
* size of the buffer we allocate for the value.
@@ -13856,11 +13907,11 @@ ERL_NIF_TERM ngetopt_timeval_opt(ErlNifEnv* env,
*/
#if defined(USE_GETOPT_STR_OPT)
static
-ERL_NIF_TERM ngetopt_str_opt(ErlNifEnv* env,
- ESockDescriptor* descP,
- int level,
- int opt,
- int max)
+ERL_NIF_TERM esock_getopt_str_opt(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ int level,
+ int opt,
+ int max)
{
ERL_NIF_TERM result;
char* val = MALLOC(max);
@@ -13868,7 +13919,7 @@ ERL_NIF_TERM ngetopt_str_opt(ErlNifEnv* env,
int res;
SSDBG( descP,
- ("SOCKET", "ngetopt_str_opt -> entry with"
+ ("SOCKET", "esock_getopt_str_opt -> entry with"
"\r\n level: %d"
"\r\n opt: %d"
"\r\n max: %d"
@@ -13885,7 +13936,7 @@ ERL_NIF_TERM ngetopt_str_opt(ErlNifEnv* env,
}
SSDBG( descP,
- ("SOCKET", "ngetopt_str_opt -> done when"
+ ("SOCKET", "esock_getopt_str_opt -> done when"
"\r\n result: %T"
"\r\n", result) );
@@ -13924,7 +13975,7 @@ ERL_NIF_TERM nif_sockname(ErlNifEnv* env,
/* Extract arguments and perform preliminary validation */
if ((argc != 1) ||
- !enif_get_resource(env, argv[0], sockets, (void**) &descP)) {
+ !ESOCK_GET_RESOURCE(env, argv[0], (void**) &descP)) {
return enif_make_badarg(env);
}
@@ -13936,7 +13987,7 @@ ERL_NIF_TERM nif_sockname(ErlNifEnv* env,
"\r\n Socket: %T"
"\r\n", descP->sock, argv[0]) );
- res = nsockname(env, descP);
+ res = esock_sockname(env, descP);
SSDBG( descP, ("SOCKET", "nif_sockname -> done with res = %T\r\n", res) );
@@ -13948,8 +13999,8 @@ ERL_NIF_TERM nif_sockname(ErlNifEnv* env,
#if !defined(__WIN32__)
static
-ERL_NIF_TERM nsockname(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_sockname(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
ESockAddress sa;
ESockAddress* saP = &sa;
@@ -13998,7 +14049,7 @@ ERL_NIF_TERM nif_peername(ErlNifEnv* env,
/* Extract arguments and perform preliminary validation */
if ((argc != 1) ||
- !enif_get_resource(env, argv[0], sockets, (void**) &descP)) {
+ !ESOCK_GET_RESOURCE(env, argv[0], (void**) &descP)) {
return enif_make_badarg(env);
}
@@ -14010,7 +14061,7 @@ ERL_NIF_TERM nif_peername(ErlNifEnv* env,
"\r\n Socket: %T"
"\r\n", descP->sock, argv[0]) );
- res = npeername(env, descP);
+ res = esock_peername(env, descP);
SSDBG( descP, ("SOCKET", "nif_peername -> done with res = %T\r\n", res) );
@@ -14022,8 +14073,8 @@ ERL_NIF_TERM nif_peername(ErlNifEnv* env,
#if !defined(__WIN32__)
static
-ERL_NIF_TERM npeername(ErlNifEnv* env,
- ESockDescriptor* descP)
+ERL_NIF_TERM esock_peername(ErlNifEnv* env,
+ ESockDescriptor* descP)
{
ESockAddress sa;
ESockAddress* saP = &sa;
@@ -14074,7 +14125,7 @@ ERL_NIF_TERM nif_cancel(ErlNifEnv* env,
sockRef = argv[0];
if ((argc != 3) ||
- !enif_get_resource(env, sockRef, sockets, (void**) &descP)) {
+ !ESOCK_GET_RESOURCE(env, sockRef, (void**) &descP)) {
return enif_make_badarg(env);
}
op = argv[1];
@@ -14089,7 +14140,7 @@ ERL_NIF_TERM nif_cancel(ErlNifEnv* env,
"\r\n opRef: %T"
"\r\n", descP->sock, op, opRef) );
- result = ncancel(env, descP, op, sockRef, opRef);
+ result = esock_cancel(env, descP, op, sockRef, opRef);
SSDBG( descP,
("SOCKET", "nif_cancel -> done with result: "
@@ -14103,11 +14154,11 @@ ERL_NIF_TERM nif_cancel(ErlNifEnv* env,
#if !defined(__WIN32__)
static
-ERL_NIF_TERM ncancel(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM op,
- ERL_NIF_TERM sockRef,
- ERL_NIF_TERM opRef)
+ERL_NIF_TERM esock_cancel(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM op,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM opRef)
{
/* <KOLLA>
*
@@ -14118,21 +14169,21 @@ ERL_NIF_TERM ncancel(ErlNifEnv* env,
* </KOLLA>
*/
if (COMPARE(op, esock_atom_connect) == 0) {
- return ncancel_connect(env, descP, opRef);
+ return esock_cancel_connect(env, descP, opRef);
} else if (COMPARE(op, esock_atom_accept) == 0) {
- return ncancel_accept(env, descP, sockRef, opRef);
+ return esock_cancel_accept(env, descP, sockRef, opRef);
} else if (COMPARE(op, esock_atom_send) == 0) {
- return ncancel_send(env, descP, sockRef, opRef);
+ return esock_cancel_send(env, descP, sockRef, opRef);
} else if (COMPARE(op, esock_atom_sendto) == 0) {
- return ncancel_send(env, descP, sockRef, opRef);
+ return esock_cancel_send(env, descP, sockRef, opRef);
} else if (COMPARE(op, esock_atom_sendmsg) == 0) {
- return ncancel_send(env, descP, sockRef, opRef);
+ return esock_cancel_send(env, descP, sockRef, opRef);
} else if (COMPARE(op, esock_atom_recv) == 0) {
- return ncancel_recv(env, descP, sockRef, opRef);
+ return esock_cancel_recv(env, descP, sockRef, opRef);
} else if (COMPARE(op, esock_atom_recvfrom) == 0) {
- return ncancel_recv(env, descP, sockRef, opRef);
+ return esock_cancel_recv(env, descP, sockRef, opRef);
} else if (COMPARE(op, esock_atom_recvmsg) == 0) {
- return ncancel_recv(env, descP, sockRef, opRef);
+ return esock_cancel_recv(env, descP, sockRef, opRef);
} else {
return esock_make_error(env, esock_atom_einval);
}
@@ -14140,20 +14191,20 @@ ERL_NIF_TERM ncancel(ErlNifEnv* env,
-/* *** ncancel_connect ***
+/* *** esock_cancel_connect ***
*
*
*/
static
-ERL_NIF_TERM ncancel_connect(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM opRef)
+ERL_NIF_TERM esock_cancel_connect(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM opRef)
{
- return ncancel_write_select(env, descP, opRef);
+ return esock_cancel_write_select(env, descP, opRef);
}
-/* *** ncancel_accept ***
+/* *** esock_cancel_accept ***
*
* We have two different cases:
* *) Its the current acceptor
@@ -14164,15 +14215,15 @@ ERL_NIF_TERM ncancel_connect(ErlNifEnv* env,
*
*/
static
-ERL_NIF_TERM ncancel_accept(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef,
- ERL_NIF_TERM opRef)
+ERL_NIF_TERM esock_cancel_accept(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM opRef)
{
ERL_NIF_TERM res;
SSDBG( descP,
- ("SOCKET", "ncancel_accept -> entry with"
+ ("SOCKET", "esock_cancel_accept -> entry with"
"\r\n opRef: %T"
"\r\n %s"
"\r\n", opRef,
@@ -14182,9 +14233,9 @@ ERL_NIF_TERM ncancel_accept(ErlNifEnv* env,
if (descP->currentAcceptorP != NULL) {
if (COMPARE(opRef, descP->currentAcceptor.ref) == 0) {
- res = ncancel_accept_current(env, descP, sockRef);
+ res = esock_cancel_accept_current(env, descP, sockRef);
} else {
- res = ncancel_accept_waiting(env, descP, opRef);
+ res = esock_cancel_accept_waiting(env, descP, opRef);
}
} else {
/* Or badarg? */
@@ -14194,7 +14245,7 @@ ERL_NIF_TERM ncancel_accept(ErlNifEnv* env,
MUNLOCK(descP->accMtx);
SSDBG( descP,
- ("SOCKET", "ncancel_accept -> done with result:"
+ ("SOCKET", "esock_cancel_accept -> done with result:"
"\r\n %T"
"\r\n", res) );
@@ -14207,27 +14258,27 @@ ERL_NIF_TERM ncancel_accept(ErlNifEnv* env,
* in the acceptor queue).
*/
static
-ERL_NIF_TERM ncancel_accept_current(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef)
+ERL_NIF_TERM esock_cancel_accept_current(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef)
{
ERL_NIF_TERM res;
- SSDBG( descP, ("SOCKET", "ncancel_accept_current -> entry\r\n") );
+ SSDBG( descP, ("SOCKET", "esock_cancel_accept_current -> entry\r\n") );
- DEMONP("ncancel_accept_current -> current acceptor",
+ DEMONP("esock_cancel_accept_current -> current acceptor",
env, descP, &descP->currentAcceptor.mon);
- res = ncancel_read_select(env, descP, descP->currentAcceptor.ref);
+ res = esock_cancel_read_select(env, descP, descP->currentAcceptor.ref);
SSDBG( descP, ("SOCKET",
- "ncancel_accept_current -> cancel res: %T\r\n", res) );
+ "esock_cancel_accept_current -> cancel res: %T\r\n", res) );
if (!activate_next_acceptor(env, descP, sockRef)) {
SSDBG( descP,
- ("SOCKET", "ncancel_accept_current -> no more writers\r\n") );
+ ("SOCKET", "esock_cancel_accept_current -> no more writers\r\n") );
- descP->state = SOCKET_STATE_LISTENING;
+ descP->state = ESOCK_STATE_LISTENING;
descP->currentAcceptorP = NULL;
descP->currentAcceptor.ref = esock_atom_undefined;
@@ -14235,7 +14286,7 @@ ERL_NIF_TERM ncancel_accept_current(ErlNifEnv* env,
esock_monitor_init(&descP->currentAcceptor.mon);
}
- SSDBG( descP, ("SOCKET", "ncancel_accept_current -> done with result:"
+ SSDBG( descP, ("SOCKET", "esock_cancel_accept_current -> done with result:"
"\r\n %T"
"\r\n", res) );
@@ -14247,9 +14298,9 @@ ERL_NIF_TERM ncancel_accept_current(ErlNifEnv* env,
* remove them from the acceptor queue.
*/
static
-ERL_NIF_TERM ncancel_accept_waiting(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM opRef)
+ERL_NIF_TERM esock_cancel_accept_waiting(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM opRef)
{
ErlNifPid caller;
@@ -14268,23 +14319,23 @@ ERL_NIF_TERM ncancel_accept_waiting(ErlNifEnv* env,
-/* *** ncancel_send ***
+/* *** esock_cancel_send ***
*
* Cancel a send operation.
* Its either the current writer or one of the waiting writers.
*/
static
-ERL_NIF_TERM ncancel_send(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef,
- ERL_NIF_TERM opRef)
+ERL_NIF_TERM esock_cancel_send(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM opRef)
{
ERL_NIF_TERM res;
MLOCK(descP->writeMtx);
SSDBG( descP,
- ("SOCKET", "ncancel_send -> entry with"
+ ("SOCKET", "esock_cancel_send -> entry with"
"\r\n opRef: %T"
"\r\n %s"
"\r\n", opRef,
@@ -14292,9 +14343,9 @@ ERL_NIF_TERM ncancel_send(ErlNifEnv* env,
if (descP->currentWriterP != NULL) {
if (COMPARE(opRef, descP->currentWriter.ref) == 0) {
- res = ncancel_send_current(env, descP, sockRef);
+ res = esock_cancel_send_current(env, descP, sockRef);
} else {
- res = ncancel_send_waiting(env, descP, opRef);
+ res = esock_cancel_send_waiting(env, descP, opRef);
}
} else {
/* Or badarg? */
@@ -14304,7 +14355,7 @@ ERL_NIF_TERM ncancel_send(ErlNifEnv* env,
MUNLOCK(descP->writeMtx);
SSDBG( descP,
- ("SOCKET", "ncancel_send -> done with result:"
+ ("SOCKET", "esock_cancel_send -> done with result:"
"\r\n %T"
"\r\n", res) );
@@ -14318,31 +14369,31 @@ ERL_NIF_TERM ncancel_send(ErlNifEnv* env,
* in the writer queue).
*/
static
-ERL_NIF_TERM ncancel_send_current(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef)
+ERL_NIF_TERM esock_cancel_send_current(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef)
{
ERL_NIF_TERM res;
- SSDBG( descP, ("SOCKET", "ncancel_send_current -> entry\r\n") );
+ SSDBG( descP, ("SOCKET", "esock_cancel_send_current -> entry\r\n") );
- DEMONP("ncancel_send_current -> current writer",
+ DEMONP("esock_cancel_send_current -> current writer",
env, descP, &descP->currentWriter.mon);
- res = ncancel_write_select(env, descP, descP->currentWriter.ref);
+ res = esock_cancel_write_select(env, descP, descP->currentWriter.ref);
SSDBG( descP,
- ("SOCKET", "ncancel_send_current -> cancel res: %T\r\n", res) );
+ ("SOCKET", "esock_cancel_send_current -> cancel res: %T\r\n", res) );
if (!activate_next_writer(env, descP, sockRef)) {
SSDBG( descP,
- ("SOCKET", "ncancel_send_current -> no more writers\r\n") );
+ ("SOCKET", "esock_cancel_send_current -> no more writers\r\n") );
descP->currentWriterP = NULL;
descP->currentWriter.ref = esock_atom_undefined;
enif_set_pid_undefined(&descP->currentWriter.pid);
esock_monitor_init(&descP->currentWriter.mon);
}
- SSDBG( descP, ("SOCKET", "ncancel_send_current -> done with result:"
+ SSDBG( descP, ("SOCKET", "esock_cancel_send_current -> done with result:"
"\r\n %T"
"\r\n", res) );
@@ -14354,9 +14405,9 @@ ERL_NIF_TERM ncancel_send_current(ErlNifEnv* env,
* remove them from the writer queue.
*/
static
-ERL_NIF_TERM ncancel_send_waiting(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM opRef)
+ERL_NIF_TERM esock_cancel_send_waiting(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM opRef)
{
ErlNifPid caller;
@@ -14375,23 +14426,23 @@ ERL_NIF_TERM ncancel_send_waiting(ErlNifEnv* env,
-/* *** ncancel_recv ***
+/* *** esock_cancel_recv ***
*
* Cancel a read operation.
* Its either the current reader or one of the waiting readers.
*/
static
-ERL_NIF_TERM ncancel_recv(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef,
- ERL_NIF_TERM opRef)
+ERL_NIF_TERM esock_cancel_recv(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ERL_NIF_TERM opRef)
{
ERL_NIF_TERM res;
MLOCK(descP->readMtx);
SSDBG( descP,
- ("SOCKET", "ncancel_recv -> entry with"
+ ("SOCKET", "esock_cancel_recv -> entry with"
"\r\n opRef: %T"
"\r\n %s"
"\r\n", opRef,
@@ -14399,9 +14450,9 @@ ERL_NIF_TERM ncancel_recv(ErlNifEnv* env,
if (descP->currentReaderP != NULL) {
if (COMPARE(opRef, descP->currentReader.ref) == 0) {
- res = ncancel_recv_current(env, descP, sockRef);
+ res = esock_cancel_recv_current(env, descP, sockRef);
} else {
- res = ncancel_recv_waiting(env, descP, opRef);
+ res = esock_cancel_recv_waiting(env, descP, opRef);
}
} else {
/* Or badarg? */
@@ -14411,7 +14462,7 @@ ERL_NIF_TERM ncancel_recv(ErlNifEnv* env,
MUNLOCK(descP->readMtx);
SSDBG( descP,
- ("SOCKET", "ncancel_recv -> done with result:"
+ ("SOCKET", "esock_cancel_recv -> done with result:"
"\r\n %T"
"\r\n", res) );
@@ -14424,31 +14475,31 @@ ERL_NIF_TERM ncancel_recv(ErlNifEnv* env,
* in the reader queue).
*/
static
-ERL_NIF_TERM ncancel_recv_current(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef)
+ERL_NIF_TERM esock_cancel_recv_current(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef)
{
ERL_NIF_TERM res;
- SSDBG( descP, ("SOCKET", "ncancel_recv_current -> entry\r\n") );
+ SSDBG( descP, ("SOCKET", "esock_cancel_recv_current -> entry\r\n") );
- DEMONP("ncancel_recv_current -> current reader",
+ DEMONP("esock_cancel_recv_current -> current reader",
env, descP, &descP->currentReader.mon);
- res = ncancel_read_select(env, descP, descP->currentReader.ref);
+ res = esock_cancel_read_select(env, descP, descP->currentReader.ref);
SSDBG( descP,
- ("SOCKET", "ncancel_recv_current -> cancel res: %T\r\n", res) );
+ ("SOCKET", "esock_cancel_recv_current -> cancel res: %T\r\n", res) );
if (!activate_next_reader(env, descP, sockRef)) {
SSDBG( descP,
- ("SOCKET", "ncancel_recv_current -> no more readers\r\n") );
+ ("SOCKET", "esock_cancel_recv_current -> no more readers\r\n") );
descP->currentReaderP = NULL;
descP->currentReader.ref = esock_atom_undefined;
enif_set_pid_undefined(&descP->currentReader.pid);
esock_monitor_init(&descP->currentReader.mon);
}
- SSDBG( descP, ("SOCKET", "ncancel_recv_current -> done with result:"
+ SSDBG( descP, ("SOCKET", "esock_cancel_recv_current -> done with result:"
"\r\n %T"
"\r\n", res) );
@@ -14460,9 +14511,9 @@ ERL_NIF_TERM ncancel_recv_current(ErlNifEnv* env,
* remove them from the reader queue.
*/
static
-ERL_NIF_TERM ncancel_recv_waiting(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM opRef)
+ERL_NIF_TERM esock_cancel_recv_waiting(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM opRef)
{
ErlNifPid caller;
@@ -14482,33 +14533,33 @@ ERL_NIF_TERM ncancel_recv_waiting(ErlNifEnv* env,
static
-ERL_NIF_TERM ncancel_read_select(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM opRef)
+ERL_NIF_TERM esock_cancel_read_select(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM opRef)
{
- return ncancel_mode_select(env, descP, opRef,
- ERL_NIF_SELECT_READ,
- ERL_NIF_SELECT_READ_CANCELLED);
+ return esock_cancel_mode_select(env, descP, opRef,
+ ERL_NIF_SELECT_READ,
+ ERL_NIF_SELECT_READ_CANCELLED);
}
static
-ERL_NIF_TERM ncancel_write_select(ErlNifEnv* env,
+ERL_NIF_TERM esock_cancel_write_select(ErlNifEnv* env,
ESockDescriptor* descP,
ERL_NIF_TERM opRef)
{
- return ncancel_mode_select(env, descP, opRef,
- ERL_NIF_SELECT_WRITE,
- ERL_NIF_SELECT_WRITE_CANCELLED);
+ return esock_cancel_mode_select(env, descP, opRef,
+ ERL_NIF_SELECT_WRITE,
+ ERL_NIF_SELECT_WRITE_CANCELLED);
}
static
-ERL_NIF_TERM ncancel_mode_select(ErlNifEnv* env,
- ESockDescriptor* descP,
- ERL_NIF_TERM opRef,
- int smode,
- int rmode)
+ERL_NIF_TERM esock_cancel_mode_select(ErlNifEnv* env,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM opRef,
+ int smode,
+ int rmode)
{
int selectRes = esock_select_cancel(env, descP->sock, smode, descP);
@@ -14520,7 +14571,8 @@ ERL_NIF_TERM ncancel_mode_select(ErlNifEnv* env,
return esock_make_error(env, esock_atom_select_sent);
} else {
/* Stopped? */
- SSDBG( descP, ("SOCKET", "ncancel_mode_select -> failed: %d (0x%lX)"
+ SSDBG( descP, ("SOCKET",
+ "esock_cancel_mode_select -> failed: %d (0x%lX)"
"\r\n", selectRes, selectRes) );
return esock_make_error(env, esock_atom_einval);
}
@@ -15358,7 +15410,7 @@ ERL_NIF_TERM recv_check_fail_closed(ErlNifEnv* env,
*/
descP->closeLocal = FALSE;
- descP->state = SOCKET_STATE_CLOSING;
+ descP->state = ESOCK_STATE_CLOSING;
recv_error_current_reader(env, descP, sockRef, res);
@@ -17378,11 +17430,11 @@ BOOLEAN_T decode_native_get_opt(ErlNifEnv* env, ERL_NIF_TERM eVal,
if (COMPARE(nativeOptT[1], atom_int) == 0) {
SGDBG( ("SOCKET", "decode_native_get_opt -> int\r\n") );
- *valueType = SOCKET_OPT_VALUE_TYPE_INT;
+ *valueType = ESOCK_OPT_VALUE_TYPE_INT;
*valueSz = sizeof(int); // Just to be sure
} else if (COMPARE(nativeOptT[1], atom_bool) == 0) {
SGDBG( ("SOCKET", "decode_native_get_opt -> bool\r\n") );
- *valueType = SOCKET_OPT_VALUE_TYPE_BOOL;
+ *valueType = ESOCK_OPT_VALUE_TYPE_BOOL;
*valueSz = sizeof(int); // Just to be sure
} else {
return FALSE;
@@ -17390,7 +17442,7 @@ BOOLEAN_T decode_native_get_opt(ErlNifEnv* env, ERL_NIF_TERM eVal,
} else if (IS_NUM(env, nativeOptT[1])) {
if (GET_INT(env, nativeOptT[1], valueSz)) {
SGDBG( ("SOCKET", "decode_native_get_opt -> unspec\r\n") );
- *valueType = SOCKET_OPT_VALUE_TYPE_UNSPEC;
+ *valueType = ESOCK_OPT_VALUE_TYPE_UNSPEC;
} else {
return FALSE;
}
@@ -17457,7 +17509,7 @@ ESockDescriptor* alloc_descriptor(SOCKET sock, HANDLE event)
{
ESockDescriptor* descP;
- if ((descP = enif_alloc_resource(sockets, sizeof(ESockDescriptor))) != NULL) {
+ if ((descP = enif_alloc_resource(esocks, sizeof(ESockDescriptor))) != NULL) {
char buf[64]; /* Buffer used for building the mutex name */
descP->pattern = ESOCK_DESC_PATTERN_CREATED;
@@ -17516,13 +17568,13 @@ ESockDescriptor* alloc_descriptor(SOCKET sock, HANDLE event)
sprintf(buf, "esock[cfg,%d]", sock);
descP->cfgMtx = MCREATE(buf);
- descP->rBufSz = SOCKET_RECV_BUFFER_SIZE_DEFAULT;
+ descP->rBufSz = ESOCK_RECV_BUFFER_SIZE_DEFAULT;
descP->rNum = 0;
descP->rNumCnt = 0;
- descP->rCtrlSz = SOCKET_RECV_CTRL_BUFFER_SIZE_DEFAULT;
- descP->wCtrlSz = SOCKET_SEND_CTRL_BUFFER_SIZE_DEFAULT;
+ descP->rCtrlSz = ESOCK_RECV_CTRL_BUFFER_SIZE_DEFAULT;
+ descP->wCtrlSz = ESOCK_SEND_CTRL_BUFFER_SIZE_DEFAULT;
descP->iow = FALSE;
- descP->dbg = SOCKET_DEBUG_DEFAULT;
+ descP->dbg = ESOCK_DEBUG_DEFAULT;
descP->sock = sock;
descP->event = event;
@@ -17649,17 +17701,17 @@ static
BOOLEAN_T edomain2domain(int edomain, int* domain)
{
switch (edomain) {
- case SOCKET_DOMAIN_INET:
+ case ESOCK_DOMAIN_INET:
*domain = AF_INET;
break;
#if defined(HAVE_IN6) && defined(AF_INET6)
- case SOCKET_DOMAIN_INET6:
+ case ESOCK_DOMAIN_INET6:
*domain = AF_INET6;
break;
#endif
#ifdef HAVE_SYS_UN_H
- case SOCKET_DOMAIN_LOCAL:
+ case ESOCK_DOMAIN_LOCAL:
*domain = AF_UNIX;
break;
#endif
@@ -17681,20 +17733,20 @@ static
BOOLEAN_T etype2type(int etype, int* type)
{
switch (etype) {
- case SOCKET_TYPE_STREAM:
+ case ESOCK_TYPE_STREAM:
*type = SOCK_STREAM;
break;
- case SOCKET_TYPE_DGRAM:
+ case ESOCK_TYPE_DGRAM:
*type = SOCK_DGRAM;
break;
- case SOCKET_TYPE_RAW:
+ case ESOCK_TYPE_RAW:
*type = SOCK_RAW;
break;
#ifdef HAVE_SCTP
- case SOCKET_TYPE_SEQPACKET:
+ case ESOCK_TYPE_SEQPACKET:
*type = SOCK_SEQPACKET;
break;
#endif
@@ -17726,33 +17778,33 @@ BOOLEAN_T eproto2proto(ErlNifEnv* env,
}
switch (ep) {
- case SOCKET_PROTOCOL_DEFAULT:
+ case ESOCK_PROTOCOL_DEFAULT:
*proto = 0; // default - note that _IP also has the value 0...
break;
- case SOCKET_PROTOCOL_IP:
+ case ESOCK_PROTOCOL_IP:
*proto = IPPROTO_IP;
break;
- case SOCKET_PROTOCOL_TCP:
+ case ESOCK_PROTOCOL_TCP:
*proto = IPPROTO_TCP;
break;
- case SOCKET_PROTOCOL_UDP:
+ case ESOCK_PROTOCOL_UDP:
*proto = IPPROTO_UDP;
break;
#if defined(HAVE_SCTP)
- case SOCKET_PROTOCOL_SCTP:
+ case ESOCK_PROTOCOL_SCTP:
*proto = IPPROTO_SCTP;
break;
#endif
- case SOCKET_PROTOCOL_ICMP:
+ case ESOCK_PROTOCOL_ICMP:
*proto = IPPROTO_ICMP;
break;
- case SOCKET_PROTOCOL_IGMP:
+ case ESOCK_PROTOCOL_IGMP:
*proto = IPPROTO_IGMP;
break;
@@ -17861,47 +17913,47 @@ BOOLEAN_T esendflags2sendflags(unsigned int eflags, int* flags)
return TRUE;
}
- for (ef = SOCKET_SEND_FLAG_LOW; ef <= SOCKET_SEND_FLAG_HIGH; ef++) {
+ for (ef = ESOCK_SEND_FLAG_LOW; ef <= ESOCK_SEND_FLAG_HIGH; ef++) {
switch (ef) {
#if defined(MSG_CONFIRM)
- case SOCKET_SEND_FLAG_CONFIRM:
- if ((1 << SOCKET_SEND_FLAG_CONFIRM) & eflags)
+ case ESOCK_SEND_FLAG_CONFIRM:
+ if ((1 << ESOCK_SEND_FLAG_CONFIRM) & eflags)
tmp |= MSG_CONFIRM;
break;
#endif
#if defined(MSG_DONTROUTE)
- case SOCKET_SEND_FLAG_DONTROUTE:
- if ((1 << SOCKET_SEND_FLAG_DONTROUTE) & eflags)
+ case ESOCK_SEND_FLAG_DONTROUTE:
+ if ((1 << ESOCK_SEND_FLAG_DONTROUTE) & eflags)
tmp |= MSG_DONTROUTE;
break;
#endif
#if defined(MSG_EOR)
- case SOCKET_SEND_FLAG_EOR:
- if ((1 << SOCKET_SEND_FLAG_EOR) & eflags)
+ case ESOCK_SEND_FLAG_EOR:
+ if ((1 << ESOCK_SEND_FLAG_EOR) & eflags)
tmp |= MSG_EOR;
break;
#endif
#if defined(MSG_MORE)
- case SOCKET_SEND_FLAG_MORE:
- if ((1 << SOCKET_SEND_FLAG_MORE) & eflags)
+ case ESOCK_SEND_FLAG_MORE:
+ if ((1 << ESOCK_SEND_FLAG_MORE) & eflags)
tmp |= MSG_MORE;
break;
#endif
#if defined(MSG_NOSIGNAL)
- case SOCKET_SEND_FLAG_NOSIGNAL:
- if ((1 << SOCKET_SEND_FLAG_NOSIGNAL) & eflags)
+ case ESOCK_SEND_FLAG_NOSIGNAL:
+ if ((1 << ESOCK_SEND_FLAG_NOSIGNAL) & eflags)
tmp |= MSG_NOSIGNAL;
break;
#endif
#if defined(MSG_OOB)
- case SOCKET_SEND_FLAG_OOB:
- if ((1 << SOCKET_SEND_FLAG_OOB) & eflags)
+ case ESOCK_SEND_FLAG_OOB:
+ if ((1 << ESOCK_SEND_FLAG_OOB) & eflags)
tmp |= MSG_OOB;
break;
#endif
@@ -17937,7 +17989,7 @@ BOOLEAN_T erecvflags2recvflags(unsigned int eflags, int* flags)
return TRUE;
}
- for (ef = SOCKET_RECV_FLAG_LOW; ef <= SOCKET_RECV_FLAG_HIGH; ef++) {
+ for (ef = ESOCK_RECV_FLAG_LOW; ef <= ESOCK_RECV_FLAG_HIGH; ef++) {
SGDBG( ("SOCKET", "erecvflags2recvflags -> iteration"
"\r\n ef: %d"
@@ -17946,22 +17998,22 @@ BOOLEAN_T erecvflags2recvflags(unsigned int eflags, int* flags)
switch (ef) {
#if defined(MSG_CMSG_CLOEXEC)
- case SOCKET_RECV_FLAG_CMSG_CLOEXEC:
- if ((1 << SOCKET_RECV_FLAG_CMSG_CLOEXEC) & eflags)
+ case ESOCK_RECV_FLAG_CMSG_CLOEXEC:
+ if ((1 << ESOCK_RECV_FLAG_CMSG_CLOEXEC) & eflags)
tmp |= MSG_CMSG_CLOEXEC;
break;
#endif
#if defined(MSG_ERRQUEUE)
- case SOCKET_RECV_FLAG_ERRQUEUE:
- if ((1 << SOCKET_RECV_FLAG_ERRQUEUE) & eflags)
+ case ESOCK_RECV_FLAG_ERRQUEUE:
+ if ((1 << ESOCK_RECV_FLAG_ERRQUEUE) & eflags)
tmp |= MSG_ERRQUEUE;
break;
#endif
#if defined(MSG_OOB)
- case SOCKET_RECV_FLAG_OOB:
- if ((1 << SOCKET_RECV_FLAG_OOB) & eflags)
+ case ESOCK_RECV_FLAG_OOB:
+ if ((1 << ESOCK_RECV_FLAG_OOB) & eflags)
tmp |= MSG_OOB;
break;
#endif
@@ -17974,15 +18026,15 @@ BOOLEAN_T erecvflags2recvflags(unsigned int eflags, int* flags)
* </KOLLA>
*/
#if defined(MSG_PEEK)
- case SOCKET_RECV_FLAG_PEEK:
- if ((1 << SOCKET_RECV_FLAG_PEEK) & eflags)
+ case ESOCK_RECV_FLAG_PEEK:
+ if ((1 << ESOCK_RECV_FLAG_PEEK) & eflags)
tmp |= MSG_PEEK;
break;
#endif
#if defined(MSG_TRUNC)
- case SOCKET_RECV_FLAG_TRUNC:
- if ((1 << SOCKET_RECV_FLAG_TRUNC) & eflags)
+ case ESOCK_RECV_FLAG_TRUNC:
+ if ((1 << ESOCK_RECV_FLAG_TRUNC) & eflags)
tmp |= MSG_TRUNC;
break;
#endif
@@ -18008,15 +18060,15 @@ static
BOOLEAN_T ehow2how(unsigned int ehow, int* how)
{
switch (ehow) {
- case SOCKET_SHUTDOWN_HOW_RD:
+ case ESOCK_SHUTDOWN_HOW_RD:
*how = SHUT_RD;
break;
- case SOCKET_SHUTDOWN_HOW_WR:
+ case ESOCK_SHUTDOWN_HOW_WR:
*how = SHUT_WR;
break;
- case SOCKET_SHUTDOWN_HOW_RDWR:
+ case ESOCK_SHUTDOWN_HOW_RDWR:
*how = SHUT_RDWR;
break;
@@ -18062,7 +18114,7 @@ BOOLEAN_T ecommand2command(ErlNifEnv* env,
return FALSE;
}
if (COMPARE(ecmd, esock_atom_debug) == 0) {
- *command = SOCKET_CMD_DEBUG;
+ *command = ESOCK_CMD_DEBUG;
} else {
SGDBG( ("SOCKET", "ecommand2command -> unknown command %T\r\n", ecmd) );
return FALSE;
@@ -18884,11 +18936,11 @@ static void free_request_queue(ESockRequestQueue* q)
}
/* =========================================================================
- * socket_dtor - Callback function for resource destructor
+ * esock_dtor - Callback function for resource destructor
*
*/
static
-void socket_dtor(ErlNifEnv* env, void* obj)
+void esock_dtor(ErlNifEnv* env, void* obj)
{
#if !defined(__WIN32__)
ESockDescriptor* descP = (ESockDescriptor*) obj;
@@ -18915,14 +18967,14 @@ void socket_dtor(ErlNifEnv* env, void* obj)
free_request_queue(&descP->writersQ);
free_request_queue(&descP->acceptorsQ);
- descP->state = SOCKET_STATE_DTOR;
+ descP->state = ESOCK_STATE_DTOR;
descP->pattern = ESOCK_DESC_PATTERN_DTOR;
#endif
}
/* =========================================================================
- * socket_stop - Callback function for resource stop
+ * esock_stop - Callback function for resource stop
*
* When the socket is stopped, we need to inform:
*
@@ -18937,14 +18989,14 @@ void socket_dtor(ErlNifEnv* env, void* obj)
*
*/
static
-void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call)
+void esock_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call)
{
#if !defined(__WIN32__)
ESockDescriptor* descP = (ESockDescriptor*) obj;
ERL_NIF_TERM sockRef;
SSDBG( descP,
- ("SOCKET", "socket_stop -> entry when %s"
+ ("SOCKET", "esock_stop -> entry when %s"
"\r\n sock: %d (%d)"
"\r\n",
((is_direct_call) ? "called" : "scheduled"), descP->sock, fd) );
@@ -18957,7 +19009,7 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call)
MLOCK(descP->cfgMtx);
if (!is_direct_call) MLOCK(descP->closeMtx);
- SSDBG( descP, ("SOCKET", "socket_stop -> "
+ SSDBG( descP, ("SOCKET", "esock_stop -> "
"[%d, %T] all mutex(s) locked when counters:"
"\r\n writePkgCnt: %u"
"\r\n writeByteCnt: %u"
@@ -18981,7 +19033,7 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call)
descP->readWaits) );
sockRef = enif_make_resource(env, descP);
- descP->state = SOCKET_STATE_CLOSING; // Just in case...???
+ descP->state = ESOCK_STATE_CLOSING; // Just in case...???
descP->isReadable = FALSE;
descP->isWritable = FALSE;
@@ -18992,7 +19044,7 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call)
* there is no point to demonitor. Also, we do not actually
* have a monitor in that case...
*/
- DEMONP("socket_stop -> ctrl", env, descP, &descP->ctrlMon);
+ DEMONP("esock_stop -> ctrl", env, descP, &descP->ctrlMon);
@@ -19009,12 +19061,12 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call)
* writers waiting.
*/
- socket_stop_handle_current(env,
- "writer",
- descP, sockRef, &descP->currentWriter);
+ esock_stop_handle_current(env,
+ "writer",
+ descP, sockRef, &descP->currentWriter);
/* And also deal with the waiting writers (in the same way) */
- SSDBG( descP, ("SOCKET", "socket_stop -> handle waiting writer(s)\r\n") );
+ SSDBG( descP, ("SOCKET", "esock_stop -> handle waiting writer(s)\r\n") );
inform_waiting_procs(env, "writer",
descP, sockRef, &descP->writersQ, TRUE, atom_closed);
}
@@ -19033,12 +19085,12 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call)
* readers waiting.
*/
- socket_stop_handle_current(env,
- "reader",
- descP, sockRef, &descP->currentReader);
+ esock_stop_handle_current(env,
+ "reader",
+ descP, sockRef, &descP->currentReader);
/* And also deal with the waiting readers (in the same way) */
- SSDBG( descP, ("SOCKET", "socket_stop -> handle waiting reader(s)\r\n") );
+ SSDBG( descP, ("SOCKET", "esock_stop -> handle waiting reader(s)\r\n") );
inform_waiting_procs(env, "reader",
descP, sockRef, &descP->readersQ, TRUE, atom_closed);
}
@@ -19058,12 +19110,12 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call)
* acceptors waiting.
*/
- socket_stop_handle_current(env,
- "acceptor",
- descP, sockRef, &descP->currentAcceptor);
-
+ esock_stop_handle_current(env,
+ "acceptor",
+ descP, sockRef, &descP->currentAcceptor);
+
/* And also deal with the waiting acceptors (in the same way) */
- SSDBG( descP, ("SOCKET", "socket_stop -> handle waiting acceptor(s)\r\n") );
+ SSDBG( descP, ("SOCKET", "esock_stop -> handle waiting acceptor(s)\r\n") );
inform_waiting_procs(env, "acceptor",
descP, sockRef, &descP->acceptorsQ, TRUE, atom_closed);
}
@@ -19087,7 +19139,7 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call)
esock_send_close_msg(env, descP, &descP->closerPid);
- DEMONP("socket_stop -> closer", env, descP, &descP->closerMon);
+ DEMONP("esock_stop -> closer", env, descP, &descP->closerMon);
} else {
@@ -19096,14 +19148,14 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call)
*/
if (descP->closeEnv != NULL)
- esock_free_env("socket_stop - close-env", descP->closeEnv);
+ esock_free_env("esock_stop - close-env", descP->closeEnv);
}
}
}
- SSDBG( descP, ("SOCKET", "socket_stop -> unlock all mutex(s)\r\n") );
+ SSDBG( descP, ("SOCKET", "esock_stop -> unlock all mutex(s)\r\n") );
if (!is_direct_call) MUNLOCK(descP->closeMtx);
MUNLOCK(descP->cfgMtx);
@@ -19112,33 +19164,33 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call)
MUNLOCK(descP->writeMtx);
SSDBG( descP,
- ("SOCKET", "socket_stop -> done (%d, %d)\r\n", descP->sock, fd) );
+ ("SOCKET", "esock_stop -> done (%d, %d)\r\n", descP->sock, fd) );
#endif // if !defined(__WIN32__)
}
-/* *** socket_stop_handle_current ***
+/* *** esock_stop_handle_current ***
*
* Handle current requestor (reader, writer or acceptor) during
* socket stop.
*/
#if !defined(__WIN32__)
static
-void socket_stop_handle_current(ErlNifEnv* env,
- const char* role,
- ESockDescriptor* descP,
- ERL_NIF_TERM sockRef,
- ESockRequestor* reqP)
+void esock_stop_handle_current(ErlNifEnv* env,
+ const char* role,
+ ESockDescriptor* descP,
+ ERL_NIF_TERM sockRef,
+ ESockRequestor* reqP)
{
- SSDBG( descP, ("SOCKET", "socket_stop -> handle current %s\r\n", role) );
+ SSDBG( descP, ("SOCKET", "esock_stop -> handle current %s\r\n", role) );
- DEMONP("socket_stop_handle_current", env, descP, &reqP->mon);
+ DEMONP("esock_stop_handle_current", env, descP, &reqP->mon);
if (COMPARE_PIDS(&descP->closerPid, &reqP->pid) != 0) {
- SSDBG( descP, ("SOCKET", "socket_stop_handle_current -> "
+ SSDBG( descP, ("SOCKET", "esock_stop_handle_current -> "
"send abort message to current %s %T\r\n",
role, reqP->pid) );
@@ -19223,21 +19275,21 @@ void inform_waiting_procs(ErlNifEnv* env,
/* =========================================================================
- * socket_down - Callback function for resource down (monitored processes)
+ * esock_down - Callback function for resource down (monitored processes)
*
*/
static
-void socket_down(ErlNifEnv* env,
- void* obj,
- const ErlNifPid* pid,
- const ErlNifMonitor* mon)
+void esock_down(ErlNifEnv* env,
+ void* obj,
+ const ErlNifPid* pid,
+ const ErlNifMonitor* mon)
{
#if !defined(__WIN32__)
ESockDescriptor* descP = (ESockDescriptor*) obj;
int sres;
ERL_NIF_TERM sockRef;
- SSDBG( descP, ("SOCKET", "socket_down -> entry with"
+ SSDBG( descP, ("SOCKET", "esock_down -> entry with"
"\r\n sock: %d"
"\r\n pid: %T"
"\r\n Close: %s (%s)"
@@ -19255,9 +19307,9 @@ void socket_down(ErlNifEnv* env,
*/
SSDBG( descP,
- ("SOCKET", "socket_down -> controlling process exit\r\n") );
+ ("SOCKET", "esock_down -> controlling process exit\r\n") );
- descP->state = SOCKET_STATE_CLOSING;
+ descP->state = ESOCK_STATE_CLOSING;
descP->closeLocal = TRUE;
descP->closerPid = *pid;
MON_INIT(&descP->closerMon);
@@ -19269,10 +19321,10 @@ void socket_down(ErlNifEnv* env,
/* We are done - we can finalize (socket close) directly */
SSDBG( descP,
("SOCKET",
- "socket_down -> [%d] stop called\r\n", descP->sock) );
+ "esock_down -> [%d] stop called\r\n", descP->sock) );
dec_socket(descP->domain, descP->type, descP->protocol);
- descP->state = SOCKET_STATE_CLOSED;
+ descP->state = ESOCK_STATE_CLOSED;
/* And finally close the socket.
* Since we close the socket because of an exiting owner,
@@ -19295,7 +19347,7 @@ void socket_down(ErlNifEnv* env,
descP->sock = INVALID_SOCKET;
descP->event = INVALID_EVENT;
- descP->state = SOCKET_STATE_CLOSED;
+ descP->state = ESOCK_STATE_CLOSED;
} else if (sres & ERL_NIF_SELECT_STOP_SCHEDULED) {
@@ -19306,7 +19358,7 @@ void socket_down(ErlNifEnv* env,
*/
SSDBG( descP,
("SOCKET",
- "socket_down -> [%d] stop scheduled\r\n",
+ "esock_down -> [%d] stop scheduled\r\n",
descP->sock) );
dec_socket(descP->domain, descP->type, descP->protocol);
@@ -19346,9 +19398,9 @@ void socket_down(ErlNifEnv* env,
* The same goes for the monitor (connMon).
*/
- descP->state = SOCKET_STATE_OPEN; /* restore state */
+ descP->state = ESOCK_STATE_OPEN; /* restore state */
enif_set_pid_undefined(&descP->connPid);
- DEMONP("socket_down -> connector",
+ DEMONP("esock_down -> connector",
env, descP, &descP->connMon);
} else {
@@ -19359,43 +19411,43 @@ void socket_down(ErlNifEnv* env,
*
*/
- SSDBG( descP, ("SOCKET", "socket_down -> other process term\r\n") );
+ SSDBG( descP, ("SOCKET", "esock_down -> other process term\r\n") );
sockRef = enif_make_resource(env, descP);
MLOCK(descP->accMtx);
if (descP->currentAcceptorP != NULL)
- socket_down_acceptor(env, descP, sockRef, pid);
+ esock_down_acceptor(env, descP, sockRef, pid);
MUNLOCK(descP->accMtx);
MLOCK(descP->writeMtx);
if (descP->currentWriterP != NULL)
- socket_down_writer(env, descP, sockRef, pid);
+ esock_down_writer(env, descP, sockRef, pid);
MUNLOCK(descP->writeMtx);
MLOCK(descP->readMtx);
if (descP->currentReaderP != NULL)
- socket_down_reader(env, descP, sockRef, pid);
+ esock_down_reader(env, descP, sockRef, pid);
MUNLOCK(descP->readMtx);
}
}
- SSDBG( descP, ("SOCKET", "socket_down -> done\r\n") );
+ SSDBG( descP, ("SOCKET", "esock_down -> done\r\n") );
#endif // if !defined(__WIN32__)
}
-/* *** socket_down_acceptor ***
+/* *** esock_down_acceptor ***
*
* Check and then handle a downed acceptor process.
*
*/
#if !defined(__WIN32__)
static
-void socket_down_acceptor(ErlNifEnv* env,
+void esock_down_acceptor(ErlNifEnv* env,
ESockDescriptor* descP,
ERL_NIF_TERM sockRef,
const ErlNifPid* pid)
@@ -19403,15 +19455,15 @@ void socket_down_acceptor(ErlNifEnv* env,
if (COMPARE_PIDS(&descP->currentAcceptor.pid, pid) == 0) {
SSDBG( descP, ("SOCKET",
- "socket_down_acceptor -> "
+ "esock_down_acceptor -> "
"current acceptor - try activate next\r\n") );
if (!activate_next_acceptor(env, descP, sockRef)) {
SSDBG( descP,
- ("SOCKET", "socket_down_acceptor -> no more writers\r\n") );
+ ("SOCKET", "esock_down_acceptor -> no more writers\r\n") );
- descP->state = SOCKET_STATE_LISTENING;
+ descP->state = ESOCK_STATE_LISTENING;
descP->currentAcceptorP = NULL;
descP->currentAcceptor.ref = esock_atom_undefined;
@@ -19424,7 +19476,7 @@ void socket_down_acceptor(ErlNifEnv* env,
/* Maybe unqueue one of the waiting acceptors */
SSDBG( descP, ("SOCKET",
- "socket_down_acceptor -> "
+ "esock_down_acceptor -> "
"not current acceptor - maybe a waiting acceptor\r\n") );
acceptor_unqueue(env, descP, pid);
@@ -19434,13 +19486,13 @@ void socket_down_acceptor(ErlNifEnv* env,
-/* *** socket_down_writer ***
+/* *** esock_down_writer ***
*
* Check and then handle a downed writer process.
*
*/
static
-void socket_down_writer(ErlNifEnv* env,
+void esock_down_writer(ErlNifEnv* env,
ESockDescriptor* descP,
ERL_NIF_TERM sockRef,
const ErlNifPid* pid)
@@ -19448,12 +19500,12 @@ void socket_down_writer(ErlNifEnv* env,
if (COMPARE_PIDS(&descP->currentWriter.pid, pid) == 0) {
SSDBG( descP, ("SOCKET",
- "socket_down_writer -> "
+ "esock_down_writer -> "
"current writer - try activate next\r\n") );
if (!activate_next_writer(env, descP, sockRef)) {
SSDBG( descP, ("SOCKET",
- "socket_down_writer -> no active writer\r\n") );
+ "esock_down_writer -> no active writer\r\n") );
descP->currentWriterP = NULL;
descP->currentWriter.ref = esock_atom_undefined;
enif_set_pid_undefined(&descP->currentWriter.pid);
@@ -19465,7 +19517,7 @@ void socket_down_writer(ErlNifEnv* env,
/* Maybe unqueue one of the waiting writer(s) */
SSDBG( descP, ("SOCKET",
- "socket_down_writer -> "
+ "esock_down_writer -> "
"not current writer - maybe a waiting writer\r\n") );
writer_unqueue(env, descP, pid);
@@ -19475,13 +19527,13 @@ void socket_down_writer(ErlNifEnv* env,
-/* *** socket_down_reader ***
+/* *** esock_down_reader ***
*
* Check and then handle a downed reader process.
*
*/
static
-void socket_down_reader(ErlNifEnv* env,
+void esock_down_reader(ErlNifEnv* env,
ESockDescriptor* descP,
ERL_NIF_TERM sockRef,
const ErlNifPid* pid)
@@ -19489,12 +19541,13 @@ void socket_down_reader(ErlNifEnv* env,
if (COMPARE_PIDS(&descP->currentReader.pid, pid) == 0) {
SSDBG( descP, ("SOCKET",
- "socket_down_reader -> "
+ "esock_down_reader -> "
"current reader - try activate next\r\n") );
if (!activate_next_reader(env, descP, sockRef)) {
SSDBG( descP,
- ("SOCKET", "ncancel_recv_current -> no more readers\r\n") );
+ ("SOCKET",
+ "esock_down_reader -> no more readers\r\n") );
descP->currentReaderP = NULL;
descP->currentReader.ref = esock_atom_undefined;
enif_set_pid_undefined(&descP->currentReader.pid);
@@ -19506,7 +19559,7 @@ void socket_down_reader(ErlNifEnv* env,
/* Maybe unqueue one of the waiting reader(s) */
SSDBG( descP, ("SOCKET",
- "socket_down_reader -> "
+ "esock_down_reader -> "
"not current reader - maybe a waiting reader\r\n") );
reader_unqueue(env, descP, pid);
@@ -19522,7 +19575,7 @@ void socket_down_reader(ErlNifEnv* env,
*/
static
-ErlNifFunc socket_funcs[] =
+ErlNifFunc esock_funcs[] =
{
// Some utility and support functions
{"nif_info", 0, nif_info, 0},
@@ -19574,7 +19627,7 @@ BOOLEAN_T extract_debug(ErlNifEnv* env,
*/
ERL_NIF_TERM debug = MKA(env, "debug");
- return esock_extract_bool_from_map(env, map, debug, SOCKET_GLOBAL_DEBUG_DEFAULT);
+ return esock_extract_bool_from_map(env, map, debug, ESOCK_GLOBAL_DEBUG_DEFAULT);
}
static
@@ -19587,7 +19640,7 @@ BOOLEAN_T extract_iow(ErlNifEnv* env,
*/
ERL_NIF_TERM iow = MKA(env, "iow");
- return esock_extract_bool_from_map(env, map, iow, SOCKET_NIF_IOW_DEFAULT);
+ return esock_extract_bool_from_map(env, map, iow, ESOCK_NIF_IOW_DEFAULT);
}
#endif // if !defined(__WIN32__)
@@ -19608,7 +19661,7 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
data.iow = extract_iow(env, load_info);
/* +++ Global Counters +++ */
- data.cntMtx = MCREATE("socket[gcnt]");
+ data.cntMtx = MCREATE("esock[gcnt]");
data.numSockets = 0;
data.numTypeDGrams = 0;
data.numTypeStreams = 0;
@@ -19635,13 +19688,21 @@ GLOBAL_ERROR_REASON_ATOMS
#undef GLOBAL_ATOM_DECL
esock_atom_socket_tag = MKA(env, "$socket");
- sockets = enif_open_resource_type_x(env,
- "sockets",
- &socketInit,
- ERL_NIF_RT_CREATE,
- NULL);
+ esocks = enif_open_resource_type_x(env,
+ "sockets",
+ &esockInit,
+ ERL_NIF_RT_CREATE,
+ NULL);
- return !sockets;
+ return !esocks;
}
-ERL_NIF_INIT(socket, socket_funcs, on_load, NULL, NULL, NULL)
+/*
+ * MODULE: socket (the erlang API/interface module)
+ * funcs: esock_funcs (defines the API of this nif)
+ * load: on_load (load this nif)
+ * upgrade: NULL (not used)
+ * NULL: THIS IS NOT USED
+ * unload: NULL (not used)
+ */
+ERL_NIF_INIT(socket, esock_funcs, on_load, NULL, NULL, NULL)
diff --git a/erts/emulator/nifs/common/socket_util.c b/erts/emulator/nifs/common/socket_util.c
index 2740cb51ef..54c310ecc7 100644
--- a/erts/emulator/nifs/common/socket_util.c
+++ b/erts/emulator/nifs/common/socket_util.c
@@ -741,16 +741,24 @@ char* esock_decode_ip4_address(ErlNifEnv* env,
"\r\n", eAddr) );
if (IS_ATOM(env, eAddr)) {
- /* This is either 'any' or 'loopback' */
+
+ /* This is either 'any' | 'broadcast' | 'loopback' */
if (COMPARE(esock_atom_loopback, eAddr) == 0) {
- UDBG( ("SUTIL", "esock_decode_ip4_address -> address: lookback\r\n") );
+ UDBG( ("SUTIL",
+ "esock_decode_ip4_address -> address: lookback\r\n") );
addr.s_addr = htonl(INADDR_LOOPBACK);
} else if (COMPARE(esock_atom_any, eAddr) == 0) {
- UDBG( ("SUTIL", "esock_decode_ip4_address -> address: any\r\n") );
- addr.s_addr = htonl(INADDR_ANY);
+ UDBG( ("SUTIL",
+ "esock_decode_ip4_address -> address: any\r\n") );
+ addr.s_addr = htonl(INADDR_ANY);
+ } else if (COMPARE(esock_atom_broadcast, eAddr) == 0) {
+ UDBG( ("SUTIL",
+ "esock_decode_ip4_address -> address: broadcast\r\n") );
+ addr.s_addr = htonl(INADDR_BROADCAST);
} else {
- UDBG( ("SUTIL", "esock_decode_ip4_address -> address: unknown\r\n") );
+ UDBG( ("SUTIL",
+ "esock_decode_ip4_address -> address: unknown\r\n") );
return ESOCK_STR_EINVAL;
}
diff --git a/erts/emulator/nifs/unix/unix_prim_file.c b/erts/emulator/nifs/unix/unix_prim_file.c
index 20021b9358..176a9318b2 100644
--- a/erts/emulator/nifs/unix/unix_prim_file.c
+++ b/erts/emulator/nifs/unix/unix_prim_file.c
@@ -627,6 +627,33 @@ int efile_truncate(efile_data_t *d) {
return 1;
}
+static void build_file_info(struct stat *data, efile_fileinfo_t *result) {
+ if(S_ISCHR(data->st_mode) || S_ISBLK(data->st_mode)) {
+ result->type = EFILE_FILETYPE_DEVICE;
+ } else if(S_ISDIR(data->st_mode)) {
+ result->type = EFILE_FILETYPE_DIRECTORY;
+ } else if(S_ISREG(data->st_mode)) {
+ result->type = EFILE_FILETYPE_REGULAR;
+ } else if(S_ISLNK(data->st_mode)) {
+ result->type = EFILE_FILETYPE_SYMLINK;
+ } else {
+ result->type = EFILE_FILETYPE_OTHER;
+ }
+
+ result->a_time = (Sint64)data->st_atime;
+ result->m_time = (Sint64)data->st_mtime;
+ result->c_time = (Sint64)data->st_ctime;
+ result->size = data->st_size;
+
+ result->major_device = data->st_dev;
+ result->minor_device = data->st_rdev;
+ result->links = data->st_nlink;
+ result->inode = data->st_ino;
+ result->mode = data->st_mode;
+ result->uid = data->st_uid;
+ result->gid = data->st_gid;
+}
+
posix_errno_t efile_read_info(const efile_path_t *path, int follow_links, efile_fileinfo_t *result) {
struct stat data;
@@ -640,30 +667,7 @@ posix_errno_t efile_read_info(const efile_path_t *path, int follow_links, efile_
}
}
- if(S_ISCHR(data.st_mode) || S_ISBLK(data.st_mode)) {
- result->type = EFILE_FILETYPE_DEVICE;
- } else if(S_ISDIR(data.st_mode)) {
- result->type = EFILE_FILETYPE_DIRECTORY;
- } else if(S_ISREG(data.st_mode)) {
- result->type = EFILE_FILETYPE_REGULAR;
- } else if(S_ISLNK(data.st_mode)) {
- result->type = EFILE_FILETYPE_SYMLINK;
- } else {
- result->type = EFILE_FILETYPE_OTHER;
- }
-
- result->a_time = (Sint64)data.st_atime;
- result->m_time = (Sint64)data.st_mtime;
- result->c_time = (Sint64)data.st_ctime;
- result->size = data.st_size;
-
- result->major_device = data.st_dev;
- result->minor_device = data.st_rdev;
- result->links = data.st_nlink;
- result->inode = data.st_ino;
- result->mode = data.st_mode;
- result->uid = data.st_uid;
- result->gid = data.st_gid;
+ build_file_info(&data, result);
#ifndef NO_ACCESS
result->access = EFILE_ACCESS_NONE;
@@ -682,6 +686,56 @@ posix_errno_t efile_read_info(const efile_path_t *path, int follow_links, efile_
return 0;
}
+static int check_access(struct stat *st) {
+ int ret = EFILE_ACCESS_NONE;
+
+ if(st->st_uid == getuid()) {
+ if(st->st_mode & S_IRUSR) {
+ ret |= EFILE_ACCESS_READ;
+ }
+ if(st->st_mode & S_IWUSR) {
+ ret |= EFILE_ACCESS_WRITE;
+ }
+ return ret;
+ }
+
+ if(st->st_gid == getgid()) {
+ if(st->st_mode & S_IRGRP) {
+ ret |= EFILE_ACCESS_READ;
+ }
+ if(st->st_mode & S_IWGRP) {
+ ret |= EFILE_ACCESS_WRITE;
+ }
+ return ret;
+ }
+
+ if(st->st_mode & S_IROTH) {
+ ret |= EFILE_ACCESS_READ;
+ }
+ if(st->st_mode & S_IWOTH) {
+ ret |= EFILE_ACCESS_WRITE;
+ }
+ return ret;
+}
+
+posix_errno_t efile_read_handle_info(efile_data_t *d, efile_fileinfo_t *result) {
+ struct stat data;
+ efile_unix_t *u = (efile_unix_t*)d;
+
+#ifdef HAVE_FSTAT
+ if(fstat(u->fd, &data) < 0) {
+ return errno;
+ }
+
+ build_file_info(&data, result);
+ result->access = check_access(&data);
+
+ return 0;
+#else
+ return ENOTSUP;
+#endif
+}
+
posix_errno_t efile_set_permissions(const efile_path_t *path, Uint32 permissions) {
const mode_t MUTABLE_MODES = (S_ISUID | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO);
mode_t new_modes = permissions & MUTABLE_MODES;
diff --git a/erts/emulator/nifs/win32/win_prim_file.c b/erts/emulator/nifs/win32/win_prim_file.c
index 13306104c0..839ac3ea6e 100644
--- a/erts/emulator/nifs/win32/win_prim_file.c
+++ b/erts/emulator/nifs/win32/win_prim_file.c
@@ -773,6 +773,71 @@ static int is_name_surrogate(const efile_path_t *path) {
return result;
}
+static void build_file_info(BY_HANDLE_FILE_INFORMATION *native_file_info, const efile_path_t *path, int is_link, efile_fileinfo_t *result) {
+ DWORD attributes;
+
+ attributes = native_file_info->dwFileAttributes;
+
+ if(is_link) {
+ result->type = EFILE_FILETYPE_SYMLINK;
+ /* This should be _S_IFLNK, but the old driver always set
+ * non-directories to _S_IFREG. */
+ result->mode |= _S_IFREG;
+ } else if(attributes & FILE_ATTRIBUTE_DIRECTORY) {
+ result->type = EFILE_FILETYPE_DIRECTORY;
+ result->mode |= _S_IFDIR | _S_IEXEC;
+ } else {
+ if(is_executable_file(path)) {
+ result->mode |= _S_IEXEC;
+ }
+
+ result->type = EFILE_FILETYPE_REGULAR;
+ result->mode |= _S_IFREG;
+ }
+
+ if(!(attributes & FILE_ATTRIBUTE_READONLY)) {
+ result->access = EFILE_ACCESS_READ | EFILE_ACCESS_WRITE;
+ result->mode |= _S_IREAD | _S_IWRITE;
+ } else {
+ result->access = EFILE_ACCESS_READ;
+ result->mode |= _S_IREAD;
+ }
+
+ /* Propagate user mode-bits to group/other fields */
+ result->mode |= (result->mode & 0700) >> 3;
+ result->mode |= (result->mode & 0700) >> 6;
+
+ result->size =
+ ((Uint64)native_file_info->nFileSizeHigh << 32ull) |
+ (Uint64)native_file_info->nFileSizeLow;
+ result->links = MAX(1, native_file_info->nNumberOfLinks);
+
+ result->major_device = get_drive_number(path);
+ result->minor_device = 0;
+ result->inode = 0;
+ result->uid = 0;
+ result->gid = 0;
+}
+
+static void build_file_info_times(BY_HANDLE_FILE_INFORMATION *native_file_info, efile_fileinfo_t *result) {
+ FILETIME_TO_EPOCH(result->m_time, native_file_info->ftLastWriteTime);
+ FILETIME_TO_EPOCH(result->a_time, native_file_info->ftLastAccessTime);
+ FILETIME_TO_EPOCH(result->c_time, native_file_info->ftCreationTime);
+
+ if(result->m_time == -EPOCH_DIFFERENCE) {
+ /* Default to 1970 just like the old driver. */
+ result->m_time = 0;
+ }
+
+ if(result->a_time == -EPOCH_DIFFERENCE) {
+ result->a_time = result->m_time;
+ }
+
+ if(result->c_time == -EPOCH_DIFFERENCE) {
+ result->c_time = result->m_time;
+ }
+}
+
posix_errno_t efile_read_info(const efile_path_t *path, int follow_links, efile_fileinfo_t *result) {
BY_HANDLE_FILE_INFORMATION native_file_info;
DWORD attributes;
@@ -792,23 +857,41 @@ posix_errno_t efile_read_info(const efile_path_t *path, int follow_links, efile_
return windows_to_posix_errno(last_error);
}
- attributes = FILE_ATTRIBUTE_DIRECTORY;
+ native_file_info.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
} else if(is_path_root(path)) {
/* Local (or mounted) roots can be queried with GetFileAttributesW but
* lack support for GetFileInformationByHandle, so we'll skip that
* part. */
+ native_file_info.dwFileAttributes = attributes;
} else {
HANDLE handle;
+ DWORD last_error;
+ DWORD flags;
if(attributes & FILE_ATTRIBUTE_REPARSE_POINT) {
is_link = is_name_surrogate(path);
}
+ flags = FILE_FLAG_BACKUP_SEMANTICS;
+ if(!follow_links && is_link) {
+ flags |= FILE_FLAG_OPEN_REPARSE_POINT;
+ }
+
+ handle = CreateFileW((const WCHAR*)path->data, GENERIC_READ,
+ FILE_SHARE_FLAGS, NULL, OPEN_EXISTING, flags, NULL);
+ last_error = GetLastError();
+
+ if(handle == INVALID_HANDLE_VALUE) {
+ return windows_to_posix_errno(last_error);
+ }
+
if(follow_links && is_link) {
posix_errno_t posix_errno;
efile_path_t resolved_path;
- posix_errno = internal_read_link(path, &resolved_path);
+ posix_errno = internal_read_link(handle, &resolved_path);
+
+ CloseHandle(handle);
if(posix_errno != 0) {
return posix_errno;
@@ -817,74 +900,43 @@ posix_errno_t efile_read_info(const efile_path_t *path, int follow_links, efile_
return efile_read_info(&resolved_path, 0, result);
}
- handle = CreateFileW((const WCHAR*)path->data, GENERIC_READ,
- FILE_SHARE_FLAGS, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS,
- NULL);
-
- /* The old driver never cared whether this succeeded. */
- if(handle != INVALID_HANDLE_VALUE) {
- GetFileInformationByHandle(handle, &native_file_info);
- CloseHandle(handle);
- }
+ GetFileInformationByHandle(handle, &native_file_info);
+ CloseHandle(handle);
- FILETIME_TO_EPOCH(result->m_time, native_file_info.ftLastWriteTime);
- FILETIME_TO_EPOCH(result->a_time, native_file_info.ftLastAccessTime);
- FILETIME_TO_EPOCH(result->c_time, native_file_info.ftCreationTime);
+ build_file_info_times(&native_file_info, result);
+ }
- if(result->m_time == -EPOCH_DIFFERENCE) {
- /* Default to 1970 just like the old driver. */
- result->m_time = 0;
- }
+ build_file_info(&native_file_info, path, is_link, result);
- if(result->a_time == -EPOCH_DIFFERENCE) {
- result->a_time = result->m_time;
- }
+ return 0;
+}
- if(result->c_time == -EPOCH_DIFFERENCE) {
- result->c_time = result->m_time;
- }
- }
+posix_errno_t efile_read_handle_info(efile_data_t *d, efile_fileinfo_t *result) {
+ efile_win_t *w = (efile_win_t*)d;
+ HANDLE handle;
+ BY_HANDLE_FILE_INFORMATION native_file_info;
+ posix_errno_t posix_errno;
+ efile_path_t path;
+ int length;
- if(is_link) {
- result->type = EFILE_FILETYPE_SYMLINK;
- /* This should be _S_IFLNK, but the old driver always set
- * non-directories to _S_IFREG. */
- result->mode |= _S_IFREG;
- } else if(attributes & FILE_ATTRIBUTE_DIRECTORY) {
- result->type = EFILE_FILETYPE_DIRECTORY;
- result->mode |= _S_IFDIR | _S_IEXEC;
- } else {
- if(is_executable_file(path)) {
- result->mode |= _S_IEXEC;
- }
+ sys_memset(&native_file_info, 0, sizeof(native_file_info));
- result->type = EFILE_FILETYPE_REGULAR;
- result->mode |= _S_IFREG;
+ posix_errno = internal_read_link(w->handle, &path);
+ if(posix_errno != 0) {
+ return posix_errno;
}
- if(!(attributes & FILE_ATTRIBUTE_READONLY)) {
- result->access = EFILE_ACCESS_READ | EFILE_ACCESS_WRITE;
- result->mode |= _S_IREAD | _S_IWRITE;
+ if(GetFileInformationByHandle(w->handle, &native_file_info)) {
+ build_file_info_times(&native_file_info, result);
+ } else if(is_path_root(&path)) {
+ /* GetFileInformationByHandle is not supported on path roots, so
+ * fall back to efile_read_info. */
+ return efile_read_info(&path, 0, result);
} else {
- result->access = EFILE_ACCESS_READ;
- result->mode |= _S_IREAD;
+ return windows_to_posix_errno(GetLastError());
}
- /* Propagate user mode-bits to group/other fields */
- result->mode |= (result->mode & 0700) >> 3;
- result->mode |= (result->mode & 0700) >> 6;
-
- result->size =
- ((Uint64)native_file_info.nFileSizeHigh << 32ull) |
- (Uint64)native_file_info.nFileSizeLow;
-
- result->links = MAX(1, native_file_info.nNumberOfLinks);
-
- result->major_device = get_drive_number(path);
- result->minor_device = 0;
- result->inode = 0;
- result->uid = 0;
- result->gid = 0;
+ build_file_info(&native_file_info, &path, 0, result);
return 0;
}
@@ -963,31 +1015,20 @@ posix_errno_t efile_set_time(const efile_path_t *path, Sint64 a_time, Sint64 m_t
return windows_to_posix_errno(last_error);
}
-static posix_errno_t internal_read_link(const efile_path_t *path, efile_path_t *result) {
+static posix_errno_t internal_read_link(HANDLE link_handle, efile_path_t *result) {
DWORD required_length, actual_length;
- HANDLE link_handle;
DWORD last_error;
- link_handle = CreateFileW((WCHAR*)path->data, GENERIC_READ,
- FILE_SHARE_FLAGS, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
- last_error = GetLastError();
-
- if(link_handle == INVALID_HANDLE_VALUE) {
- return windows_to_posix_errno(last_error);
- }
-
required_length = GetFinalPathNameByHandleW(link_handle, NULL, 0, 0);
last_error = GetLastError();
if(required_length <= 0) {
- CloseHandle(link_handle);
return windows_to_posix_errno(last_error);
}
/* Unlike many other path functions (eg. GetFullPathNameW), this one
* includes the NUL terminator in its required length. */
if(!enif_alloc_binary(required_length * sizeof(WCHAR), result)) {
- CloseHandle(link_handle);
return ENOMEM;
}
@@ -995,8 +1036,6 @@ static posix_errno_t internal_read_link(const efile_path_t *path, efile_path_t *
(WCHAR*)result->data, required_length, 0);
last_error = GetLastError();
- CloseHandle(link_handle);
-
if(actual_length == 0 || actual_length >= required_length) {
enif_release_binary(result);
return windows_to_posix_errno(last_error);
@@ -1014,6 +1053,8 @@ posix_errno_t efile_read_link(ErlNifEnv *env, const efile_path_t *path, ERL_NIF_
posix_errno_t posix_errno;
ErlNifBinary result_bin;
DWORD attributes;
+ HANDLE handle;
+ DWORD last_error;
ASSERT_PATH_FORMAT(path);
@@ -1029,7 +1070,17 @@ posix_errno_t efile_read_link(ErlNifEnv *env, const efile_path_t *path, ERL_NIF_
return EINVAL;
}
- posix_errno = internal_read_link(path, &result_bin);
+ handle = CreateFileW((WCHAR*)path->data, GENERIC_READ,
+ FILE_SHARE_FLAGS, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS,
+ NULL);
+ last_error = GetLastError();
+
+ if(handle == INVALID_HANDLE_VALUE) {
+ return windows_to_posix_errno(last_error);
+ }
+ posix_errno = internal_read_link(handle, &result_bin);
+
+ CloseHandle(handle);
if(posix_errno == 0) {
if(!normalize_path_result(&result_bin)) {
diff --git a/erts/emulator/sys/common/erl_osenv.h b/erts/emulator/sys/common/erl_osenv.h
index 4777f2148a..f2e96a6af7 100644
--- a/erts/emulator/sys/common/erl_osenv.h
+++ b/erts/emulator/sys/common/erl_osenv.h
@@ -35,16 +35,10 @@
# include "config.h"
#endif
-typedef struct __erts_osenv_data_t erts_osenv_data_t;
-
-typedef struct __erts_osenv_t {
- struct __env_rbtnode_t *tree;
- int variable_count;
- int content_size;
-} erts_osenv_t;
-
#include "sys.h"
+typedef struct __erts_osenv_data_t erts_osenv_data_t;
+
struct __erts_osenv_data_t {
Sint length;
void *data;
diff --git a/erts/emulator/sys/unix/erl_child_setup.c b/erts/emulator/sys/unix/erl_child_setup.c
index 9241660069..8f261761db 100644
--- a/erts/emulator/sys/unix/erl_child_setup.c
+++ b/erts/emulator/sys/unix/erl_child_setup.c
@@ -63,10 +63,13 @@
#include "erl_driver.h"
#include "sys_uds.h"
-#include "hash.h"
#include "erl_term.h"
#include "erl_child_setup.h"
+#undef ERTS_GLB_INLINE_INCL_FUNC_DEF
+#define ERTS_GLB_INLINE_INCL_FUNC_DEF 1
+#include "hash.h"
+
#define SET_CLOEXEC(fd) fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC)
#if defined(__ANDROID__)
diff --git a/erts/emulator/sys/unix/sys_drivers.c b/erts/emulator/sys/unix/sys_drivers.c
index 664d677ebd..870d0bd28a 100644
--- a/erts/emulator/sys/unix/sys_drivers.c
+++ b/erts/emulator/sys/unix/sys_drivers.c
@@ -55,6 +55,7 @@
#define WANT_NONBLOCKING /* must define this to pull in defs from sys.h */
#include "sys.h"
+#include "erl_osenv.h"
#include "erl_threads.h"
@@ -785,15 +786,15 @@ static ErlDrvSSizeT spawn_control(ErlDrvData e, unsigned int cmd, char *buf,
static int fd_get_window_size(int fd, Uint32 *width, Uint32 *height)
{
-#ifdef TIOCGWINSZ
+#ifdef TIOCGWINSZ
struct winsize ws;
if (ioctl(fd,TIOCGWINSZ,&ws) == 0) {
*width = (Uint32) ws.ws_col;
*height = (Uint32) ws.ws_row;
- return 0;
+ return 1;
}
#endif
- return -1;
+ return 0;
}
static ErlDrvSSizeT fd_control(ErlDrvData drv_data,
@@ -801,16 +802,28 @@ static ErlDrvSSizeT fd_control(ErlDrvData drv_data,
char *buf, ErlDrvSizeT len,
char **rbuf, ErlDrvSizeT rlen)
{
- int fd = (int)(long)drv_data;
char resbuff[2*sizeof(Uint32)];
-
+ ErtsSysDriverData* dd = (ErtsSysDriverData*)drv_data;
command -= ERTS_TTYSL_DRV_CONTROL_MAGIC_NUMBER;
switch (command) {
case FD_CTRL_OP_GET_WINSIZE:
{
Uint32 w,h;
- if (fd_get_window_size(fd,&w,&h))
- return 0;
+ int success = 0;
+ if (dd->ofd != NULL) {
+ /* Try with output file descriptor */
+ int out_fd = dd->ofd->fd;
+ success = fd_get_window_size(out_fd,&w,&h);
+ }
+ if (!success && dd->ifd != NULL) {
+ /* Try with input file descriptor */
+ int in_fd = dd->ifd->fd;
+ success = fd_get_window_size(in_fd,&w,&h);
+ }
+ if (!success) {
+ return -1;
+ }
+ /* Succeeded */
memcpy(resbuff,&w,sizeof(Uint32));
memcpy(resbuff+sizeof(Uint32),&h,sizeof(Uint32));
}
diff --git a/erts/emulator/sys/win32/sys.c b/erts/emulator/sys/win32/sys.c
index b95aadc9b2..bc3de42be7 100644
--- a/erts/emulator/sys/win32/sys.c
+++ b/erts/emulator/sys/win32/sys.c
@@ -27,6 +27,7 @@
#endif
#include "sys.h"
+#include "erl_osenv.h"
#include "erl_alloc.h"
#include "erl_sys_driver.h"
#include "global.h"
diff --git a/erts/emulator/sys/win32/sys_env.c b/erts/emulator/sys/win32/sys_env.c
index c78161b344..36223c2c14 100644
--- a/erts/emulator/sys/win32/sys_env.c
+++ b/erts/emulator/sys/win32/sys_env.c
@@ -23,6 +23,7 @@
#endif
#include "sys.h"
+#include "erl_osenv.h"
#include "erl_sys_driver.h"
#include "erl_alloc.h"
diff --git a/erts/emulator/test/bif_SUITE.erl b/erts/emulator/test/bif_SUITE.erl
index c5abd04e07..cde2bbfa7c 100644
--- a/erts/emulator/test/bif_SUITE.erl
+++ b/erts/emulator/test/bif_SUITE.erl
@@ -29,8 +29,8 @@
shadow_comments/1,list_to_utf8_atom/1,
specs/1,improper_bif_stubs/1,auto_imports/1,
t_list_to_existing_atom/1,os_env/1,otp_7526/1,
- binary_to_atom/1,binary_to_existing_atom/1,
- atom_to_binary/1,min_max/1, erlang_halt/1,
+ t_binary_to_atom/1,t_binary_to_existing_atom/1,
+ t_atom_to_binary/1,min_max/1, erlang_halt/1,
erl_crash_dump_bytes/1,
is_builtin/1, error_stacktrace/1,
error_stacktrace_during_call_trace/1,
@@ -50,7 +50,7 @@ all() ->
specs, improper_bif_stubs, auto_imports,
t_list_to_existing_atom, os_env, otp_7526,
display, display_string, list_to_utf8_atom,
- atom_to_binary, binary_to_atom, binary_to_existing_atom,
+ t_atom_to_binary, t_binary_to_atom, t_binary_to_existing_atom,
erl_crash_dump_bytes, min_max, erlang_halt, is_builtin,
error_stacktrace, error_stacktrace_during_call_trace,
group_leader_prio, group_leader_prio_dirty,
@@ -496,7 +496,7 @@ test_7526(N) ->
-define(BADARG(E), {'EXIT',{badarg,_}} = (catch E)).
-define(SYS_LIMIT(E), {'EXIT',{system_limit,_}} = (catch E)).
-binary_to_atom(Config) when is_list(Config) ->
+t_binary_to_atom(Config) when is_list(Config) ->
HalfLong = lists:seq(0, 127),
HalfLongAtom = list_to_atom(HalfLong),
HalfLongBin = list_to_binary(HalfLong),
@@ -524,8 +524,10 @@ binary_to_atom(Config) when is_list(Config) ->
test_binary_to_atom(<<C/utf8>>, utf8)
end],
- <<"こんにちは"/utf8>> =
- atom_to_binary(test_binary_to_atom(<<"こんにちは"/utf8>>, utf8), utf8),
+ ExoticBin = <<"こんにちは"/utf8>>,
+ ExoticAtom = test_binary_to_atom(ExoticBin, utf8),
+ ExoticBin = atom_to_binary(ExoticAtom, utf8),
+ ExoticBin = atom_to_binary(ExoticAtom),
%% badarg failures.
fail_binary_to_atom(atom),
@@ -543,6 +545,7 @@ binary_to_atom(Config) when is_list(Config) ->
%% Bad UTF8 sequences.
?BADARG(binary_to_atom(id(<<255>>), utf8)),
+ ?BADARG(binary_to_atom(id(<<255>>))),
?BADARG(binary_to_atom(id(<<255,0>>), utf8)),
?BADARG(binary_to_atom(id(<<16#C0,16#80>>), utf8)), %Overlong 0.
<<B:1/binary, _/binary>> = id(<<194, 163>>), %Truncated character ERL-474
@@ -550,6 +553,7 @@ binary_to_atom(Config) when is_list(Config) ->
%% system_limit failures.
?SYS_LIMIT(binary_to_atom(id(<<0:512/unit:8,255>>), utf8)),
+ ?SYS_LIMIT(binary_to_atom(id(<<0:512/unit:8,255>>))),
?SYS_LIMIT(binary_to_atom(id(<<0:512/unit:8,255,0>>), utf8)),
?SYS_LIMIT(binary_to_atom(<<0:256/unit:8>>, latin1)),
?SYS_LIMIT(binary_to_atom(<<0:257/unit:8>>, latin1)),
@@ -562,6 +566,14 @@ binary_to_atom(Config) when is_list(Config) ->
test_binary_to_atom(Bin0, Encoding) ->
Res = binary_to_atom(Bin0, Encoding),
Res = binary_to_existing_atom(Bin0, Encoding),
+ if
+ Encoding =:= utf8;
+ Encoding =:= unicode ->
+ Res = binary_to_atom(Bin0),
+ Res = binary_to_existing_atom(Bin0);
+ true ->
+ ok
+ end,
Bin1 = id(<<7:3,Bin0/binary,32:5>>),
Sz = byte_size(Bin0),
<<_:3,UnalignedBin:Sz/binary,_:5>> = Bin1,
@@ -581,6 +593,12 @@ fail_binary_to_atom(Bin) ->
ok
end,
try
+ binary_to_atom(Bin)
+ catch
+ error:badarg ->
+ ok
+ end,
+ try
binary_to_existing_atom(Bin, latin1)
catch
error:badarg ->
@@ -591,10 +609,16 @@ fail_binary_to_atom(Bin) ->
catch
error:badarg ->
ok
+ end,
+ try
+ binary_to_existing_atom(Bin)
+ catch
+ error:badarg ->
+ ok
end.
-binary_to_existing_atom(Config) when is_list(Config) ->
+t_binary_to_existing_atom(Config) when is_list(Config) ->
UnlikelyBin = <<"ou0897979655678dsfj923874390867er869fds973qerueoru">>,
try
binary_to_existing_atom(UnlikelyBin, latin1),
@@ -609,6 +633,12 @@ binary_to_existing_atom(Config) when is_list(Config) ->
catch
error:badarg -> ok
end,
+ try
+ binary_to_existing_atom(UnlikelyBin),
+ ct:fail(atom_exists)
+ catch
+ error:badarg -> ok
+ end,
UnlikelyAtom = binary_to_atom(id(UnlikelyBin), latin1),
UnlikelyAtom = binary_to_existing_atom(UnlikelyBin, latin1),
@@ -625,7 +655,7 @@ binary_to_existing_atom(Config) when is_list(Config) ->
ok.
-atom_to_binary(Config) when is_list(Config) ->
+t_atom_to_binary(Config) when is_list(Config) ->
HalfLong = lists:seq(0, 127),
HalfLongAtom = list_to_atom(HalfLong),
HalfLongBin = list_to_binary(HalfLong),
@@ -641,12 +671,15 @@ atom_to_binary(Config) when is_list(Config) ->
LongBin = atom_to_binary(LongAtom, latin1),
%% utf8.
+ <<>> = atom_to_binary(''),
<<>> = atom_to_binary('', utf8),
<<>> = atom_to_binary('', unicode),
<<127>> = atom_to_binary('\177', utf8),
<<"abcdef">> = atom_to_binary(abcdef, utf8),
HalfLongBin = atom_to_binary(HalfLongAtom, utf8),
+ HalfLongBin = atom_to_binary(HalfLongAtom),
LongAtomBin = atom_to_binary(LongAtom, utf8),
+ LongAtomBin = atom_to_binary(LongAtom),
verify_long_atom_bin(LongAtomBin, 0),
%% Failing cases.
@@ -678,8 +711,15 @@ fail_atom_to_binary(Term) ->
catch
error:badarg ->
ok
+ end,
+ try
+ atom_to_binary(Term)
+ catch
+ error:badarg ->
+ ok
end.
+
min_max(Config) when is_list(Config) ->
a = erlang:min(id(a), a),
a = erlang:min(id(a), b),
@@ -860,6 +900,7 @@ error_stacktrace_test() ->
Types = [apply_const_last, apply_const, apply_last,
apply, double_apply_const_last, double_apply_const,
double_apply_last, double_apply, multi_apply_const_last,
+ apply_const_only, apply_only,
multi_apply_const, multi_apply_last, multi_apply,
call_const_last, call_last, call_const, call],
lists:foreach(fun (Type) ->
@@ -897,6 +938,8 @@ error_stacktrace_test() ->
ok.
stk([], Type, Func) ->
+ put(erlang, erlang),
+ put(tail, []),
tail(Type, Func, jump),
ok;
stk([_|L], Type, Func) ->
@@ -910,6 +953,12 @@ tail(Type, error_1, do) ->
tail(Type, error_2, do) ->
do_error_2(Type).
+do_error_2(apply_const_only) ->
+ apply(erlang, error, [oops, [apply_const_only]]);
+do_error_2(apply_only) ->
+ Erlang = get(erlang),
+ Tail = get(tail),
+ apply(Erlang, error, [oops, [apply_only|Tail]]);
do_error_2(apply_const_last) ->
erlang:apply(erlang, error, [oops, [apply_const_last]]);
do_error_2(apply_const) ->
@@ -951,6 +1000,12 @@ do_error_2(call) ->
erlang:error(id(oops), id([call])).
+do_error_1(apply_const_only) ->
+ apply(erlang, error, [oops]);
+do_error_1(apply_only) ->
+ Erlang = get(erlang),
+ Tail = get(tail),
+ apply(Erlang, error, [oops|Tail]);
do_error_1(apply_const_last) ->
erlang:apply(erlang, error, [oops]);
do_error_1(apply_const) ->
diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl
index 4fb339926e..fbd1325c3a 100644
--- a/erts/emulator/test/binary_SUITE.erl
+++ b/erts/emulator/test/binary_SUITE.erl
@@ -1438,7 +1438,8 @@ sleeper() ->
gc_test(Config) when is_list(Config) ->
%% Note: This test is only relevant for REFC binaries.
%% Therefore, we take care that all binaries are REFC binaries.
- B = list_to_binary(lists:seq(0, ?heap_binary_size)),
+ true = 192 > ?heap_binary_size,
+ B = list_to_binary(lists:seq(1, 192)),
Self = self(),
F1 = fun() ->
gc(),
@@ -1447,22 +1448,22 @@ gc_test(Config) when is_list(Config) ->
end,
F = fun() ->
receive go -> ok end,
- {binary,[{_,65,1}]} = process_info(self(), binary),
+ {binary,[{_,192,1}]} = process_info(self(), binary),
gc(),
- {B1,B2} = my_split_binary(B, 4),
+ {B1,B2} = my_split_binary(B, 68),
gc(),
gc(),
{binary,L1} = process_info(self(), binary),
[Binfo1,Binfo2,Binfo3] = L1,
- {_,65,3} = Binfo1 = Binfo2 = Binfo3,
- 65 = size(B),
- 4 = size(B1),
- 61 = size(B2),
+ {_,192,3} = Binfo1 = Binfo2 = Binfo3,
+ 192 = size(B),
+ 68 = size(B1),
+ 124 = size(B2),
F1()
end,
gc(),
gc(),
- 65 = size(B),
+ 192 = size(B),
gc_test1(spawn_opt(erlang, apply, [F,[]], [link,{fullsweep_after,0}])).
gc_test1(Pid) ->
diff --git a/erts/emulator/test/call_trace_SUITE.erl b/erts/emulator/test/call_trace_SUITE.erl
index 742592f88e..477b0f5bb3 100644
--- a/erts/emulator/test/call_trace_SUITE.erl
+++ b/erts/emulator/test/call_trace_SUITE.erl
@@ -832,21 +832,27 @@ deep_exception() ->
R1 -> ct:fail({returned,abbr(R1)})
catch error:badarg -> ok
end,
- expect(fun ({trace,S,call,{lists,reverse,[L1,L2]}})
+ expect(fun ({trace,S,call,{lists,reverse,[L1,L2]}}, Traps)
when is_list(L1), is_list(L2), S == Self ->
- next;
+ %% Each trapping call to reverse/2 must have a corresponding
+ %% exception_from
+ {next, Traps + 1};
({trace,S,exception_from,
- {lists,reverse,2},{error,badarg}})
+ {lists,reverse,2},{error,badarg}}, Traps)
+ when S == Self, Traps > 1 ->
+ {next, Traps - 1};
+ ({trace,S,exception_from,
+ {lists,reverse,2},{error,badarg}}, 1)
when S == Self ->
expected;
- ('_') ->
+ ('_', _Traps) ->
{trace,Self,exception_from,
{lists,reverse,2},{error,badarg}};
- (_) ->
+ (_, _Traps) ->
{unexpected,
{trace,Self,exception_from,
{lists,reverse,2},{error,badarg}}}
- end),
+ end, 0),
deep_exception(?LINE, deep_5, [1,2], 7,
[{trace,Self,call,{erlang,error,[undef]}},
{trace,Self,exception_from,{erlang,error,1},
@@ -896,21 +902,27 @@ deep_exception() ->
R2 -> ct:fail({returned,abbr(R2)})
catch error:badarg -> ok
end,
- expect(fun ({trace,S,call,{lists,reverse,[L1,L2]}})
+ expect(fun ({trace,S,call,{lists,reverse,[L1,L2]}}, Traps)
when is_list(L1), is_list(L2), S == Self ->
- next;
+ %% Each trapping call to reverse/2 must have a corresponding
+ %% exception_from
+ {next, Traps + 1};
+ ({trace,S,exception_from,
+ {lists,reverse,2},{error,badarg}}, Traps)
+ when S == Self, Traps > 1 ->
+ {next, Traps - 1};
({trace,S,exception_from,
- {lists,reverse,2},{error,badarg}})
+ {lists,reverse,2},{error,badarg}}, 1)
when S == Self ->
expected;
- ('_') ->
+ ('_', _Traps) ->
{trace,Self,exception_from,
{lists,reverse,2},{error,badarg}};
- (_) ->
+ (_, _Traps) ->
{unexpected,
{trace,Self,exception_from,
{lists,reverse,2},{error,badarg}}}
- end),
+ end, 0),
deep_exception(?LINE, apply, [?MODULE,deep_5,[1,2]], 7,
[{trace,Self,call,{erlang,error,[undef]}},
{trace,Self,exception_from,{erlang,error,1},
@@ -975,21 +987,27 @@ deep_exception() ->
R3 -> ct:fail({returned,abbr(R3)})
catch error:badarg -> ok
end,
- expect(fun ({trace,S,call,{lists,reverse,[L1,L2]}})
+ expect(fun ({trace,S,call,{lists,reverse,[L1,L2]}}, Traps)
when is_list(L1), is_list(L2), S == Self ->
- next;
+ %% Each trapping call to reverse/2 must have a corresponding
+ %% exception_from
+ {next, Traps + 1};
+ ({trace,S,exception_from,
+ {lists,reverse,2},{error,badarg}}, Traps)
+ when S == Self, Traps > 1 ->
+ {next, Traps - 1};
({trace,S,exception_from,
- {lists,reverse,2},{error,badarg}})
+ {lists,reverse,2},{error,badarg}}, 1)
when S == Self ->
expected;
- ('_') ->
+ ('_', _Traps) ->
{trace,Self,exception_from,
{lists,reverse,2},{error,badarg}};
- (_) ->
+ (_, _Traps) ->
{unexpected,
{trace,Self,exception_from,
{lists,reverse,2},{error,badarg}}}
- end),
+ end, 0),
deep_exception(?LINE, apply,
[fun () -> ?MODULE:deep_5(1,2) end, []], 7,
[{trace,Self,call,{erlang,error,[undef]}},
@@ -1249,6 +1267,24 @@ expect(Message) ->
ct:fail(no_trace_message)
end.
+expect(Validator, State0) when is_function(Validator) ->
+ receive
+ M ->
+ case Validator(M, State0) of
+ expected ->
+ ok = io:format("Expected and got ~p", [abbr(M)]);
+ {next, State} ->
+ ok = io:format("Expected and got ~p", [abbr(M)]),
+ expect(Validator, State);
+ {unexpected,Message} ->
+ io:format("Expected ~p; got ~p", [abbr(Message),abbr(M)]),
+ ct:fail({unexpected,abbr([M|flush()])})
+ end
+ after 5000 ->
+ io:format("Expected ~p; got nothing", [abbr(Validator('_'))]),
+ ct:fail(no_trace_message)
+ end.
+
trace_info(What, Key) ->
get(tracer) ! {apply,self(),{erlang,trace_info,[What,Key]}},
Res = receive
diff --git a/erts/emulator/test/dirty_bif_SUITE.erl b/erts/emulator/test/dirty_bif_SUITE.erl
index 4f5ad0295a..2ded862b8a 100644
--- a/erts/emulator/test/dirty_bif_SUITE.erl
+++ b/erts/emulator/test/dirty_bif_SUITE.erl
@@ -397,7 +397,9 @@ dirty_process_trace(Config) when is_list(Config) ->
access_dirty_process(
Config,
fun() ->
- erlang:trace_pattern({erts_debug,dirty_io,2},
+ %% BIFs can only be traced when their modules are loaded.
+ code:ensure_loaded(erts_debug),
+ 1 = erlang:trace_pattern({erts_debug,dirty_io,2},
[{'_',[],[{return_trace}]}],
[local,meta]),
ok
diff --git a/erts/emulator/test/esock_ttest/esock-ttest b/erts/emulator/test/esock_ttest/esock-ttest
index 9adc51fc8b..2ded557484 100755
--- a/erts/emulator/test/esock_ttest/esock-ttest
+++ b/erts/emulator/test/esock_ttest/esock-ttest
@@ -203,7 +203,7 @@ process_client_args(["--max-outstanding", Max|Args], State) ->
end;
process_client_args(["--scon", Server|Args], State) ->
- case string:tokens(Server, [$:]) of
+ case string:split(Server, ":", trailing) of
[AddrStr,PortStr] ->
Addr = case inet:parse_address(AddrStr) of
{ok, A} ->
@@ -343,9 +343,9 @@ exec(#{role := client,
runtime := RunTime}) ->
case socket_test_ttest_tcp_client_socket:start(true,
Async,
+ Active,
Method,
ServerInfo,
- Active,
MsgID, MaxOutstanding,
RunTime) of
{ok, Pid} ->
diff --git a/erts/emulator/test/esock_ttest/esock-ttest-client b/erts/emulator/test/esock_ttest/esock-ttest-client
index 7c90ae6391..5ae05d03b8 100755
--- a/erts/emulator/test/esock_ttest/esock-ttest-client
+++ b/erts/emulator/test/esock_ttest/esock-ttest-client
@@ -20,23 +20,30 @@
# %CopyrightEnd%
#
+#
+# This is just a simple convenience wrapper to the esock-ttest.
+# That means that there are some options not available here.
+#
+
EMU=$ERL_TOP/erts/emulator
EMU_TEST=$EMU/test
ESOCK_TTEST=$EMU_TEST/esock_ttest
RUNTIME=30
+# RUNTIME=60
+# RUNTIME=600
if [ $# = 3 ]; then
MSGID=$1
SERVER_INFO=$2:$3
ITERATIONS="\
- gen false $MSGID
- gen true $MSGID
- gen once $MSGID
- sock false $MSGID
- sock true $MSGID
- sock once $MSGID"
+ gen false $MSGID
+ gen true $MSGID
+ gen once $MSGID
+ sock false $MSGID --async
+ sock true $MSGID --async
+ sock once $MSGID --async"
else
if [ $# = 2 ]; then
@@ -44,9 +51,9 @@ else
SERVER_INFO=$2
ITERATIONS="\
- sock false $MSGID
- sock true $MSGID
- sock once $MSGID"
+ sock false $MSGID --async
+ sock true $MSGID --async
+ sock once $MSGID --async"
else
echo "Invalid number of args"
@@ -70,14 +77,14 @@ fi
# ---------------------------------------------------------------------------
echo "$ITERATIONS" |
- while read TRANSPORT ACTIVE MSG_ID; do
+ while read TRANSPORT ACTIVE MSG_ID ASYNC; do
echo ""
echo "=========== transport = $TRANSPORT, active = $ACTIVE, msg-id = $MSG_ID ==========="
# The /dev/null at the end is necessary because erlang "does things" with stdin
# and this case would cause the 'while read' to "fail" so that we only would
# loop one time
- $ESOCK_TTEST/esock-ttest --client --transport $TRANSPORT --active $ACTIVE --msg-id $MSG_ID --scon $SERVER_INFO --runtime $RUNTIME </dev/null
+ $ESOCK_TTEST/esock-ttest --client --transport $TRANSPORT $ASYNC --active $ACTIVE --msg-id $MSG_ID --scon $SERVER_INFO --runtime $RUNTIME </dev/null
echo ""
done
diff --git a/erts/emulator/test/esock_ttest/esock-ttest-server-sock b/erts/emulator/test/esock_ttest/esock-ttest-server-sock
index fc87499f09..c443d42e64 100755
--- a/erts/emulator/test/esock_ttest/esock-ttest-server-sock
+++ b/erts/emulator/test/esock_ttest/esock-ttest-server-sock
@@ -24,9 +24,10 @@ EMU=$ERL_TOP/erts/emulator
EMU_TEST=$EMU/test
ESOCK_TTEST=$EMU_TEST/esock_ttest
-# $1 - async - boolean()
-# $2 - active - once | boolean()
-if [ $# = 2 ]; then
+# $1 - async - boolean()
+# $2 - active - once | boolean()
+# [$3 - domain - inet (default) | inet6 | local]
+if [ $# -ge 2 ]; then
async=$1
active=$2
@@ -39,6 +40,11 @@ if [ $# = 2 ]; then
ACTIVE="--active $active"
+ if [ $# = 3 ]; then
+ DOMAIN="--domain $3"
+ fi
+
+
else
echo "<ERROR> Missing args: async and active"
echo ""
@@ -46,5 +52,5 @@ else
fi
-$ESOCK_TTEST/esock-ttest --server $ASYNC --transport sock $ACTIVE
+$ESOCK_TTEST/esock-ttest --server $DOMAIN $ASYNC --transport sock $ACTIVE
diff --git a/erts/emulator/test/fun_SUITE.erl b/erts/emulator/test/fun_SUITE.erl
index 2cbde621ce..ad8ef0feff 100644
--- a/erts/emulator/test/fun_SUITE.erl
+++ b/erts/emulator/test/fun_SUITE.erl
@@ -27,7 +27,7 @@
fun_to_port/1,t_phash/1,t_phash2/1,md5/1,
refc/1,refc_ets/1,refc_dist/1,
const_propagation/1,t_arity/1,t_is_function2/1,
- t_fun_info/1,t_fun_info_mfa/1]).
+ t_fun_info/1,t_fun_info_mfa/1,t_fun_to_list/1]).
-export([nothing/0]).
@@ -44,7 +44,7 @@ all() ->
equality, ordering, fun_to_port, t_phash,
t_phash2, md5, refc, refc_ets, refc_dist,
const_propagation, t_arity, t_is_function2, t_fun_info,
- t_fun_info_mfa].
+ t_fun_info_mfa,t_fun_to_list].
%% Test that the correct EXIT code is returned for all types of bad funs.
bad_apply(Config) when is_list(Config) ->
@@ -802,6 +802,12 @@ t_fun_info_mfa(Config) when is_list(Config) ->
{'EXIT',_} = (catch erlang:fun_info_mfa(id(d))),
ok.
+t_fun_to_list(Config) when is_list(Config) ->
+ "fun a:b/1" = erlang:fun_to_list(fun a:b/1),
+ "fun 'a-esc':'b-esc'/1" = erlang:fun_to_list(fun 'a-esc':'b-esc'/1),
+ "fun 'a-esc':b/1" = erlang:fun_to_list(fun 'a-esc':b/1),
+ "fun a:'b-esc'/1" = erlang:fun_to_list(fun a:'b-esc'/1),
+ ok.
bad_info(Term) ->
try erlang:fun_info(Term, module) of
diff --git a/erts/emulator/test/hibernate_SUITE.erl b/erts/emulator/test/hibernate_SUITE.erl
index a20f306e04..d65d0ff2fd 100644
--- a/erts/emulator/test/hibernate_SUITE.erl
+++ b/erts/emulator/test/hibernate_SUITE.erl
@@ -46,12 +46,17 @@ all() ->
basic(Config) when is_list(Config) ->
Ref = make_ref(),
Info = {self(),Ref},
- ExpectedHeapSz = erts_debug:size([Info]),
+ ExpectedHeapSz = expected_heap_size([Info]),
Child = spawn_link(fun() -> basic_hibernator(Info) end),
hibernate_wake_up(100, ExpectedHeapSz, Child),
Child ! please_quit_now,
ok.
+expected_heap_size(Term) ->
+ %% When hibernating, an extra word will be allocated on the stack
+ %% for a continuation pointer.
+ erts_debug:size(Term) + 1.
+
hibernate_wake_up(0, _, _) -> ok;
hibernate_wake_up(N, ExpectedHeapSz, Child) ->
{heap_size,Before} = process_info(Child, heap_size),
@@ -142,7 +147,7 @@ whats_up_calc(A1, A2, A3, A4, A5, A6, A7, A8, A9, Acc) ->
dynamic_call(Config) when is_list(Config) ->
Ref = make_ref(),
Info = {self(),Ref},
- ExpectedHeapSz = erts_debug:size([Info]),
+ ExpectedHeapSz = expected_heap_size([Info]),
Child = spawn_link(fun() -> ?MODULE:dynamic_call_hibernator(Info, hibernate) end),
hibernate_wake_up(100, ExpectedHeapSz, Child),
Child ! please_quit_now,
diff --git a/erts/emulator/test/lttng_SUITE.erl b/erts/emulator/test/lttng_SUITE.erl
index 19c3844c40..f19047ba71 100644
--- a/erts/emulator/test/lttng_SUITE.erl
+++ b/erts/emulator/test/lttng_SUITE.erl
@@ -27,8 +27,7 @@
-export([t_lttng_list/1,
t_carrier_pool/1,
t_memory_carrier/1,
- t_async_io_pool/1,
- t_driver_control_ready_async/1,
+ t_driver_control/1,
t_driver_start_stop/1,
t_driver_ready_input_output/1,
t_driver_timeout/1,
@@ -36,6 +35,8 @@
t_driver_flush/1,
t_scheduler_poll/1]).
+-export([ets_load/0]).
+
-include_lib("common_test/include/ct.hrl").
suite() ->
@@ -46,10 +47,9 @@ all() ->
[t_lttng_list,
t_memory_carrier,
t_carrier_pool,
- t_async_io_pool,
t_driver_start_stop,
t_driver_ready_input_output,
- t_driver_control_ready_async,
+ t_driver_control,
t_driver_timeout,
t_driver_caller,
t_driver_flush,
@@ -124,7 +124,7 @@ t_carrier_pool(Config) ->
true ->
ok = lttng_start_event("org_erlang_otp:carrier_pool*", Config),
- ok = ets_load(),
+ ok = ets_load(Config),
Res = lttng_stop_and_view(Config),
ok = check_tracepoint("org_erlang_otp:carrier_pool_get", Res),
@@ -141,7 +141,7 @@ t_memory_carrier(Config) ->
true ->
ok = lttng_start_event("org_erlang_otp:carrier_*", Config),
- ok = ets_load(),
+ ok = ets_load(Config),
Res = lttng_stop_and_view(Config),
ok = check_tracepoint("org_erlang_otp:carrier_destroy", Res),
@@ -149,70 +149,49 @@ t_memory_carrier(Config) ->
ok
end.
-%% org_erlang_otp:aio_pool_put
-%% org_erlang_otp:aio_pool_get
-t_async_io_pool(Config) ->
- case have_async_threads() of
- false ->
- {skip, "No Async Threads configured on system."};
- true ->
- ok = lttng_start_event("org_erlang_otp:aio_pool_*", Config),
-
- Path1 = proplists:get_value(priv_dir, Config),
- {ok, [[Path2]]} = init:get_argument(home),
- {ok, _} = file:list_dir(Path1),
- {ok, _} = file:list_dir(Path2),
- {ok, _} = file:list_dir(Path1),
- {ok, _} = file:list_dir(Path2),
-
- Res = lttng_stop_and_view(Config),
- ok = check_tracepoint("org_erlang_otp:aio_pool_put", Res),
- ok = check_tracepoint("org_erlang_otp:aio_pool_get", Res),
- ok
- end.
-
-
%% org_erlang_otp:driver_start
%% org_erlang_otp:driver_stop
t_driver_start_stop(Config) ->
ok = lttng_start_event("org_erlang_otp:driver_*", Config),
timer:sleep(500),
- Path = proplists:get_value(priv_dir, Config),
- Name = filename:join(Path, "sometext.txt"),
- Bin = txt(),
- ok = file:write_file(Name, Bin),
- {ok, Bin} = file:read_file(Name),
+ os:cmd("echo hello"),
timer:sleep(500),
Res = lttng_stop_and_view(Config),
ok = check_tracepoint("org_erlang_otp:driver_start", Res),
ok = check_tracepoint("org_erlang_otp:driver_stop", Res),
- ok = check_tracepoint("org_erlang_otp:driver_control", Res),
- ok = check_tracepoint("org_erlang_otp:driver_outputv", Res),
- ok = check_tracepoint("org_erlang_otp:driver_ready_async", Res),
+ ok = check_tracepoint("org_erlang_otp:driver_output", Res),
ok.
%% org_erlang_otp:driver_control
%% org_erlang_otp:driver_outputv
-%% org_erlang_otp:driver_ready_async
-t_driver_control_ready_async(Config) ->
+t_driver_control(Config) ->
ok = lttng_start_event("org_erlang_otp:driver_control", Config),
ok = lttng_start_event("org_erlang_otp:driver_outputv", Config),
- ok = lttng_start_event("org_erlang_otp:driver_ready_async", Config),
- Path = proplists:get_value(priv_dir, Config),
- Name = filename:join(Path, "sometext.txt"),
- Bin = txt(),
- ok = file:write_file(Name, Bin),
- {ok, Bin} = file:read_file(Name),
+
+ timer:sleep(500),
+ Me = self(),
+ Pid = spawn_link(fun() -> tcp_server(Me, active) end),
+ receive {Pid, accept} -> ok end,
+ Bin = txt(),
+ Sz = byte_size(Bin),
+
+ {ok, Sock} = gen_tcp:connect("localhost", 5679, [binary, {packet, 2}]),
+ ok = gen_tcp:send(Sock, <<Sz:16, Bin/binary>>),
+ ok = gen_tcp:send(Sock, <<Sz:16, Bin/binary>>),
+ ok = gen_tcp:close(Sock),
+ receive {Pid, done} -> ok end,
+
+ timer:sleep(500),
Res = lttng_stop_and_view(Config),
ok = check_tracepoint("org_erlang_otp:driver_control", Res),
ok = check_tracepoint("org_erlang_otp:driver_outputv", Res),
- ok = check_tracepoint("org_erlang_otp:driver_ready_async", Res),
ok.
%% org_erlang_otp:driver_ready_input
%% org_erlang_otp:driver_ready_output
t_driver_ready_input_output(Config) ->
ok = lttng_start_event("org_erlang_otp:driver_ready_*", Config),
+
timer:sleep(500),
Me = self(),
Pid = spawn_link(fun() -> tcp_server(Me, active) end),
@@ -290,8 +269,27 @@ t_driver_caller(Config) ->
t_scheduler_poll(Config) ->
ok = lttng_start_event("org_erlang_otp:scheduler_poll", Config),
+ N = 100,
+
+ Me = self(),
+ Pid = spawn_link(fun() -> tcp_server(Me, {active, N*2}) end),
+ receive {Pid, accept} -> ok end,
+
+ %% We want to create a scenario where the fd is moved into a scheduler
+ %% pollset, this means we have to send many small packages to the
+ %% same socket, but not fast enough for them to all arrive at the
+ %% same time.
+ {ok, Sock} = gen_tcp:connect("localhost", 5679, [binary, {packet, 2}]),
+ [begin gen_tcp:send(Sock,txt()), receive ok -> ok end end || _ <- lists:seq(1,N)],
+
ok = memory_load(),
+ [begin gen_tcp:send(Sock,txt()), receive ok -> ok end end || _ <- lists:seq(1,N)],
+
+ ok = gen_tcp:close(Sock),
+ Pid ! die,
+ receive {Pid, done} -> ok end,
+
Res = lttng_stop_and_view(Config),
ok = check_tracepoint("org_erlang_otp:scheduler_poll", Res),
ok.
@@ -335,8 +333,37 @@ chk_caller(Port, Callback, ExpectedCaller) ->
ExpectedCaller = Caller
end.
+memory_load() ->
+ Me = self(),
+ Pids0 = [spawn_link(fun() -> memory_loop(Me, 20, <<42>>) end) || _ <- lists:seq(1,30)],
+ timer:sleep(50),
+ Pids1 = [spawn_link(fun() -> memory_loop(Me, 20, <<42>>) end) || _ <- lists:seq(1,30)],
+ [receive {Pid, done} -> ok end || Pid <- Pids0 ++ Pids1],
+ timer:sleep(500),
+ ok.
+
+memory_loop(Parent, N, Bin) ->
+ memory_loop(Parent, N, Bin, []).
+
+memory_loop(Parent, 0, _Bin, _) ->
+ Parent ! {self(), done};
+memory_loop(Parent, N, Bin0, Ls) ->
+ Bin = binary:copy(<<Bin0/binary, Bin0/binary>>),
+ memory_loop(Parent, N - 1, Bin, [a,b,c|Ls]).
+
+ets_load(Config) ->
+
+ %% Have to do on a fresh node to guarantee that carriers are created
+ {ok,Node} = start_node(Config),
+
+ Res = rpc:call(Node, ?MODULE, ets_load, []),
+
+ stop_node(Node),
+
+ Res.
ets_load() ->
+
Tid = ets:new(ets_load, [public,set]),
N = erlang:system_info(schedulers_online),
Pids = [spawn_link(fun() -> ets_shuffle(Tid) end) || _ <- lists:seq(1,N)],
@@ -368,27 +395,6 @@ ets_shuffle(Tid, I, N, Data, Data0) ->
ets_shuffle(Tid, I - 1, N, Data1, Data0)
end.
-
-
-
-memory_load() ->
- Me = self(),
- Pids0 = [spawn_link(fun() -> memory_loop(Me, 20, <<42>>) end) || _ <- lists:seq(1,30)],
- timer:sleep(50),
- Pids1 = [spawn_link(fun() -> memory_loop(Me, 20, <<42>>) end) || _ <- lists:seq(1,30)],
- [receive {Pid, done} -> ok end || Pid <- Pids0 ++ Pids1],
- timer:sleep(500),
- ok.
-
-memory_loop(Parent, N, Bin) ->
- memory_loop(Parent, N, Bin, []).
-
-memory_loop(Parent, 0, _Bin, _) ->
- Parent ! {self(), done};
-memory_loop(Parent, N, Bin0, Ls) ->
- Bin = binary:copy(<<Bin0/binary, Bin0/binary>>),
- memory_loop(Parent, N - 1, Bin, [a,b,c|Ls]).
-
tcp_server(Pid, Type) ->
{ok, LSock} = gen_tcp:listen(5679, [binary,
{reuseaddr, true},
@@ -399,14 +405,19 @@ tcp_server(Pid, Type) ->
passive_no_read ->
receive die -> ok end;
active ->
- inet:setopts(Sock, [{active, once}, {packet,2}]),
+ inet:setopts(Sock, [{active, 2}, {packet,2}]),
receive Msg1 -> io:format("msg1: ~p~n", [Msg1]) end,
- inet:setopts(Sock, [{active, once}, {packet,2}]),
receive Msg2 -> io:format("msg2: ~p~n", [Msg2]) end,
ok = gen_tcp:close(Sock);
timeout ->
Res = gen_tcp:recv(Sock, 2000, 1000),
- io:format("res ~p~n", [Res])
+ io:format("res ~p~n", [Res]);
+ {active, Number} when is_number(Number) ->
+ inet:setopts(Sock, [{active, Number}, {packet,2}]),
+ [begin
+ receive _Msg1 -> Pid ! ok, io:format("msg ~p~n", [I]) end
+ end || I <- lists:seq(1,Number)],
+ ok = gen_tcp:close(Sock)
end,
Pid ! {self(), done},
ok.
@@ -497,3 +508,20 @@ cmd(Cmd) ->
Res = os:cmd(Cmd),
io:format(">> ~ts~n", [Res]),
{ok,Res}.
+
+start_node(Config) ->
+ start_node(Config, "").
+
+start_node(Config, Args) when is_list(Config) ->
+ Pa = filename:dirname(code:which(?MODULE)),
+ Name = list_to_atom(atom_to_list(?MODULE)
+ ++ "-"
+ ++ atom_to_list(proplists:get_value(testcase, Config))
+ ++ "-"
+ ++ integer_to_list(erlang:system_time(second))
+ ++ "-"
+ ++ integer_to_list(erlang:unique_integer([positive]))),
+ test_server:start_node(Name, slave, [{args, "-pa "++Pa++" "++Args}]).
+
+stop_node(Node) ->
+ test_server:stop_node(Node).
diff --git a/erts/emulator/test/match_spec_SUITE.erl b/erts/emulator/test/match_spec_SUITE.erl
index 21de6b1002..686b431876 100644
--- a/erts/emulator/test/match_spec_SUITE.erl
+++ b/erts/emulator/test/match_spec_SUITE.erl
@@ -198,7 +198,8 @@ caller_and_return_to(Config) ->
{trace,Tracee,call,{?MODULE,do_the_put,[test]},{?MODULE,do_put,1}},
{trace,Tracee,call,{erlang,integer_to_list,[1]},{?MODULE,do_the_put,1}},
{trace,Tracee,return_to,{?MODULE,do_the_put,1}},
- {trace,Tracee,call,{erlang,put,[test,"1"]},{?MODULE,do_put,1}},
+ {trace,Tracee,call,{erlang,put,[test,"1"]},{?MODULE,do_the_put,1}},
+ {trace,Tracee,return_to,{?MODULE,do_the_put,1}},
{trace,Tracee,return_to,{?MODULE,do_put,1}},
%% These last trace messages are a bit strange...
diff --git a/erts/emulator/test/node_container_SUITE.erl b/erts/emulator/test/node_container_SUITE.erl
index 7d2db5257c..c44693f8d9 100644
--- a/erts/emulator/test/node_container_SUITE.erl
+++ b/erts/emulator/test/node_container_SUITE.erl
@@ -51,7 +51,8 @@
unique_pid/1,
iter_max_procs/1,
magic_ref/1,
- dist_entry_gc/1]).
+ dist_entry_gc/1,
+ persistent_term/1]).
suite() ->
[{ct_hooks,[ts_install_cth]},
@@ -63,7 +64,8 @@ all() ->
node_table_gc, dist_link_refc, dist_monitor_refc,
node_controller_refc, ets_refc, match_spec_refc,
timer_refc, pid_wrap, port_wrap, bad_nc,
- unique_pid, iter_max_procs, magic_ref].
+ unique_pid, iter_max_procs,
+ magic_ref, persistent_term].
init_per_suite(Config) ->
Config.
@@ -906,6 +908,44 @@ magic_ref(Config) when is_list(Config) ->
true = erts_debug:get_internal_state({magic_ref,MRef2}),
ok.
+persistent_term(Config) when is_list(Config) ->
+ {ok, Node} = start_node(get_nodefirstname()),
+ Self = self(),
+ NcData = make_ref(),
+ RPid = spawn_link(Node,
+ fun () ->
+ Self ! {NcData, self(), hd(erlang:ports()), erlang:make_ref()}
+ end),
+ Data = receive
+ {NcData, RPid, RPort, RRef} ->
+ {RPid, RPort, RRef}
+ end,
+ unlink(RPid),
+ stop_node(Node),
+ Stuff = lists:foldl(fun (N, Acc) ->
+ persistent_term:put({?MODULE, N}, Data),
+ persistent_term:erase({?MODULE, N-1}),
+ node_container_refc_check(node()),
+ Data = persistent_term:get({?MODULE, N}),
+ try
+ persistent_term:get({?MODULE, N-1})
+ catch
+ error:badarg ->
+ ok
+ end,
+ case N rem 4 of
+ 0 -> [persistent_term:get({?MODULE, N})|Acc];
+ _ -> Acc
+ end
+ end,
+ [],
+ lists:seq(1, 100)),
+ persistent_term:erase({?MODULE, 100}),
+ receive after 2000 -> ok end, %% give literal gc some time to run...
+ node_container_refc_check(node()),
+ id(Stuff),
+ ok.
+
lost_pending_connection(Node) ->
_ = (catch erts_internal:new_connection(Node)),
diff --git a/erts/emulator/test/nofrag_SUITE.erl b/erts/emulator/test/nofrag_SUITE.erl
index 8b1519ae36..d4c74579e2 100644
--- a/erts/emulator/test/nofrag_SUITE.erl
+++ b/erts/emulator/test/nofrag_SUITE.erl
@@ -22,6 +22,11 @@
-include_lib("common_test/include/ct.hrl").
+%% This suite alters the return values of functions which breaks certain
+%% assumptions made by the compiler, so we have to turn off module-level type
+%% optimization to be safe.
+-compile(no_module_opt).
+
-export([all/0, suite/0,
error_handler/1,error_handler_apply/1,
error_handler_fixed_apply/1,error_handler_fun/1,
diff --git a/erts/emulator/test/socket_SUITE.erl b/erts/emulator/test/socket_SUITE.erl
index b80cc794e7..124ae09951 100644
--- a/erts/emulator/test/socket_SUITE.erl
+++ b/erts/emulator/test/socket_SUITE.erl
@@ -58,6 +58,7 @@
-include_lib("common_test/include/ct.hrl").
-include_lib("common_test/include/ct_event.hrl").
+-include("socket_test_evaluator.hrl").
%% Suite exports
-export([suite/0, all/0, groups/0]).
@@ -86,26 +87,52 @@
%% *** API async ***
api_a_connect_tcp4/1,
+ api_a_connect_tcp6/1,
api_a_sendto_and_recvfrom_udp4/1,
+ api_a_sendto_and_recvfrom_udp6/1,
api_a_sendmsg_and_recvmsg_udp4/1,
+ api_a_sendmsg_and_recvmsg_udp6/1,
api_a_send_and_recv_tcp4/1,
+ api_a_send_and_recv_tcp6/1,
api_a_sendmsg_and_recvmsg_tcp4/1,
+ api_a_sendmsg_and_recvmsg_tcp6/1,
api_a_recvfrom_cancel_udp4/1,
+ api_a_recvfrom_cancel_udp6/1,
api_a_recvmsg_cancel_udp4/1,
+ api_a_recvmsg_cancel_udp6/1,
api_a_accept_cancel_tcp4/1,
+ api_a_accept_cancel_tcp6/1,
api_a_recv_cancel_tcp4/1,
+ api_a_recv_cancel_tcp6/1,
api_a_recvmsg_cancel_tcp4/1,
+ api_a_recvmsg_cancel_tcp6/1,
api_a_mrecvfrom_cancel_udp4/1,
+ api_a_mrecvfrom_cancel_udp6/1,
api_a_mrecvmsg_cancel_udp4/1,
+ api_a_mrecvmsg_cancel_udp6/1,
api_a_maccept_cancel_tcp4/1,
+ api_a_maccept_cancel_tcp6/1,
api_a_mrecv_cancel_tcp4/1,
+ api_a_mrecv_cancel_tcp6/1,
api_a_mrecvmsg_cancel_tcp4/1,
+ api_a_mrecvmsg_cancel_tcp6/1,
%% *** API Options ***
api_opt_simple_otp_options/1,
api_opt_simple_otp_rcvbuf_option/1,
api_opt_simple_otp_controlling_process/1,
+ api_opt_sock_acceptconn_udp/1,
+ api_opt_sock_acceptconn_tcp/1,
+ api_opt_sock_acceptfilter/1,
+ api_opt_sock_bindtodevice/1,
+ api_opt_sock_broadcast/1,
+ api_opt_sock_debug/1,
+ api_opt_sock_domain/1,
+ api_opt_sock_dontroute/1,
+ api_opt_sock_error/1,
+ api_opt_sock_keepalive/1,
+ api_opt_sock_linger/1,
api_opt_ip_add_drop_membership/1,
%% *** API Operation Timeout ***
@@ -172,12 +199,16 @@
%% *** Traffic ***
traffic_send_and_recv_counters_tcp4/1,
+ traffic_send_and_recv_counters_tcp6/1,
traffic_send_and_recv_counters_tcpL/1,
traffic_sendmsg_and_recvmsg_counters_tcp4/1,
+ traffic_sendmsg_and_recvmsg_counters_tcp6/1,
traffic_sendmsg_and_recvmsg_counters_tcpL/1,
traffic_sendto_and_recvfrom_counters_udp4/1,
+ traffic_sendto_and_recvfrom_counters_udp6/1,
traffic_sendto_and_recvfrom_counters_udpL/1,
traffic_sendmsg_and_recvmsg_counters_udp4/1,
+ traffic_sendmsg_and_recvmsg_counters_udp6/1,
traffic_sendmsg_and_recvmsg_counters_udpL/1,
traffic_send_and_recv_chunks_tcp4/1,
@@ -526,30 +557,31 @@
]).
--include("socket_test_evaluator.hrl").
-
%% Internal exports
%% -export([]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
--define(BASIC_REQ, <<"hejsan">>).
--define(BASIC_REP, <<"hoppsan">>).
+-define(LIB, socket_test_lib).
+-define(TTEST_LIB, socket_test_ttest_lib).
+-define(LOGGER, socket_test_logger).
--define(DATA, <<"HOPPSAN">>). % Temporary
--define(FAIL(R), exit(R)).
+-define(BASIC_REQ, <<"hejsan">>).
+-define(BASIC_REP, <<"hoppsan">>).
--define(SLEEP(T), receive after T -> ok end).
+-define(DATA, <<"HOPPSAN">>). % Temporary
+-define(FAIL(R), exit(R)).
--define(MINS(M), timer:minutes(M)).
--define(SECS(S), timer:seconds(S)).
+-define(SLEEP(T), receive after T -> ok end).
--define(TT(T), ct:timetrap(T)).
+-define(MINS(M), timer:minutes(M)).
+-define(SECS(S), timer:seconds(S)).
+
+-define(TT(T), ct:timetrap(T)).
+
+-define(F(F, A), ?LIB:f(F, A)).
--define(LIB, socket_test_lib).
--define(TTEST_LIB, socket_test_ttest_lib).
--define(LOGGER, socket_test_logger).
-define(TPP_SMALL, lists:seq(1, 8)).
-define(TPP_MEDIUM, lists:flatten(lists:duplicate(1024, ?TPP_SMALL))).
@@ -594,80 +626,86 @@ use_group(Group, Env, Default) ->
groups() ->
- [{api, [], api_cases()},
- {api_misc, [], api_misc_cases()},
- {api_basic, [], api_basic_cases()},
- {api_async, [], api_async_cases()},
- {api_options, [], api_options_cases()},
- {api_options_otp, [], api_options_otp_cases()},
- {api_options_ip, [], api_options_ip_cases()},
- {api_op_with_timeout, [], api_op_with_timeout_cases()},
- {socket_close, [], socket_close_cases()},
- {sc_ctrl_proc_exit, [], sc_cp_exit_cases()},
- {sc_local_close, [], sc_lc_cases()},
- {sc_remote_close, [], sc_rc_cases()},
- {sc_remote_shutdown, [], sc_rs_cases()},
- {traffic, [], traffic_cases()},
- {traffic_counters, [], traffic_counters_cases()},
- {traffic_chunks, [], traffic_chunks_cases()},
- {traffic_pp_send_recv, [], traffic_pp_send_recv_cases()},
- {traffic_pp_sendto_recvfrom, [], traffic_pp_sendto_recvfrom_cases()},
- {traffic_pp_sendmsg_recvmsg, [], traffic_pp_sendmsg_recvmsg_cases()},
- {ttest, [], ttest_cases()},
- {ttest_sgenf, [], ttest_sgenf_cases()},
- {ttest_sgenf_cgen, [], ttest_sgenf_cgen_cases()},
- {ttest_sgenf_cgenf, [], ttest_sgenf_cgenf_cases()},
- {ttest_sgenf_cgeno, [], ttest_sgenf_cgeno_cases()},
- {ttest_sgenf_cgent, [], ttest_sgenf_cgent_cases()},
- {ttest_sgenf_csock, [], ttest_sgenf_csock_cases()},
- {ttest_sgenf_csockf, [], ttest_sgenf_csockf_cases()},
- {ttest_sgenf_csocko, [], ttest_sgenf_csocko_cases()},
- {ttest_sgenf_csockt, [], ttest_sgenf_csockt_cases()},
- {ttest_sgeno, [], ttest_sgeno_cases()},
- {ttest_sgeno_cgen, [], ttest_sgeno_cgen_cases()},
- {ttest_sgeno_cgenf, [], ttest_sgeno_cgenf_cases()},
- {ttest_sgeno_cgeno, [], ttest_sgeno_cgeno_cases()},
- {ttest_sgeno_cgent, [], ttest_sgeno_cgent_cases()},
- {ttest_sgeno_csock, [], ttest_sgeno_csock_cases()},
- {ttest_sgeno_csockf, [], ttest_sgeno_csockf_cases()},
- {ttest_sgeno_csocko, [], ttest_sgeno_csocko_cases()},
- {ttest_sgeno_csockt, [], ttest_sgeno_csockt_cases()},
- {ttest_sgent, [], ttest_sgent_cases()},
- {ttest_sgent_cgen, [], ttest_sgent_cgen_cases()},
- {ttest_sgent_cgenf, [], ttest_sgent_cgenf_cases()},
- {ttest_sgent_cgeno, [], ttest_sgent_cgeno_cases()},
- {ttest_sgent_cgent, [], ttest_sgent_cgent_cases()},
- {ttest_sgent_csock, [], ttest_sgent_csock_cases()},
- {ttest_sgent_csockf, [], ttest_sgent_csockf_cases()},
- {ttest_sgent_csocko, [], ttest_sgent_csocko_cases()},
- {ttest_sgent_csockt, [], ttest_sgent_csockt_cases()},
- {ttest_ssockf, [], ttest_ssockf_cases()},
- {ttest_ssockf_cgen, [], ttest_ssockf_cgen_cases()},
- {ttest_ssockf_cgenf, [], ttest_ssockf_cgenf_cases()},
- {ttest_ssockf_cgeno, [], ttest_ssockf_cgeno_cases()},
- {ttest_ssockf_cgent, [], ttest_ssockf_cgent_cases()},
- {ttest_ssockf_csock, [], ttest_ssockf_csock_cases()},
- {ttest_ssockf_csockf, [], ttest_ssockf_csockf_cases()},
- {ttest_ssockf_csocko, [], ttest_ssockf_csocko_cases()},
- {ttest_ssockf_csockt, [], ttest_ssockf_csockt_cases()},
- {ttest_ssocko, [], ttest_ssocko_cases()},
- {ttest_ssocko_cgen, [], ttest_ssocko_cgen_cases()},
- {ttest_ssocko_cgenf, [], ttest_ssocko_cgenf_cases()},
- {ttest_ssocko_cgeno, [], ttest_ssocko_cgeno_cases()},
- {ttest_ssocko_cgent, [], ttest_ssocko_cgent_cases()},
- {ttest_ssocko_csock, [], ttest_ssocko_csock_cases()},
- {ttest_ssocko_csockf, [], ttest_ssocko_csockf_cases()},
- {ttest_ssocko_csocko, [], ttest_ssocko_csocko_cases()},
- {ttest_ssocko_csockt, [], ttest_ssocko_csockt_cases()},
- {ttest_ssockt, [], ttest_ssockt_cases()},
- {ttest_ssockt_cgen, [], ttest_ssockt_cgen_cases()},
- {ttest_ssockt_cgenf, [], ttest_ssockt_cgenf_cases()},
- {ttest_ssockt_cgeno, [], ttest_ssockt_cgeno_cases()},
- {ttest_ssockt_cgent, [], ttest_ssockt_cgent_cases()},
- {ttest_ssockt_csock, [], ttest_ssockt_csock_cases()},
- {ttest_ssockt_csockf, [], ttest_ssockt_csockf_cases()},
- {ttest_ssockt_csocko, [], ttest_ssockt_csocko_cases()},
- {ttest_ssockt_csockt, [], ttest_ssockt_csockt_cases()}
+ [{api, [], api_cases()},
+ {api_misc, [], api_misc_cases()},
+ {api_basic, [], api_basic_cases()},
+ {api_async, [], api_async_cases()},
+ {api_options, [], api_options_cases()},
+ {api_options_otp, [], api_options_otp_cases()},
+ {api_options_socket, [], api_options_socket_cases()},
+ {api_option_sock_acceptconn, [], api_option_sock_acceptconn_cases()},
+ {api_options_ip, [], api_options_ip_cases()},
+ %% {api_options_ipv6, [], api_options_ipv6_cases()},
+ %% {api_options_tcp, [], api_options_tcp_cases()},
+ %% {api_options_udp, [], api_options_udp_cases()},
+ %% {api_options_sctp, [], api_options_sctp_cases()},
+ {api_op_with_timeout, [], api_op_with_timeout_cases()},
+ {socket_close, [], socket_close_cases()},
+ {sc_ctrl_proc_exit, [], sc_cp_exit_cases()},
+ {sc_local_close, [], sc_lc_cases()},
+ {sc_remote_close, [], sc_rc_cases()},
+ {sc_remote_shutdown, [], sc_rs_cases()},
+ {traffic, [], traffic_cases()},
+ {traffic_counters, [], traffic_counters_cases()},
+ {traffic_chunks, [], traffic_chunks_cases()},
+ {traffic_pp_send_recv, [], traffic_pp_send_recv_cases()},
+ {traffic_pp_sendto_recvfrom, [], traffic_pp_sendto_recvfrom_cases()},
+ {traffic_pp_sendmsg_recvmsg, [], traffic_pp_sendmsg_recvmsg_cases()},
+ {ttest, [], ttest_cases()},
+ {ttest_sgenf, [], ttest_sgenf_cases()},
+ {ttest_sgenf_cgen, [], ttest_sgenf_cgen_cases()},
+ {ttest_sgenf_cgenf, [], ttest_sgenf_cgenf_cases()},
+ {ttest_sgenf_cgeno, [], ttest_sgenf_cgeno_cases()},
+ {ttest_sgenf_cgent, [], ttest_sgenf_cgent_cases()},
+ {ttest_sgenf_csock, [], ttest_sgenf_csock_cases()},
+ {ttest_sgenf_csockf, [], ttest_sgenf_csockf_cases()},
+ {ttest_sgenf_csocko, [], ttest_sgenf_csocko_cases()},
+ {ttest_sgenf_csockt, [], ttest_sgenf_csockt_cases()},
+ {ttest_sgeno, [], ttest_sgeno_cases()},
+ {ttest_sgeno_cgen, [], ttest_sgeno_cgen_cases()},
+ {ttest_sgeno_cgenf, [], ttest_sgeno_cgenf_cases()},
+ {ttest_sgeno_cgeno, [], ttest_sgeno_cgeno_cases()},
+ {ttest_sgeno_cgent, [], ttest_sgeno_cgent_cases()},
+ {ttest_sgeno_csock, [], ttest_sgeno_csock_cases()},
+ {ttest_sgeno_csockf, [], ttest_sgeno_csockf_cases()},
+ {ttest_sgeno_csocko, [], ttest_sgeno_csocko_cases()},
+ {ttest_sgeno_csockt, [], ttest_sgeno_csockt_cases()},
+ {ttest_sgent, [], ttest_sgent_cases()},
+ {ttest_sgent_cgen, [], ttest_sgent_cgen_cases()},
+ {ttest_sgent_cgenf, [], ttest_sgent_cgenf_cases()},
+ {ttest_sgent_cgeno, [], ttest_sgent_cgeno_cases()},
+ {ttest_sgent_cgent, [], ttest_sgent_cgent_cases()},
+ {ttest_sgent_csock, [], ttest_sgent_csock_cases()},
+ {ttest_sgent_csockf, [], ttest_sgent_csockf_cases()},
+ {ttest_sgent_csocko, [], ttest_sgent_csocko_cases()},
+ {ttest_sgent_csockt, [], ttest_sgent_csockt_cases()},
+ {ttest_ssockf, [], ttest_ssockf_cases()},
+ {ttest_ssockf_cgen, [], ttest_ssockf_cgen_cases()},
+ {ttest_ssockf_cgenf, [], ttest_ssockf_cgenf_cases()},
+ {ttest_ssockf_cgeno, [], ttest_ssockf_cgeno_cases()},
+ {ttest_ssockf_cgent, [], ttest_ssockf_cgent_cases()},
+ {ttest_ssockf_csock, [], ttest_ssockf_csock_cases()},
+ {ttest_ssockf_csockf, [], ttest_ssockf_csockf_cases()},
+ {ttest_ssockf_csocko, [], ttest_ssockf_csocko_cases()},
+ {ttest_ssockf_csockt, [], ttest_ssockf_csockt_cases()},
+ {ttest_ssocko, [], ttest_ssocko_cases()},
+ {ttest_ssocko_cgen, [], ttest_ssocko_cgen_cases()},
+ {ttest_ssocko_cgenf, [], ttest_ssocko_cgenf_cases()},
+ {ttest_ssocko_cgeno, [], ttest_ssocko_cgeno_cases()},
+ {ttest_ssocko_cgent, [], ttest_ssocko_cgent_cases()},
+ {ttest_ssocko_csock, [], ttest_ssocko_csock_cases()},
+ {ttest_ssocko_csockf, [], ttest_ssocko_csockf_cases()},
+ {ttest_ssocko_csocko, [], ttest_ssocko_csocko_cases()},
+ {ttest_ssocko_csockt, [], ttest_ssocko_csockt_cases()},
+ {ttest_ssockt, [], ttest_ssockt_cases()},
+ {ttest_ssockt_cgen, [], ttest_ssockt_cgen_cases()},
+ {ttest_ssockt_cgenf, [], ttest_ssockt_cgenf_cases()},
+ {ttest_ssockt_cgeno, [], ttest_ssockt_cgeno_cases()},
+ {ttest_ssockt_cgent, [], ttest_ssockt_cgent_cases()},
+ {ttest_ssockt_csock, [], ttest_ssockt_csock_cases()},
+ {ttest_ssockt_csockf, [], ttest_ssockt_csockf_cases()},
+ {ttest_ssockt_csocko, [], ttest_ssockt_csocko_cases()},
+ {ttest_ssockt_csockt, [], ttest_ssockt_csockt_cases()}
%% {tickets, [], ticket_cases()}
].
@@ -705,26 +743,46 @@ api_basic_cases() ->
api_async_cases() ->
[
api_a_connect_tcp4,
+ api_a_connect_tcp6,
api_a_sendto_and_recvfrom_udp4,
+ api_a_sendto_and_recvfrom_udp6,
api_a_sendmsg_and_recvmsg_udp4,
+ api_a_sendmsg_and_recvmsg_udp6,
api_a_send_and_recv_tcp4,
+ api_a_send_and_recv_tcp6,
api_a_sendmsg_and_recvmsg_tcp4,
+ api_a_sendmsg_and_recvmsg_tcp6,
api_a_recvfrom_cancel_udp4,
+ api_a_recvfrom_cancel_udp6,
api_a_recvmsg_cancel_udp4,
+ api_a_recvmsg_cancel_udp6,
api_a_accept_cancel_tcp4,
+ api_a_accept_cancel_tcp6,
api_a_recv_cancel_tcp4,
+ api_a_recv_cancel_tcp6,
api_a_recvmsg_cancel_tcp4,
+ api_a_recvmsg_cancel_tcp6,
api_a_mrecvfrom_cancel_udp4,
+ api_a_mrecvfrom_cancel_udp6,
api_a_mrecvmsg_cancel_udp4,
+ api_a_mrecvmsg_cancel_udp6,
api_a_maccept_cancel_tcp4,
+ api_a_maccept_cancel_tcp6,
api_a_mrecv_cancel_tcp4,
- api_a_mrecvmsg_cancel_tcp4
+ api_a_mrecv_cancel_tcp6,
+ api_a_mrecvmsg_cancel_tcp4,
+ api_a_mrecvmsg_cancel_tcp6
].
api_options_cases() ->
[
{group, api_options_otp},
- {group, api_options_ip}
+ {group, api_options_socket},
+ {group, api_options_ip}% ,
+ %% {group, api_options_ipv6},
+ %% {group, api_options_tcp},
+ %% {group, api_options_udp},
+ %% {group, api_options_sctp}
].
api_options_otp_cases() ->
@@ -734,11 +792,35 @@ api_options_otp_cases() ->
api_opt_simple_otp_controlling_process
].
+api_options_socket_cases() ->
+ [
+ {group, api_option_sock_acceptconn},
+ api_opt_sock_acceptfilter,
+ api_opt_sock_bindtodevice,
+ api_opt_sock_broadcast,
+ api_opt_sock_debug,
+ api_opt_sock_domain,
+ api_opt_sock_dontroute,
+ api_opt_sock_error,
+ api_opt_sock_keepalive,
+ api_opt_sock_linger
+ ].
+
+api_option_sock_acceptconn_cases() ->
+ [
+ api_opt_sock_acceptconn_udp,
+ api_opt_sock_acceptconn_tcp
+ ].
+
api_options_ip_cases() ->
[
api_opt_ip_add_drop_membership
].
+%% api_options_ipv6_cases() ->
+%% [
+%% ].
+
api_op_with_timeout_cases() ->
[
api_to_connect_tcp4,
@@ -848,12 +930,16 @@ traffic_cases() ->
traffic_counters_cases() ->
[
traffic_send_and_recv_counters_tcp4,
+ traffic_send_and_recv_counters_tcp6,
traffic_send_and_recv_counters_tcpL,
traffic_sendmsg_and_recvmsg_counters_tcp4,
+ traffic_sendmsg_and_recvmsg_counters_tcp6,
traffic_sendmsg_and_recvmsg_counters_tcpL,
traffic_sendto_and_recvfrom_counters_udp4,
+ traffic_sendto_and_recvfrom_counters_udp6,
traffic_sendto_and_recvfrom_counters_udpL,
traffic_sendmsg_and_recvmsg_counters_udp4,
+ traffic_sendmsg_and_recvmsg_counters_udp6,
traffic_sendmsg_and_recvmsg_counters_udpL
].
@@ -1690,7 +1776,7 @@ quiet_mode(Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% A simple test case that tests that the global debug can be channged.
+%% A simple test case that tests that the global debug can be changed.
%% At the same time, it will test the info function (since it uses it
%% for verification).
@@ -1708,19 +1794,22 @@ api_m_debug(_Config) when is_list(_Config) ->
%% For some reason this test case triggers a gcc bug, which causes
%% a segfault, on an ancient Fedora 16 VM. So, check the version of gcc...
-%% Not pretty, but the simplest way to skip (without actually testing for the host).
+%% Not pretty, but the simplest way to skip (without actually testing
+%% for the host).
has_bugfree_gcc() ->
has_bugfree_gcc(os:type()).
%% Make sure we are on linux
has_bugfree_gcc({unix, linux}) ->
- has_bugfree_gcc2(os:cmd("cat /etc/issue"));
+ has_bugfree_gcc2(string:trim(os:cmd("cat /etc/issue")));
has_bugfree_gcc(_) ->
ok.
%% Make sure we are on Fedora 16
has_bugfree_gcc2("Fedora release 16 " ++ _) ->
has_bugfree_gcc3(os:cmd("gcc --version"));
+has_bugfree_gcc2("Welcome to SUSE Linux " ++ _) ->
+ has_bugfree_gcc4(os:cmd("gcc --version"));
has_bugfree_gcc2(_) ->
ok.
@@ -1729,6 +1818,11 @@ has_bugfree_gcc3("gcc (GCC) 4.6.3 20120306 (Red Hat 4.6.3-2" ++ _) ->
has_bugfree_gcc3(_) ->
ok.
+has_bugfree_gcc4("gcc (SUSE Linux) 4.3.2" ++ _) ->
+ skip("Buggy GCC");
+has_bugfree_gcc4(_) ->
+ ok.
+
api_m_debug() ->
i("get initial info"),
#{debug := D0} = socket:info(),
@@ -1746,6 +1840,7 @@ api_m_debug() ->
ok.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% %%
@@ -2768,7 +2863,7 @@ api_b_send_and_recv_tcp(InitState) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% Basically establish a TCP connection via an async connect.
+%% Basically establish a TCP connection via an async connect. IPv4.
api_a_connect_tcp4(suite) ->
[];
@@ -2778,25 +2873,48 @@ api_a_connect_tcp4(_Config) when is_list(_Config) ->
?TT(?SECS(10)),
tc_try(api_a_connect_tcp4,
fun() ->
- Connect = fun(Sock, SockAddr) ->
- socket:connect(Sock, SockAddr, nowait)
- end,
- Send = fun(Sock, Data) ->
- socket:send(Sock, Data)
- end,
- Recv = fun(Sock) ->
- socket:recv(Sock)
- end,
- InitState = #{domain => inet,
- connect => Connect,
- send => Send,
- recv => Recv},
- ok = api_a_connect_tcp(InitState)
+ ok = api_a_connect_tcpD(inet)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically establish a TCP connection via an async connect. IPv6.
+
+api_a_connect_tcp6(suite) ->
+ [];
+api_a_connect_tcp6(doc) ->
+ [];
+api_a_connect_tcp6(_Config) when is_list(_Config) ->
+ ?TT(?SECS(10)),
+ tc_try(api_a_connect_tcp6,
+ fun() -> has_support_ipv6() end,
+ fun() ->
+ ok = api_a_connect_tcpD(inet6)
end).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+api_a_connect_tcpD(Domain) ->
+ Connect = fun(Sock, SockAddr) ->
+ socket:connect(Sock, SockAddr, nowait)
+ end,
+ Send = fun(Sock, Data) ->
+ socket:send(Sock, Data)
+ end,
+ Recv = fun(Sock) ->
+ socket:recv(Sock)
+ end,
+ InitState = #{domain => Domain,
+ connect => Connect,
+ send => Send,
+ recv => Recv},
+ api_a_connect_tcp(InitState).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
api_a_connect_tcp(InitState) ->
process_flag(trap_exit, true),
ServerSeq =
@@ -3296,6 +3414,37 @@ api_a_sendto_and_recvfrom_udp4(_Config) when is_list(_Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Basically send and receive on an IPv6 UDP (dgram) socket using
+%% sendto and recvfrom. But we try to be async. That is, we use
+%% the 'nowait' value for the Timeout argument (and await the eventual
+%% select message). Note that we only do this for the recvfrom,
+%% since its much more difficult to "arrange" for sendto.
+%%
+api_a_sendto_and_recvfrom_udp6(suite) ->
+ [];
+api_a_sendto_and_recvfrom_udp6(doc) ->
+ [];
+api_a_sendto_and_recvfrom_udp6(_Config) when is_list(_Config) ->
+ ?TT(?SECS(5)),
+ tc_try(api_a_sendto_and_recvfrom_udp6,
+ fun() -> has_support_ipv6() end,
+ fun() ->
+ Send = fun(Sock, Data, Dest) ->
+ socket:sendto(Sock, Data, Dest)
+ end,
+ Recv = fun(Sock) ->
+ socket:recvfrom(Sock, 0, nowait)
+ end,
+ InitState = #{domain => inet6,
+ send => Send,
+ recv => Recv},
+ ok = api_a_send_and_recv_udp(InitState)
+ end).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
%% Basically send and receive on an IPv4 UDP (dgram) socket using
%% sendto and recvfrom. But we try to be async. That is, we use
%% the 'nowait' value for the Timeout argument (and await the eventual
@@ -3339,6 +3488,50 @@ api_a_sendmsg_and_recvmsg_udp4(_Config) when is_list(_Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Basically send and receive on an IPv6 UDP (dgram) socket using
+%% sendto and recvfrom. But we try to be async. That is, we use
+%% the 'nowait' value for the Timeout argument (and await the eventual
+%% select message). Note that we only do this for the recvmsg,
+%% since its much more difficult to "arrange" for sendmsg.
+%%
+api_a_sendmsg_and_recvmsg_udp6(suite) ->
+ [];
+api_a_sendmsg_and_recvmsg_udp6(doc) ->
+ [];
+api_a_sendmsg_and_recvmsg_udp6(_Config) when is_list(_Config) ->
+ ?TT(?SECS(5)),
+ tc_try(api_a_sendmsg_and_recvmsg_udp6,
+ fun() -> has_support_ipv6() end,
+ fun() ->
+ Send = fun(Sock, Data, Dest) ->
+ MsgHdr = #{addr => Dest,
+ %% ctrl => CMsgHdrs,
+ iov => [Data]},
+ socket:sendmsg(Sock, MsgHdr)
+ end,
+ Recv = fun(Sock) ->
+ case socket:recvmsg(Sock, nowait) of
+ {ok, #{addr := Source,
+ iov := [Data]}} ->
+ {ok, {Source, Data}};
+ {ok, _} = OK ->
+ OK;
+ {select, _} = SELECT ->
+ SELECT;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end,
+ InitState = #{domain => inet6,
+ send => Send,
+ recv => Recv},
+ ok = api_a_send_and_recv_udp(InitState)
+ end).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
api_a_send_and_recv_udp(InitState) ->
ServerSeq =
[
@@ -3788,6 +3981,37 @@ api_a_send_and_recv_tcp4(_Config) when is_list(_Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Basically send and receive using the "common" functions (send and recv)
+%% on an IPv6 TCP (stream) socket. But we try to be async. That is, we use
+%% the 'nowait' value for the Timeout argument (and await the eventual
+%% select message). Note that we only do this for the recv,
+%% since its much more difficult to "arrange" for send.
+%% We *also* test async for accept.
+api_a_send_and_recv_tcp6(suite) ->
+ [];
+api_a_send_and_recv_tcp6(doc) ->
+ [];
+api_a_send_and_recv_tcp6(_Config) when is_list(_Config) ->
+ ?TT(?SECS(10)),
+ tc_try(api_a_send_and_recv_tcp6,
+ fun() -> has_support_ipv6() end,
+ fun() ->
+ Send = fun(Sock, Data) ->
+ socket:send(Sock, Data)
+ end,
+ Recv = fun(Sock) ->
+ socket:recv(Sock, 0, nowait)
+ end,
+ InitState = #{domain => inet6,
+ send => Send,
+ recv => Recv},
+ ok = api_a_send_and_recv_tcp(InitState)
+ end).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
%% Basically send and receive using the msg functions (sendmsg and recvmsg)
%% on an IPv4 TCP (stream) socket. But we try to be async. That is, we use
%% the 'nowait' value for the Timeout argument (and await the eventual
@@ -3815,7 +4039,7 @@ api_a_sendmsg_and_recvmsg_tcp4(_Config) when is_list(_Config) ->
OK;
{select, _} = SELECT ->
SELECT;
- {error, _} = ERROR ->
+ {error, _} = ERROR ->
ERROR
end
end,
@@ -3826,6 +4050,49 @@ api_a_sendmsg_and_recvmsg_tcp4(_Config) when is_list(_Config) ->
end).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Basically send and receive using the msg functions (sendmsg and recvmsg)
+%% on an IPv6 TCP (stream) socket. But we try to be async. That is, we use
+%% the 'nowait' value for the Timeout argument (and await the eventual
+%% select message). Note that we only do this for the recvmsg,
+%% since its much more difficult to "arrange" for sendmsg.
+%% We *also* test async for accept.
+api_a_sendmsg_and_recvmsg_tcp6(suite) ->
+ [];
+api_a_sendmsg_and_recvmsg_tcp6(doc) ->
+ [];
+api_a_sendmsg_and_recvmsg_tcp6(_Config) when is_list(_Config) ->
+ ?TT(?SECS(10)),
+ tc_try(api_a_sendmsg_and_recvmsg_tcp6,
+ fun() -> has_support_ipv6() end,
+ fun() ->
+ Send = fun(Sock, Data) ->
+ MsgHdr = #{iov => [Data]},
+ socket:sendmsg(Sock, MsgHdr)
+ end,
+ Recv = fun(Sock) ->
+ case socket:recvmsg(Sock, nowait) of
+ {ok, #{addr := undefined,
+ iov := [Data]}} ->
+ {ok, Data};
+ {ok, _} = OK ->
+ OK;
+ {select, _} = SELECT ->
+ SELECT;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end,
+ InitState = #{domain => inet6,
+ send => Send,
+ recv => Recv},
+ ok = api_a_send_and_recv_tcp(InitState)
+ end).
+
+
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
api_a_send_and_recv_tcp(InitState) ->
@@ -4326,7 +4593,7 @@ api_a_send_and_recv_tcp(InitState) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Basically we make an async (Timeout = nowait) call to recvfrom,
-%% wait some time and then cancel.
+%% wait some time and then cancel. IPv4
%%
api_a_recvfrom_cancel_udp4(suite) ->
[];
@@ -4355,8 +4622,39 @@ api_a_recvfrom_cancel_udp4(_Config) when is_list(_Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Basically we make an async (Timeout = nowait) call to recvfrom,
+%% wait some time and then cancel. IPv6
+%%
+api_a_recvfrom_cancel_udp6(suite) ->
+ [];
+api_a_recvfrom_cancel_udp6(doc) ->
+ [];
+api_a_recvfrom_cancel_udp6(_Config) when is_list(_Config) ->
+ ?TT(?SECS(10)),
+ tc_try(api_a_recvfrom_cancel_udp6,
+ fun() -> has_support_ipv6() end,
+ fun() ->
+ Recv = fun(Sock) ->
+ case socket:recvfrom(Sock, 0, nowait) of
+ {ok, _} = OK ->
+ OK;
+ {select, _} = SELECT ->
+ SELECT;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end,
+ InitState = #{domain => inet6,
+ recv => Recv},
+ ok = api_a_recv_cancel_udp(InitState)
+ end).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
%% Basically we make an async (Timeout = nowait) call to recvmsg,
-%% wait some time and then cancel.
+%% wait some time and then cancel. IPv4
%%
api_a_recvmsg_cancel_udp4(suite) ->
[];
@@ -4385,6 +4683,37 @@ api_a_recvmsg_cancel_udp4(_Config) when is_list(_Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Basically we make an async (Timeout = nowait) call to recvmsg,
+%% wait some time and then cancel. IPv6
+%%
+api_a_recvmsg_cancel_udp6(suite) ->
+ [];
+api_a_recvmsg_cancel_udp6(doc) ->
+ [];
+api_a_recvmsg_cancel_udp6(_Config) when is_list(_Config) ->
+ ?TT(?SECS(10)),
+ tc_try(api_a_recvmsg_cancel_udp6,
+ fun() -> has_support_ipv6() end,
+ fun() ->
+ Recv = fun(Sock) ->
+ case socket:recvmsg(Sock, nowait) of
+ {ok, _} = OK ->
+ OK;
+ {select, _} = SELECT ->
+ SELECT;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end,
+ InitState = #{domain => inet6,
+ recv => Recv},
+ ok = api_a_recv_cancel_udp(InitState)
+ end).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
api_a_recv_cancel_udp(InitState) ->
ServerSeq =
[
@@ -4590,7 +4919,7 @@ api_a_recv_cancel_udp(InitState) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Basically we make an async (Timeout = nowait) call to accept,
-%% wait some time and then cancel.
+%% wait some time and then cancel. IPv4
%%
api_a_accept_cancel_tcp4(suite) ->
[];
@@ -4620,6 +4949,38 @@ api_a_accept_cancel_tcp4(_Config) when is_list(_Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Basically we make an async (Timeout = nowait) call to accept,
+%% wait some time and then cancel. IPv6
+%%
+api_a_accept_cancel_tcp6(suite) ->
+ [];
+api_a_accept_cancel_tcp6(doc) ->
+ [];
+api_a_accept_cancel_tcp6(_Config) when is_list(_Config) ->
+ ?TT(?SECS(10)),
+ tc_try(api_a_accept_cancel_tcp6,
+ fun() -> has_support_ipv6() end,
+ fun() ->
+ Accept = fun(Sock) ->
+ case socket:accept(Sock, nowait) of
+ {ok, _} = OK ->
+ OK;
+ {select, _} = SELECT ->
+ SELECT;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end,
+ InitState = #{domain => inet6,
+ accept => Accept},
+ ok = api_a_accept_cancel_tcp(InitState)
+ end).
+
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
api_a_accept_cancel_tcp(InitState) ->
process_flag(trap_exit, true),
ServerSeq =
@@ -4820,7 +5181,7 @@ api_a_accept_cancel_tcp(InitState) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Basically we make an async (Timeout = nowait) call to recv,
-%% wait some time and then cancel.
+%% wait some time and then cancel. IPv4
%%
api_a_recv_cancel_tcp4(suite) ->
[];
@@ -4842,8 +5203,32 @@ api_a_recv_cancel_tcp4(_Config) when is_list(_Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Basically we make an async (Timeout = nowait) call to recv,
+%% wait some time and then cancel. IPv6
+%%
+api_a_recv_cancel_tcp6(suite) ->
+ [];
+api_a_recv_cancel_tcp6(doc) ->
+ [];
+api_a_recv_cancel_tcp6(_Config) when is_list(_Config) ->
+ ?TT(?SECS(10)),
+ tc_try(api_a_recv_cancel_tcp6,
+ fun() -> has_support_ipv6() end,
+ fun() ->
+ Recv = fun(Sock) ->
+ socket:recv(Sock, 0, nowait)
+ end,
+ InitState = #{domain => inet6,
+ recv => Recv},
+ ok = api_a_recv_cancel_tcp(InitState)
+ end).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
%% Basically we make an async (Timeout = nowait) call to recvmsg,
-%% wait some time and then cancel.
+%% wait some time and then cancel. IPv4
%%
api_a_recvmsg_cancel_tcp4(suite) ->
[];
@@ -4865,6 +5250,30 @@ api_a_recvmsg_cancel_tcp4(_Config) when is_list(_Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Basically we make an async (Timeout = nowait) call to recvmsg,
+%% wait some time and then cancel. IPv6
+%%
+api_a_recvmsg_cancel_tcp6(suite) ->
+ [];
+api_a_recvmsg_cancel_tcp6(doc) ->
+ [];
+api_a_recvmsg_cancel_tcp6(_Config) when is_list(_Config) ->
+ ?TT(?SECS(10)),
+ tc_try(api_a_recvmsg_cancel_tcp6,
+ fun() -> has_support_ipv6() end,
+ fun() ->
+ Recv = fun(Sock) ->
+ socket:recvmsg(Sock, nowait)
+ end,
+ InitState = #{domain => inet6,
+ recv => Recv},
+ ok = api_a_recv_cancel_tcp(InitState)
+ end).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
api_a_recv_cancel_tcp(InitState) ->
process_flag(trap_exit, true),
ServerSeq =
@@ -5218,7 +5627,7 @@ api_a_recv_cancel_tcp(InitState) ->
%% Basically we make multiple async (Timeout = nowait) call(s) to recvfrom
%% (from *several* processes), wait some time and then cancel.
-%% This should result in abort messages to the 'other' processes.
+%% This should result in abort messages to the 'other' processes. IPv4
%%
api_a_mrecvfrom_cancel_udp4(suite) ->
[];
@@ -5247,9 +5656,41 @@ api_a_mrecvfrom_cancel_udp4(_Config) when is_list(_Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Basically we make multiple async (Timeout = nowait) call(s) to recvfrom
+%% (from *several* processes), wait some time and then cancel.
+%% This should result in abort messages to the 'other' processes. IPv6
+%%
+api_a_mrecvfrom_cancel_udp6(suite) ->
+ [];
+api_a_mrecvfrom_cancel_udp6(doc) ->
+ [];
+api_a_mrecvfrom_cancel_udp6(_Config) when is_list(_Config) ->
+ ?TT(?SECS(20)),
+ tc_try(api_a_mrecvfrom_cancel_udp6,
+ fun() -> has_support_ipv6() end,
+ fun() ->
+ Recv = fun(Sock) ->
+ case socket:recvfrom(Sock, 0, nowait) of
+ {ok, _} = OK ->
+ OK;
+ {select, _} = SELECT ->
+ SELECT;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end,
+ InitState = #{domain => inet6,
+ recv => Recv},
+ ok = api_a_mrecv_cancel_udp(InitState)
+ end).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
%% Basically we make multiple async (Timeout = nowait) call(s) to recvmsg
%% (from *several* processes), wait some time and then cancel.
-%% This should result in abort messages to the 'other' processes.
+%% This should result in abort messages to the 'other' processes. IPv4
%%
api_a_mrecvmsg_cancel_udp4(suite) ->
[];
@@ -5278,6 +5719,38 @@ api_a_mrecvmsg_cancel_udp4(_Config) when is_list(_Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Basically we make multiple async (Timeout = nowait) call(s) to recvmsg
+%% (from *several* processes), wait some time and then cancel.
+%% This should result in abort messages to the 'other' processes. IPv6
+%%
+api_a_mrecvmsg_cancel_udp6(suite) ->
+ [];
+api_a_mrecvmsg_cancel_udp6(doc) ->
+ [];
+api_a_mrecvmsg_cancel_udp6(_Config) when is_list(_Config) ->
+ ?TT(?SECS(20)),
+ tc_try(api_a_mrecvmsg_cancel_udp6,
+ fun() -> has_support_ipv6() end,
+ fun() ->
+ Recv = fun(Sock) ->
+ case socket:recvmsg(Sock, nowait) of
+ {ok, _} = OK ->
+ OK;
+ {select, _} = SELECT ->
+ SELECT;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end,
+ InitState = #{domain => inet6,
+ recv => Recv},
+ ok = api_a_mrecv_cancel_udp(InitState)
+ end).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
api_a_mrecv_cancel_udp(InitState) ->
ServerSeq =
[
@@ -5649,7 +6122,7 @@ api_a_mrecv_cancel_udp(InitState) ->
%% Basically we make multiple async (Timeout = nowait) call(s) to accept
%% (from *several* processes), wait some time and then cancel,
-%% This should result in abort messages to the 'other' processes.
+%% This should result in abort messages to the 'other' processes. IPv4
%%
api_a_maccept_cancel_tcp4(suite) ->
[];
@@ -5679,6 +6152,39 @@ api_a_maccept_cancel_tcp4(_Config) when is_list(_Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Basically we make multiple async (Timeout = nowait) call(s) to accept
+%% (from *several* processes), wait some time and then cancel,
+%% This should result in abort messages to the 'other' processes. IPv6
+%%
+api_a_maccept_cancel_tcp6(suite) ->
+ [];
+api_a_maccept_cancel_tcp6(doc) ->
+ [];
+api_a_maccept_cancel_tcp6(_Config) when is_list(_Config) ->
+ ?TT(?SECS(20)),
+ tc_try(api_a_maccept_cancel_tcp6,
+ fun() -> has_support_ipv6() end,
+ fun() ->
+ Accept = fun(Sock) ->
+ case socket:accept(Sock, nowait) of
+ {ok, _} = OK ->
+ OK;
+ {select, _} = SELECT ->
+ SELECT;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end,
+ InitState = #{domain => inet6,
+ accept => Accept},
+ ok = api_a_maccept_cancel_tcp(InitState)
+ end).
+
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
api_a_maccept_cancel_tcp(InitState) ->
process_flag(trap_exit, true),
ServerSeq =
@@ -6047,7 +6553,7 @@ api_a_maccept_cancel_tcp(InitState) ->
%% Basically we make multiple async (Timeout = nowait) call(s) to recv
%% (from *several* processes), wait some time and then cancel,
-%% This should result in abort messages to the 'other' processes.
+%% This should result in abort messages to the 'other' processes. IPv4
%%
api_a_mrecv_cancel_tcp4(suite) ->
[];
@@ -6069,9 +6575,34 @@ api_a_mrecv_cancel_tcp4(_Config) when is_list(_Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Basically we make multiple async (Timeout = nowait) call(s) to recv
+%% (from *several* processes), wait some time and then cancel,
+%% This should result in abort messages to the 'other' processes. IPv6
+%%
+api_a_mrecv_cancel_tcp6(suite) ->
+ [];
+api_a_mrecv_cancel_tcp6(doc) ->
+ [];
+api_a_mrecv_cancel_tcp6(_Config) when is_list(_Config) ->
+ ?TT(?SECS(20)),
+ tc_try(api_a_mrecv_cancel_tcp6,
+ fun() -> has_support_ipv6() end,
+ fun() ->
+ Recv = fun(Sock) ->
+ socket:recv(Sock, 0, nowait)
+ end,
+ InitState = #{domain => inet6,
+ recv => Recv},
+ ok = api_a_mrecv_cancel_tcp(InitState)
+ end).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
%% Basically we make multiple async (Timeout = nowait) call(s) to recvmsg
%% (from *several* processes), wait some time and then cancel,
-%% This should result in abort messages to the 'other' processes.
+%% This should result in abort messages to the 'other' processes. IPv4
%%
api_a_mrecvmsg_cancel_tcp4(suite) ->
[];
@@ -6093,6 +6624,31 @@ api_a_mrecvmsg_cancel_tcp4(_Config) when is_list(_Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Basically we make multiple async (Timeout = nowait) call(s) to recvmsg
+%% (from *several* processes), wait some time and then cancel,
+%% This should result in abort messages to the 'other' processes. IPv6
+%%
+api_a_mrecvmsg_cancel_tcp6(suite) ->
+ [];
+api_a_mrecvmsg_cancel_tcp6(doc) ->
+ [];
+api_a_mrecvmsg_cancel_tcp6(_Config) when is_list(_Config) ->
+ ?TT(?SECS(20)),
+ tc_try(api_a_mrecvmsg_cancel_tcp6,
+ fun() -> has_support_ipv6() end,
+ fun() ->
+ Recv = fun(Sock) ->
+ socket:recvmsg(Sock, nowait)
+ end,
+ InitState = #{domain => inet6,
+ recv => Recv},
+ ok = api_a_mrecv_cancel_tcp(InitState)
+ end).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
api_a_mrecv_cancel_tcp(InitState) ->
process_flag(trap_exit, true),
ServerSeq =
@@ -7834,6 +8390,1695 @@ api_opt_simple_otp_controlling_process() ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Tests the socket option acceptconn for UDP.
+%% This should be possible to get but not set.
+
+api_opt_sock_acceptconn_udp(suite) ->
+ [];
+api_opt_sock_acceptconn_udp(doc) ->
+ [];
+api_opt_sock_acceptconn_udp(_Config) when is_list(_Config) ->
+ ?TT(?SECS(30)),
+ tc_try(api_opt_sock_acceptconn_udp,
+ fun() ->
+ has_support_sock_acceptconn()
+ end,
+ fun() -> api_opt_sock_acceptconn_udp() end).
+
+
+
+api_opt_sock_acceptconn_udp() ->
+ Opt = acceptconn,
+ Set = fun(S, Val) ->
+ socket:setopt(S, socket, Opt, Val)
+ end,
+ Get = fun(S) ->
+ socket:getopt(S, socket, Opt)
+ end,
+
+ TesterSeq =
+ [
+ #{desc => "which local address",
+ cmd => fun(#{domain := Domain} = State) ->
+ LSA = which_local_socket_addr(Domain),
+ {ok, State#{local_sa => LSA}}
+ end},
+ #{desc => "create socket",
+ cmd => fun(#{domain := Domain} = State) ->
+ case socket:open(Domain, dgram, udp) of
+ {ok, Sock} ->
+ {ok, State#{sock => Sock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+ #{desc => "[get] verify socket (before bind)",
+ cmd => fun(#{sock := Sock} = _State) ->
+ case Get(Sock) of
+ {ok, false} ->
+ ?SEV_IPRINT("Expected Success: "
+ "Not accepting connections"),
+ ok;
+ {ok, true} ->
+ ?SEV_EPRINT("Unexpected Success: "
+ "Accepting connections"),
+ {error, {unexpected_success, {Opt, true}}};
+ {error, enoprotoopt = Reason} ->
+ %% On some platforms this is not accepted
+ %% for UDP, so skip this part (UDP).
+ ?SEV_EPRINT("Expected Failure: "
+ "~p => SKIP", [Reason]),
+ (catch socket:close(Sock)),
+ {skip, Reason};
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("Unexpected Failure: ~p",
+ [Reason]),
+ ERROR
+ end
+ end},
+ #{desc => "[set] verify socket (before bind)",
+ cmd => fun(#{sock := Sock} = _State) ->
+ case Set(Sock, true) of
+ {error, Reason} ->
+ ?SEV_IPRINT("Expected Failure: ~p",
+ [Reason]),
+ ok;
+ ok ->
+ ?SEV_EPRINT("Unexpected Success: "
+ "Set acceptconn (=true)"),
+ {error, unexpected_success}
+ end
+ end},
+
+ #{desc => "bind socket to local address",
+ cmd => fun(#{sock := Sock, local_sa := LSA} = _State) ->
+ case socket:bind(Sock, LSA) of
+ {ok, _} ->
+ ok;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+ ?SEV_SLEEP(?SECS(1)),
+
+ #{desc => "[get] verify socket (after bind)",
+ cmd => fun(#{sock := Sock} = _State) ->
+ case Get(Sock) of
+ {ok, false} ->
+ ?SEV_IPRINT("Expected Success: "
+ "Not accepting connections"),
+ ok;
+ {ok, true} ->
+ ?SEV_EPRINT("Unexpected Success: "
+ "Accepting connections"),
+ {error, {unexpected_success, {Opt, true}}};
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("Unexpected Failure: ~p",
+ [Reason]),
+ ERROR
+ end
+ end},
+ #{desc => "[set] verify socket (after bind)",
+ cmd => fun(#{sock := Sock} = _State) ->
+ case Set(Sock, true) of
+ {error, Reason} ->
+ ?SEV_IPRINT("Expected Failure: ~p",
+ [Reason]),
+ ok;
+ ok ->
+ ?SEV_EPRINT("Unexpected Success: "
+ "Set acceptconn (=true)"),
+ {error, unexpected_success}
+ end
+ end},
+
+ %% *** Termination ***
+ #{desc => "close socket",
+ cmd => fun(#{sock := Sock} = State) ->
+ socket:close(Sock),
+ {ok, maps:remove(sock, State)}
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+ Domain = inet,
+
+ i("start tester evaluator"),
+ InitState = #{domain => Domain},
+ Tester = ?SEV_START("tester", TesterSeq, InitState),
+
+ i("await evaluator(s)"),
+ ok = ?SEV_AWAIT_FINISH([Tester]).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Tests the socket option acceptconn for TCP.
+%% This should be possible to get but not set.
+
+api_opt_sock_acceptconn_tcp(suite) ->
+ [];
+api_opt_sock_acceptconn_tcp(doc) ->
+ [];
+api_opt_sock_acceptconn_tcp(_Config) when is_list(_Config) ->
+ ?TT(?SECS(30)),
+ tc_try(api_opt_sock_acceptconn_tcp,
+ fun() ->
+ has_support_sock_acceptconn()
+ end,
+ fun() -> api_opt_sock_acceptconn_tcp() end).
+
+
+
+api_opt_sock_acceptconn_tcp() ->
+ Opt = acceptconn,
+ Set = fun(S, Val) ->
+ socket:setopt(S, socket, Opt, Val)
+ end,
+ Get = fun(S) ->
+ socket:getopt(S, socket, Opt)
+ end,
+
+ TesterSeq =
+ [
+ #{desc => "which local address",
+ cmd => fun(#{domain := Domain} = State) ->
+ LSA = which_local_socket_addr(Domain),
+ {ok, State#{local_sa => LSA}}
+ end},
+
+ #{desc => "create listen socket",
+ cmd => fun(#{domain := Domain} = State) ->
+ case socket:open(Domain, stream, tcp) of
+ {ok, Sock} ->
+ {ok, State#{lsock => Sock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+ #{desc => "[get] verify listen socket (before bind)",
+ cmd => fun(#{lsock := Sock} = _State) ->
+ case Get(Sock) of
+ {ok, false} ->
+ ?SEV_IPRINT("Expected Success: "
+ "Not accepting connections"),
+ ok;
+ {ok, true} ->
+ ?SEV_EPRINT("Unexpected Success: "
+ "Accepting connections"),
+ {error, {unexpected_success, {Opt, true}}};
+ {error, enoprotoopt = Reason} ->
+ ?SEV_EPRINT("Expected Failure: "
+ "~p => SKIP", [Reason]),
+ (catch socket:close(Sock)),
+ {skip, Reason};
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("Unexpected Failure: ~p",
+ [Reason]),
+ ERROR
+ end
+ end},
+ #{desc => "[set] verify listen socket (before bind)",
+ cmd => fun(#{lsock := Sock} = _State) ->
+ case Set(Sock, true) of
+ {error, Reason} ->
+ ?SEV_IPRINT("Expected Failure: ~p", [Reason]),
+ ok;
+ ok ->
+ ?SEV_EPRINT("Unexpected Success: "
+ "Set acceptconn (=true)"),
+ {error, unexpected_success}
+ end
+ end},
+
+ ?SEV_SLEEP(?SECS(1)),
+
+ #{desc => "bind listen socket to local address",
+ cmd => fun(#{lsock := Sock, local_sa := LSA} = State) ->
+ case socket:bind(Sock, LSA) of
+ {ok, Port} ->
+ {ok, State#{server_sa => LSA#{port => Port}}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+ #{desc => "[get] verify listen socket (after bind)",
+ cmd => fun(#{lsock := Sock} = _State) ->
+ case Get(Sock) of
+ {ok, false} ->
+ ?SEV_IPRINT("Expected Success: "
+ "Not accepting connections"),
+ ok;
+ {ok, true} ->
+ ?SEV_EPRINT("Unexpected Success: "
+ "Accepting connections"),
+ {error, {unexpected_success, {Opt, true}}};
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]),
+ ERROR
+ end
+ end},
+ #{desc => "[set] verify listen socket (after bind)",
+ cmd => fun(#{lsock := Sock} = _State) ->
+ case Set(Sock, true) of
+ {error, Reason} ->
+ ?SEV_IPRINT("Expected Failure: ~p", [Reason]),
+ ok;
+ ok ->
+ ?SEV_EPRINT("Unexpected Success: "
+ "Set acceptconn (=true)"),
+ {error, unexpected_success}
+ end
+ end},
+
+ ?SEV_SLEEP(?SECS(1)),
+
+ #{desc => "make listen socket accept connections",
+ cmd => fun(#{lsock := Sock} = _State) ->
+ case socket:listen(Sock) of
+ ok ->
+ ok;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+ #{desc => "[get] verify listen socket (after listen)",
+ cmd => fun(#{lsock := Sock} = _State) ->
+ case Get(Sock) of
+ {ok, true} ->
+ ?SEV_IPRINT("Expected Success: "
+ "Accepting connections"),
+ ok;
+ {ok, false} ->
+ ?SEV_EPRINT("Unexpected Success: "
+ "Not accepting connections"),
+ {error, {unexpected_success, {Opt, false}}};
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]),
+ ERROR
+ end
+ end},
+ #{desc => "[set] verify listen socket (after listen)",
+ cmd => fun(#{lsock := Sock} = _State) ->
+ case Set(Sock, false) of
+ {error, Reason} ->
+ ?SEV_IPRINT("Expected Failure: ~p", [Reason]),
+ ok;
+ ok ->
+ ?SEV_EPRINT("Unexpected Success: "
+ "Set acceptconn (=false)"),
+ {error, unexpected_success}
+ end
+ end},
+
+ ?SEV_SLEEP(?SECS(1)),
+
+ #{desc => "create (connecting) socket",
+ cmd => fun(#{domain := Domain} = State) ->
+ case socket:open(Domain, stream, tcp) of
+ {ok, Sock} ->
+ {ok, State#{csockc => Sock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+ #{desc => "bind connecting socket to local address",
+ cmd => fun(#{csockc := Sock, local_sa := LSA} = _State) ->
+ case socket:bind(Sock, LSA) of
+ {ok, _Port} ->
+ ok;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+ ?SEV_SLEEP(?SECS(1)),
+
+ #{desc => "[get] verify connecting socket (before connect)",
+ cmd => fun(#{csockc := Sock} = _State) ->
+ case Get(Sock) of
+ {ok, false} ->
+ ?SEV_IPRINT("Expected Success: "
+ "Not accepting connections"),
+ ok;
+ {ok, true} ->
+ ?SEV_EPRINT("Unexpected Success: "
+ "Accepting connections"),
+ {error, {unexpected_success, {Opt, true}}};
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]),
+ ERROR
+ end
+ end},
+ #{desc => "[set] verify connecting socket (before connect)",
+ cmd => fun(#{csockc := Sock} = _State) ->
+ case Set(Sock, true) of
+ {error, Reason} ->
+ ?SEV_IPRINT("Expected Failure: ~p", [Reason]),
+ ok;
+ ok ->
+ ?SEV_EPRINT("Unexpected Success: "
+ "Set acceptconn (=true)"),
+ {error, unexpected_success}
+ end
+ end},
+
+ ?SEV_SLEEP(?SECS(1)),
+
+ #{desc => "connect to server",
+ cmd => fun(#{csockc := Sock, server_sa := SSA} = _State) ->
+ case socket:connect(Sock, SSA) of
+ ok ->
+ ok;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+ #{desc => "accept connection",
+ cmd => fun(#{lsock := Sock} = State) ->
+ case socket:accept(Sock) of
+ {ok, CSock} ->
+ {ok, State#{csocks => CSock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+ #{desc => "[get] verify connecting socket (after connect)",
+ cmd => fun(#{csockc := Sock} = _State) ->
+ case Get(Sock) of
+ {ok, false} ->
+ ?SEV_IPRINT("Expected Success: "
+ "Not accepting connections"),
+ ok;
+ {ok, true} ->
+ ?SEV_EPRINT("Unexpected Success: "
+ "Accepting connections"),
+ {error, {unexpected_success, {Opt, true}}};
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]),
+ ERROR
+ end
+ end},
+ #{desc => "[set] verify connecting socket (after connect)",
+ cmd => fun(#{csockc := Sock} = _State) ->
+ case Set(Sock, true) of
+ {error, Reason} ->
+ ?SEV_IPRINT("Expected Failure: ~p", [Reason]),
+ ok;
+ ok ->
+ ?SEV_EPRINT("Unexpected Success: "
+ "Set acceptconn (=true)"),
+ {error, unexpected_success}
+ end
+ end},
+
+ #{desc => "[get] verify connected socket",
+ cmd => fun(#{csocks := Sock} = _State) ->
+ case Get(Sock) of
+ {ok, false} ->
+ ?SEV_IPRINT("Expected Success: "
+ "Not accepting connections"),
+ ok;
+ {ok, true} ->
+ ?SEV_EPRINT("Unexpected Success: "
+ "Accepting connections"),
+ {error, {unexpected_success, {Opt, true}}};
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]),
+ ERROR
+ end
+ end},
+ #{desc => "[set] verify connected socket",
+ cmd => fun(#{csocks := Sock} = _State) ->
+ case Set(Sock, true) of
+ {error, Reason} ->
+ ?SEV_IPRINT("Expected Failure: ~p", [Reason]),
+ ok;
+ ok ->
+ ?SEV_EPRINT("Unexpected Success: "
+ "Set acceptconn (=true)"),
+ {error, unexpected_success}
+ end
+ end},
+
+ #{desc => "[get] verify listen socket (after connect)",
+ cmd => fun(#{lsock := Sock} = _State) ->
+ case Get(Sock) of
+ {ok, true} ->
+ ?SEV_IPRINT("Expected Success: "
+ "Accepting connections"),
+ ok;
+ {ok, false} ->
+ ?SEV_EPRINT("Unexpected Success: "
+ "Not accepting connections"),
+ {error, {unexpected_success, {Opt, false}}};
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]),
+ ERROR
+ end
+ end},
+ #{desc => "[set] verify listen socket (after connect)",
+ cmd => fun(#{lsock := Sock} = _State) ->
+ case Set(Sock, false) of
+ {error, Reason} ->
+ ?SEV_IPRINT("Expected Failure: ~p", [Reason]),
+ ok;
+ ok ->
+ ?SEV_EPRINT("Unexpected Success: "
+ "Set acceptconn (=false)"),
+ {error, unexpected_success}
+ end
+ end},
+
+ %% *** Termination ***
+ #{desc => "close connecting socket(s)",
+ cmd => fun(#{csockc := Sock} = State0) ->
+ socket:close(Sock),
+ State1 = maps:remove(csockc, State0),
+ State2 = maps:remove(csocks, State1), %% Auto-close
+ {ok, maps:remove(csockc, State2)}
+ end},
+ #{desc => "close listen socket",
+ cmd => fun(#{lsock := Sock} = State) ->
+ socket:close(Sock),
+ {ok, maps:remove(lsock, State)}
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+
+ Domain = inet,
+
+ i("start tester evaluator"),
+ InitState = #{domain => Domain},
+ Tester = ?SEV_START("tester", TesterSeq, InitState),
+
+ i("await evaluator(s)"),
+ ok = ?SEV_AWAIT_FINISH([Tester]).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Tests the socket option acceptfilter. PLACEHOLDER!
+
+api_opt_sock_acceptfilter(suite) ->
+ [];
+api_opt_sock_acceptfilter(doc) ->
+ [];
+api_opt_sock_acceptfilter(_Config) when is_list(_Config) ->
+ ?TT(?SECS(30)),
+ tc_try(api_opt_sock_acceptfilter,
+ fun() -> not_yet_implemented() end,
+ fun() -> ok end).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Tests the socket option bindtodevice.
+%% It has not always been possible to 'get' this option
+%% (atleast on linux).
+
+api_opt_sock_bindtodevice(suite) ->
+ [];
+api_opt_sock_bindtodevice(doc) ->
+ [];
+api_opt_sock_bindtodevice(_Config) when is_list(_Config) ->
+ ?TT(?SECS(30)),
+ tc_try(api_opt_sock_bindtodevice,
+ fun() -> has_support_sock_bindtodevice() end,
+ fun() -> api_opt_sock_bindtodevice() end).
+
+
+api_opt_sock_bindtodevice() ->
+ Opt = bindtodevice,
+ Set = fun(S, Val) ->
+ socket:setopt(S, socket, Opt, Val)
+ end,
+ Get = fun(S) ->
+ socket:getopt(S, socket, Opt)
+ end,
+
+ TesterSeq =
+ [
+ #{desc => "which local address",
+ cmd => fun(#{domain := Domain} = State) ->
+ case ?LIB:which_local_host_info(Domain) of
+ {ok, #{name := Name, addr := Addr}} ->
+ ?SEV_IPRINT("local host info (~p): "
+ "~n Name: ~p"
+ "~n Addr: ~p",
+ [Domain, Name, Addr]),
+ LSA = #{family => Domain,
+ addr => Addr},
+ {ok, State#{dev => Name,
+ local_sa => LSA}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "create UDP socket 1",
+ cmd => fun(#{domain := Domain} = State) ->
+ case socket:open(Domain, dgram, udp) of
+ {ok, Sock} ->
+ {ok, State#{usock1 => Sock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "create UDP socket 2",
+ cmd => fun(#{domain := Domain} = State) ->
+ case socket:open(Domain, dgram, udp) of
+ {ok, Sock} ->
+ {ok, State#{usock2 => Sock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "create TCP socket 1",
+ cmd => fun(#{domain := Domain} = State) ->
+ case socket:open(Domain, stream, tcp) of
+ {ok, Sock} ->
+ {ok, State#{tsock1 => Sock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "create TCP socket 2",
+ cmd => fun(#{domain := Domain} = State) ->
+ case socket:open(Domain, stream, tcp) of
+ {ok, Sock} ->
+ {ok, State#{tsock2 => Sock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+ #{desc => "[get] verify UDP socket 1 (before bindtodevice)",
+ cmd => fun(#{usock1 := Sock} = _State) ->
+ case Get(Sock) of
+ {ok, Dev} ->
+ ?SEV_IPRINT("Expected Success: ~p", [Dev]),
+ ok;
+ {error, enoprotoopt = Reason} ->
+ ?SEV_EPRINT("Unexpected Failure: ~p => SKIP",
+ [Reason]),
+ {skip, Reason};
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("Unexpected Failure: ~p",
+ [Reason]),
+ ERROR
+ end
+ end},
+ #{desc => "[get] verify UDP socket 2 (before bind)",
+ cmd => fun(#{usock2 := Sock} = _State) ->
+ case Get(Sock) of
+ {ok, Dev} ->
+ ?SEV_IPRINT("Expected Success: ~p", [Dev]),
+ ok;
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]),
+ ERROR
+ end
+ end},
+ #{desc => "[get] verify TCP socket 1 (before bindtodevice)",
+ cmd => fun(#{tsock1 := Sock} = _State) ->
+ case Get(Sock) of
+ {ok, Dev} ->
+ ?SEV_IPRINT("Expected Success: ~p", [Dev]),
+ ok;
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]),
+ ERROR
+ end
+ end},
+ #{desc => "[get] verify TCP socket 2 (before bind)",
+ cmd => fun(#{tsock2 := Sock} = _State) ->
+ case Get(Sock) of
+ {ok, Dev} ->
+ ?SEV_IPRINT("Expected Success: ~p", [Dev]),
+ ok;
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]),
+ ERROR
+ end
+ end},
+
+ ?SEV_SLEEP(?SECS(1)),
+
+ #{desc => "Bind UDP socket 1 to device",
+ cmd => fun(#{usock1 := Sock, dev := Dev} = State) ->
+ case Set(Sock, Dev) of
+ ok ->
+ ?SEV_IPRINT("Expected Success"),
+ ok;
+ {error, eperm = Reason} ->
+ ?SEV_IPRINT("Expected Failure: ~p", [Reason]),
+ (catch socket:close(Sock)),
+ {ok, State#{usock1 => skip}};
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]),
+ ERROR
+ end
+ end},
+ #{desc => "Bind UDP socket 2 to local address",
+ cmd => fun(#{usock2 := Sock, local_sa := LSA} = _State) ->
+ case socket:bind(Sock, LSA) of
+ {ok, _Port} ->
+ ?SEV_IPRINT("Expected Success"),
+ ok;
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]),
+ ERROR
+ end
+ end},
+ #{desc => "Bind TCP socket 1 to device",
+ cmd => fun(#{usock1 := USock1,
+ tsock1 := Sock, dev := Dev} = State) ->
+ case Set(Sock, Dev) of
+ ok ->
+ ?SEV_IPRINT("Expected Success"),
+ ok;
+ {error, eperm = Reason} when (USock1 =:= skip) ->
+ ?SEV_IPRINT("Expected Failure: ~p", [Reason]),
+ {skip, Reason};
+ {error, eperm = Reason} ->
+ ?SEV_IPRINT("Expected Failure: ~p", [Reason]),
+ (catch socket:close(Sock)),
+ {ok, State#{tsock1 => skip}};
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]),
+ ERROR
+ end
+ end},
+ #{desc => "Bind TCP socket 2 to local address",
+ cmd => fun(#{tsock2 := Sock, local_sa := LSA} = _State) ->
+ case socket:bind(Sock, LSA) of
+ {ok, _Port} ->
+ ?SEV_IPRINT("Expected Success"),
+ ok;
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]),
+ ERROR
+ end
+ end},
+
+ ?SEV_SLEEP(?SECS(1)),
+
+ #{desc => "[get] verify UDP socket 1 (after bindtodevice)",
+ cmd => fun(#{usock1 := skip} = _State) ->
+ ?SEV_IPRINT("SKIP'ed (previous eperm)"),
+ ok;
+ (#{usock1 := Sock} = _State) ->
+ case Get(Sock) of
+ {ok, Dev} ->
+ ?SEV_IPRINT("Expected Success: ~p", [Dev]),
+ ok;
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]),
+ ERROR
+ end
+ end},
+ #{desc => "[get] verify UDP socket 2 (after bind)",
+ cmd => fun(#{usock2 := Sock} = _State) ->
+ case Get(Sock) of
+ {ok, Dev} ->
+ ?SEV_IPRINT("Expected Success: ~p", [Dev]),
+ ok;
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]),
+ ERROR
+ end
+ end},
+ #{desc => "[get] verify TCP socket 1 (after bindtodevice)",
+ cmd => fun(#{tsock1 := skip} = _State) ->
+ ?SEV_IPRINT("SKIP'ed (previous eperm)"),
+ ok;
+ (#{tsock1 := Sock} = _State) ->
+ case Get(Sock) of
+ {ok, Dev} ->
+ ?SEV_IPRINT("Expected Success: ~p", [Dev]),
+ ok;
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]),
+ ERROR
+ end
+ end},
+ #{desc => "[get] verify TCP socket 2 (after bind)",
+ cmd => fun(#{tsock2 := Sock} = _State) ->
+ case Get(Sock) of
+ {ok, Dev} ->
+ ?SEV_IPRINT("Expected Success: ~p", [Dev]),
+ ok;
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("Unexpected Failure: ~p", [Reason]),
+ ERROR
+ end
+ end},
+
+ ?SEV_SLEEP(?SECS(1)),
+
+ %% *** Termination ***
+ #{desc => "close UDP socket 1",
+ cmd => fun(#{usock1 := skip} = State) ->
+ ?SEV_IPRINT("SKIP'ed (already closed)"),
+ {ok, maps:remove(usock1, State)};
+ (#{usock1 := Sock} = State) ->
+ socket:close(Sock),
+ {ok, maps:remove(usock1, State)}
+ end},
+ #{desc => "close UDP socket 2",
+ cmd => fun(#{usock2 := Sock} = State) ->
+ socket:close(Sock),
+ {ok, maps:remove(usock2, State)}
+ end},
+ #{desc => "close TCP socket 1",
+ cmd => fun(#{tsock1 := skip} = State) ->
+ ?SEV_IPRINT("SKIP'ed (already closed)"),
+ {ok, maps:remove(tsock1, State)};
+ (#{tsock1 := Sock} = State) ->
+ socket:close(Sock),
+ {ok, maps:remove(tsock1, State)}
+ end},
+ #{desc => "close TCP socket 2",
+ cmd => fun(#{tsock2 := Sock} = State) ->
+ socket:close(Sock),
+ {ok, maps:remove(tsock2, State)}
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+ Domain = inet,
+
+ i("start tester evaluator"),
+ InitState = #{domain => Domain},
+ Tester = ?SEV_START("tester", TesterSeq, InitState),
+
+ i("await evaluator(s)"),
+ ok = ?SEV_AWAIT_FINISH([Tester]).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Tests the socket option broadcast.
+%% Make it possible for datagram sockets to send packets to a broadcast
+%% address (IPv4 only).
+
+api_opt_sock_broadcast(suite) ->
+ [];
+api_opt_sock_broadcast(doc) ->
+ [];
+api_opt_sock_broadcast(_Config) when is_list(_Config) ->
+ ?TT(?SECS(30)),
+ tc_try(api_opt_sock_broadcast,
+ fun() -> has_support_sock_broadcast() end,
+ fun() -> api_opt_sock_broadcast() end).
+
+
+api_opt_sock_broadcast() ->
+ Opt = broadcast,
+ Set = fun(S, Val) when is_boolean(Val) ->
+ socket:setopt(S, socket, Opt, Val)
+ end,
+ Get = fun(S) ->
+ socket:getopt(S, socket, Opt)
+ end,
+
+ TesterSeq =
+ [
+ #{desc => "which local address",
+ cmd => fun(#{domain := Domain} = State) ->
+ case ?LIB:which_local_host_info(Domain) of
+ {ok, #{name := Name,
+ addr := Addr,
+ broadaddr := BAddr}} ->
+ ?SEV_IPRINT("local host info: "
+ "~n Name: ~p"
+ "~n Addr: ~p"
+ "~n Broadcast Addr: ~p",
+ [Name, Addr, BAddr]),
+ LSA = #{family => Domain,
+ addr => Addr},
+ BSA = #{family => Domain,
+ addr => BAddr},
+ {ok, State#{lsa => LSA,
+ bsa => BSA}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+ #{desc => "[socket 1] create UDP socket (listening 1)",
+ cmd => fun(#{domain := Domain} = State) ->
+ case socket:open(Domain, dgram, udp) of
+ {ok, Sock} ->
+ {ok, State#{sock1 => Sock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "[socket 1] Bind UDP socket (to limited broadcast address)",
+ cmd => fun(#{sock1 := Sock} = State) ->
+ BSA = #{family => inet,
+ addr => broadcast},
+ ?SEV_IPRINT("Try bind (socket 1) to: "
+ "~n ~p", [BSA]),
+ case socket:bind(Sock, BSA) of
+ {ok, Port} ->
+ ?SEV_IPRINT("Expected Success (bound): ~p",
+ [Port]),
+ {ok, State#{sa1 => BSA#{port => Port}}};
+ {error, eaddrnotavail = Reason} ->
+ ?SEV_IPRINT("~p => "
+ "SKIP limited broadcast test",
+ [Reason]),
+ {ok, State#{sa1 => skip}};
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("Unexpected Failure: ~p",
+ [Reason]),
+ ERROR
+ end
+ end},
+ #{desc => "[socket 1] UDP socket sockname",
+ cmd => fun(#{sa1 := skip} = _State) ->
+ ?SEV_IPRINT("SKIP limited broadcast test"),
+ ok;
+ (#{sock1 := Sock} = _State) ->
+ case socket:sockname(Sock) of
+ {ok, SA} ->
+ ?SEV_IPRINT("SA: ~p", [SA]),
+ ok;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+ ?SEV_SLEEP(?SECS(1)),
+
+ #{desc => "[socket 2] create UDP socket (listening 2)",
+ cmd => fun(#{domain := Domain} = State) ->
+ case socket:open(Domain, dgram, udp) of
+ {ok, Sock} ->
+ {ok, State#{sock2 => Sock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "[socket 2] Bind UDP socket (to subnet-directed broadcast address)",
+ cmd => fun(#{sock2 := Sock,
+ bsa := BSA} = State) ->
+ ?SEV_IPRINT("Try bind (socket 1) to: "
+ "~n ~p", [BSA]),
+ case socket:bind(Sock, BSA) of
+ {ok, Port} ->
+ ?SEV_IPRINT("Expected Success (bound): ~p",
+ [Port]),
+ {ok, State#{sa2 => BSA#{port => Port}}};
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("Unexpected Failure: ~p",
+ [Reason]),
+ ERROR
+ end
+ end},
+ #{desc => "[socket 2] UDP socket sockname",
+ cmd => fun(#{sock2 := Sock} = _State) ->
+ case socket:sockname(Sock) of
+ {ok, SA} ->
+ ?SEV_IPRINT("SA: ~p", [SA]),
+ ok;
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+ ?SEV_SLEEP(?SECS(1)),
+
+ #{desc => "[socket 3] create UDP socket (sender)",
+ cmd => fun(#{domain := Domain} = State) ->
+ case socket:open(Domain, dgram, udp) of
+ {ok, Sock} ->
+ {ok, State#{sock3 => Sock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "[socket 3][get] verify UDP socket (before bind and set)",
+ cmd => fun(#{sock3 := Sock} = _State) ->
+ case Get(Sock) of
+ {ok, false} ->
+ ?SEV_IPRINT("Expected Success: "
+ "broadcast not allowed"),
+ ok;
+ {ok, true} ->
+ ?SEV_IPRINT("Unexpected Success result: "
+ "broadcast already allowed"),
+ ok;
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("Unexpected Failure: ~p",
+ [Reason]),
+ ERROR
+ end
+ end},
+ #{desc => "[socket 3] Try make broadcast allowed",
+ cmd => fun(#{sock3 := Sock} = _State) ->
+ case Set(Sock, true) of
+ ok ->
+ ?SEV_IPRINT("Expected Success: "
+ "broadcast now allowed"),
+ ok;
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("Unexpected Failure: ~p",
+ [Reason]),
+ ERROR
+ end
+ end},
+ #{desc => "[socket 3] verify UDP socket broadcast allowed",
+ cmd => fun(#{sock3 := Sock} = _State) ->
+ case Get(Sock) of
+ {ok, true} ->
+ ?SEV_IPRINT("Expected Success: "
+ "broadcast allowed"),
+ ok;
+ {ok, false} ->
+ ?SEV_IPRINT("Unexpected Success result: "
+ "broadcast *not* allowed"),
+ ok;
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("Unexpected Failure: ~p",
+ [Reason]),
+ ERROR
+ end
+ end},
+ #{desc => "[socket 3] Bind UDP socket (to local address)",
+ cmd => fun(#{sock3 := Sock, lsa := LSA} = State) ->
+ ?SEV_IPRINT("Try bind (socket 2) to: "
+ "~n ~p", [LSA]),
+ case socket:bind(Sock, LSA) of
+ {ok, Port} ->
+ ?SEV_IPRINT("Expected Success (bound): ~p",
+ [Port]),
+ {ok, State#{sa3 => LSA#{port => Port}}};
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("Unexpected Failure: ~p",
+ [Reason]),
+ ERROR
+ end
+ end},
+ #{desc => "[socket 3] verify UDP socket (after set)",
+ cmd => fun(#{sock3 := Sock} = _State) ->
+ case Get(Sock) of
+ {ok, true} ->
+ ?SEV_IPRINT("Expected Success: "
+ "broadcast allowed"),
+ ok;
+ {ok, false} ->
+ ?SEV_IPRINT("Unexpected Success result: "
+ "broadcast not allowed"),
+ {error, not_allowed};
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("Unexpected Failure: ~p",
+ [Reason]),
+ ERROR
+ end
+ end},
+
+ ?SEV_SLEEP(?SECS(1)),
+
+ #{desc => "[socket 3] try send to limited broadcast address",
+ cmd => fun(#{sa1 := skip} = _State) ->
+ ?SEV_IPRINT("SKIP limited broadcast test"),
+ ok;
+ (#{sock3 := Sock,
+ sa1 := Dest} = _State) ->
+ Data = list_to_binary("hejsan"),
+ ?SEV_IPRINT("try send to bradcast address: "
+ "~n ~p", [Dest]),
+ case socket:sendto(Sock, Data, Dest) of
+ ok ->
+ ?SEV_IPRINT("Expected Success: "
+ "broadcast message sent"),
+ ok;
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("Unexpected Failure: ~p",
+ [Reason]),
+ ERROR
+ end
+ end},
+ #{desc => "[socket 1] try recv",
+ cmd => fun(#{sa1 := skip} = _State) ->
+ ?SEV_IPRINT("SKIP limited broadcast test"),
+ ok;
+ (#{sock1 := Sock} = State) ->
+ case socket:recvfrom(Sock, 0, 5000) of
+ {ok, _} ->
+ ?SEV_IPRINT("Expected Success: "
+ "received message"),
+ ok;
+ {error, timeout = Reason} ->
+ %% Some platforms seem to balk at this.
+ %% It spossible to bind to this, and
+ %% send to it, but no data is received.
+ %% At some point we should investigate...
+ %% For now, we just skip this part of
+ %% the test...
+ ?SEV_IPRINT("Unexpected Failure: ~p",
+ [Reason]),
+ {ok, State#{sa1 => skip}};
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("Unexpected Failure: ~p",
+ [Reason]),
+ ERROR
+ end
+ end},
+
+ ?SEV_SLEEP(?SECS(1)),
+
+ #{desc => "[socket 3] try send to subnet-directed broadcast address",
+ cmd => fun(#{sock3 := Sock,
+ sa2 := Dest} = _State) ->
+ Data = list_to_binary("hejsan"),
+ ?SEV_IPRINT("try send to bradcast address: "
+ "~n ~p", [Dest]),
+ case socket:sendto(Sock, Data, Dest) of
+ ok ->
+ ?SEV_IPRINT("Expected Success: "
+ "broadcast message sent"),
+ ok;
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("Unexpected Failure: ~p",
+ [Reason]),
+ ERROR
+ end
+ end},
+ #{desc => "[socket 2] try recv",
+ cmd => fun(#{sock2 := Sock, sa1 := SA1} = _State) ->
+ case socket:recvfrom(Sock, 0, 5000) of
+ {ok, _} ->
+ ?SEV_IPRINT("Expected Success: "
+ "received message"),
+ ok;
+ {error, timeout = Reason} when (SA1 =:= skip) ->
+ ?SEV_IPRINT("Unexpected Failure: ~p",
+ [Reason]),
+ {skip, "receive timeout"};
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("Unexpected Failure: ~p",
+ [Reason]),
+ ERROR
+ end
+ end},
+
+ %% *** Termination ***
+ #{desc => "[socket 3] close UDP socket (sender)",
+ cmd => fun(#{sock3 := Sock} = State0) ->
+ socket:close(Sock),
+ State1 = maps:remove(sock3, State0),
+ State2 = maps:remove(sa3, State1),
+ {ok, State2}
+ end},
+ #{desc => "[socket 2] close UDP socket (listener 2)",
+ cmd => fun(#{sock2 := Sock} = State0) ->
+ socket:close(Sock),
+ State1 = maps:remove(sock2, State0),
+ State2 = maps:remove(sa2, State1),
+ {ok, State2}
+ end},
+ #{desc => "[socket 1] close UDP socket (listener 1)",
+ cmd => fun(#{sock1 := Sock} = State0) ->
+ socket:close(Sock),
+ State1 = maps:remove(sock1, State0),
+ State2 = maps:remove(sa1, State1),
+ {ok, State2}
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+ Domain = inet,
+
+ i("start tester evaluator"),
+ InitState = #{domain => Domain},
+ Tester = ?SEV_START("tester", TesterSeq, InitState),
+
+ i("await evaluator(s)"),
+ ok = ?SEV_AWAIT_FINISH([Tester]).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Tests the socket option debug.
+%% On linux, this test requires that the user running the test to have
+%% CAP_NET_ADMIN capabilities or be root (effective user ID of 0),
+%% therefor we explicitly test for the result eacces when attempting to
+%% set, and skip if we get it.
+
+api_opt_sock_debug(suite) ->
+ [];
+api_opt_sock_debug(doc) ->
+ [];
+api_opt_sock_debug(_Config) when is_list(_Config) ->
+ ?TT(?SECS(10)),
+ tc_try(api_opt_sock_debug,
+ fun() -> has_support_sock_debug() end,
+ fun() -> api_opt_sock_debug() end).
+
+
+api_opt_sock_debug() ->
+ Opt = debug,
+ Set = fun(S, Val) when is_integer(Val) ->
+ socket:setopt(S, socket, Opt, Val)
+ end,
+ Get = fun(S) ->
+ socket:getopt(S, socket, Opt)
+ end,
+
+ TesterSeq =
+ [
+ #{desc => "which local address",
+ cmd => fun(#{domain := Domain} = State) ->
+ case ?LIB:which_local_host_info(Domain) of
+ {ok, #{name := Name,
+ addr := Addr,
+ broadaddr := BAddr}} ->
+ ?SEV_IPRINT("local host info: "
+ "~n Name: ~p"
+ "~n Addr: ~p"
+ "~n Broadcast Addr: ~p",
+ [Name, Addr, BAddr]),
+ LSA = #{family => Domain,
+ addr => Addr},
+ BSA = #{family => Domain,
+ addr => BAddr},
+ {ok, State#{lsa => LSA,
+ bsa => BSA}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+ #{desc => "create UDP socket",
+ cmd => fun(#{domain := Domain} = State) ->
+ case socket:open(Domain, dgram, udp) of
+ {ok, Sock} ->
+ {ok, State#{sock => Sock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "Get current debug value",
+ cmd => fun(#{sock := Sock} = State) ->
+ case Get(Sock) of
+ {ok, Debug} when is_integer(Debug) ->
+ ?SEV_IPRINT("Success: ~p", [Debug]),
+ {ok, State#{debug => Debug}};
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("Unexpected failure: ~p",
+ [Reason]),
+ ERROR
+ end
+ end},
+ #{desc => "Try enable socket debug",
+ cmd => fun(#{sock := Sock, debug := Debug} = State) ->
+ NewDebug = Debug + 1,
+ case Set(Sock, NewDebug) of
+ ok ->
+ ?SEV_IPRINT("Expected Success"),
+ {ok, State#{debug => NewDebug}};
+ {error, eacces = Reason} ->
+ ?SEV_EPRINT("NO ACCESS => SKIP"),
+ {skip, Reason};
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("Unexpected Failure: ~p",
+ [Reason]),
+ ERROR
+ end
+ end},
+ #{desc => "Get current (new) debug value",
+ cmd => fun(#{sock := Sock, debug := Debug} = _State) ->
+ case Get(Sock) of
+ {ok, Debug} when is_integer(Debug) ->
+ ?SEV_IPRINT("Success: ~p", [Debug]),
+ ok;
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("Unexpected failure: ~p",
+ [Reason]),
+ ERROR
+ end
+ end},
+
+ %% *** Termination ***
+ #{desc => "close UDP socket",
+ cmd => fun(#{sock := Sock} = State0) ->
+ socket:close(Sock),
+ State1 = maps:remove(sock, State0),
+ {ok, State1}
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+ Domain = inet,
+
+ i("start tester evaluator"),
+ InitState = #{domain => Domain},
+ Tester = ?SEV_START("tester", TesterSeq, InitState),
+
+ i("await evaluator(s)"),
+ ok = ?SEV_AWAIT_FINISH([Tester]).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Tests the socket option domain.
+%% This is a read only option. Also not available on all platforms.
+
+api_opt_sock_domain(suite) ->
+ [];
+api_opt_sock_domain(doc) ->
+ [];
+api_opt_sock_domain(_Config) when is_list(_Config) ->
+ ?TT(?SECS(10)),
+ tc_try(api_opt_sock_domain,
+ fun() -> has_support_sock_domain() end,
+ fun() -> api_opt_sock_domain() end).
+
+
+api_opt_sock_domain() ->
+ Opt = domain,
+ Get = fun(S) ->
+ socket:getopt(S, socket, Opt)
+ end,
+
+ TesterSeq =
+ [
+ #{desc => "which local address",
+ cmd => fun(#{domain := Domain} = State) ->
+ case ?LIB:which_local_host_info(Domain) of
+ {ok, #{name := Name,
+ addr := Addr,
+ broadaddr := BAddr}} ->
+ ?SEV_IPRINT("local host info: "
+ "~n Name: ~p"
+ "~n Addr: ~p"
+ "~n Broadcast Addr: ~p",
+ [Name, Addr, BAddr]),
+ LSA = #{family => Domain,
+ addr => Addr},
+ BSA = #{family => Domain,
+ addr => BAddr},
+ {ok, State#{lsa => LSA,
+ bsa => BSA}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+ #{desc => "create IPv4 UDP socket",
+ cmd => fun(#{domain := Domain} = State) ->
+ case socket:open(Domain, dgram, udp) of
+ {ok, Sock} ->
+ {ok, State#{usock => Sock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "Get domain for the UDP socket",
+ cmd => fun(#{domain := Domain, usock := Sock} = _State) ->
+ case Get(Sock) of
+ {ok, Domain} ->
+ ?SEV_IPRINT("Success: ~p", [Domain]),
+ ok;
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("Unexpected failure: ~p",
+ [Reason]),
+ ERROR
+ end
+ end},
+ #{desc => "create TCP socket",
+ cmd => fun(#{domain := Domain} = State) ->
+ case socket:open(Domain, stream, tcp) of
+ {ok, Sock} ->
+ {ok, State#{tsock => Sock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "Get domain for the TCP socket",
+ cmd => fun(#{domain := Domain, tsock := Sock} = _State) ->
+ case Get(Sock) of
+ {ok, Domain} ->
+ ?SEV_IPRINT("Success: ~p", [Domain]),
+ ok;
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("Unexpected failure: ~p",
+ [Reason]),
+ ERROR
+ end
+ end},
+
+ %% *** Termination ***
+ #{desc => "close UDP socket",
+ cmd => fun(#{usock := Sock} = State0) ->
+ socket:close(Sock),
+ State1 = maps:remove(usock, State0),
+ {ok, State1}
+ end},
+ #{desc => "close TCP socket",
+ cmd => fun(#{tsock := Sock} = State0) ->
+ socket:close(Sock),
+ State1 = maps:remove(tsock, State0),
+ {ok, State1}
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+ Domain = inet,
+
+ i("start tester evaluator"),
+ InitState = #{domain => Domain},
+ Tester = ?SEV_START("tester", TesterSeq, InitState),
+
+ i("await evaluator(s)"),
+ ok = ?SEV_AWAIT_FINISH([Tester]).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Tests the socket option dontroute.
+%% The man page has the following to say:
+%% "Don't send via a gateway, send only to directly connected hosts.
+%% The same effect can be achieved by setting the MSG_DONTROUTE
+%% flag on a socket send(2) operation."
+%% Since its "kind of" difficult to check if it actually takes an
+%% effect (you would need a gateway for that and a machine "on the
+%% other side"), we only test if we can set and get the value.
+%% Better then nothing.
+
+api_opt_sock_dontroute(suite) ->
+ [];
+api_opt_sock_dontroute(doc) ->
+ [];
+api_opt_sock_dontroute(_Config) when is_list(_Config) ->
+ ?TT(?SECS(10)),
+ tc_try(api_opt_sock_dontroute,
+ fun() -> has_support_sock_dontroute() end,
+ fun() -> api_opt_sock_dontroute() end).
+
+
+api_opt_sock_dontroute() ->
+ Opt = dontroute,
+ Set = fun(S, Val) when is_boolean(Val) ->
+ socket:setopt(S, socket, Opt, Val)
+ end,
+ Get = fun(S) ->
+ socket:getopt(S, socket, Opt)
+ end,
+
+ TesterSeq =
+ [
+ #{desc => "which local address",
+ cmd => fun(#{domain := Domain} = State) ->
+ case ?LIB:which_local_host_info(Domain) of
+ {ok, #{name := Name,
+ addr := Addr,
+ broadaddr := BAddr}} ->
+ ?SEV_IPRINT("local host info: "
+ "~n Name: ~p"
+ "~n Addr: ~p"
+ "~n Broadcast Addr: ~p",
+ [Name, Addr, BAddr]),
+ LSA = #{family => Domain,
+ addr => Addr},
+ BSA = #{family => Domain,
+ addr => BAddr},
+ {ok, State#{lsa => LSA,
+ bsa => BSA}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+ #{desc => "create UDP socket",
+ cmd => fun(#{domain := Domain} = State) ->
+ case socket:open(Domain, dgram, udp) of
+ {ok, Sock} ->
+ {ok, State#{sock => Sock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "Get current value",
+ cmd => fun(#{sock := Sock} = State) ->
+ case Get(Sock) of
+ {ok, Val} when is_boolean(Val) ->
+ ?SEV_IPRINT("Success: ~p", [Val]),
+ {ok, State#{dontroute => Val}};
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("Unexpected failure: ~p",
+ [Reason]),
+ ERROR
+ end
+ end},
+ #{desc => "Try change value",
+ cmd => fun(#{sock := Sock, dontroute := Current} = State) ->
+ New = not Current,
+ ?SEV_IPRINT("Change from ~p to ~p", [Current, New]),
+ case Set(Sock, New) of
+ ok ->
+ ?SEV_IPRINT("Expected Success"),
+ {ok, State#{dontroute => New}};
+ {error, eopnotsupp = Reason} ->
+ ?SEV_EPRINT("Expected Failure: ~p",
+ [Reason]),
+ {skip, Reason};
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("Unexpected Failure: ~p",
+ [Reason]),
+ ERROR
+ end
+ end},
+ #{desc => "Verify changed value",
+ cmd => fun(#{sock := Sock, dontroute := Val} = _State) ->
+ case Get(Sock) of
+ {ok, Val} ->
+ ?SEV_IPRINT("Expected Success"),
+ ok;
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("Unexpected failure: ~p",
+ [Reason]),
+ ERROR
+ end
+ end},
+
+ %% *** Termination ***
+ #{desc => "close UDP socket",
+ cmd => fun(#{sock := Sock} = State0) ->
+ socket:close(Sock),
+ State1 = maps:remove(sock, State0),
+ {ok, State1}
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+ Domain = inet,
+
+ i("start tester evaluator"),
+ InitState = #{domain => Domain},
+ Tester = ?SEV_START("tester", TesterSeq, InitState),
+
+ i("await evaluator(s)"),
+ ok = ?SEV_AWAIT_FINISH([Tester]).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Tests the socket option error. PLACEHOLDER!
+
+api_opt_sock_error(suite) ->
+ [];
+api_opt_sock_error(doc) ->
+ [];
+api_opt_sock_error(_Config) when is_list(_Config) ->
+ ?TT(?SECS(10)),
+ tc_try(api_opt_sock_error,
+ fun() -> not_yet_implemented() end,
+ fun() -> ok end).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Tests the socket option keepalive.
+%% This is bit tricky to test, partly because we have no control over
+%% the underlying TCP timeouts. So, for now, we just test that we can
+%% change the value.
+
+api_opt_sock_keepalive(suite) ->
+ [];
+api_opt_sock_keepalive(doc) ->
+ [];
+api_opt_sock_keepalive(_Config) when is_list(_Config) ->
+ ?TT(?SECS(10)),
+ tc_try(api_opt_sock_keepalive,
+ fun() -> has_support_sock_keepalive() end,
+ fun() -> api_opt_sock_keepalive() end).
+
+
+api_opt_sock_keepalive() ->
+ Opt = keepalive,
+ Set = fun(S, Val) when is_boolean(Val) ->
+ socket:setopt(S, socket, Opt, Val)
+ end,
+ Get = fun(S) ->
+ socket:getopt(S, socket, Opt)
+ end,
+
+ TesterSeq =
+ [
+ #{desc => "which local address",
+ cmd => fun(#{domain := Domain} = State) ->
+ case ?LIB:which_local_host_info(Domain) of
+ {ok, #{name := Name,
+ addr := Addr,
+ broadaddr := BAddr}} ->
+ ?SEV_IPRINT("local host info: "
+ "~n Name: ~p"
+ "~n Addr: ~p"
+ "~n Broadcast Addr: ~p",
+ [Name, Addr, BAddr]),
+ LSA = #{family => Domain,
+ addr => Addr},
+ BSA = #{family => Domain,
+ addr => BAddr},
+ {ok, State#{lsa => LSA,
+ bsa => BSA}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+
+ #{desc => "create TCP socket",
+ cmd => fun(#{domain := Domain} = State) ->
+ case socket:open(Domain, stream, tcp) of
+ {ok, Sock} ->
+ {ok, State#{sock => Sock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "Get current value",
+ cmd => fun(#{sock := Sock} = State) ->
+ case Get(Sock) of
+ {ok, Val} when is_boolean(Val) ->
+ ?SEV_IPRINT("Success: ~p", [Val]),
+ {ok, State#{keepalive => Val}};
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("Unexpected failure: ~p",
+ [Reason]),
+ ERROR
+ end
+ end},
+ #{desc => "Try change the value",
+ cmd => fun(#{sock := Sock, keepalive := Current} = State) ->
+ New = not Current,
+ ?SEV_IPRINT("Try change value from ~p to ~p",
+ [Current, New]),
+ case Set(Sock, New) of
+ ok ->
+ ?SEV_IPRINT("Expected Success"),
+ {ok, State#{keepalive => New}};
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("Unexpected Failure: ~p",
+ [Reason]),
+ ERROR
+ end
+ end},
+ #{desc => "Verify (new) current value",
+ cmd => fun(#{sock := Sock, keepalive := Val} = _State) ->
+ case Get(Sock) of
+ {ok, Val} ->
+ ?SEV_IPRINT("Expected Success (~p)", [Val]),
+ ok;
+ {ok, OtherVal} ->
+ ?SEV_IPRINT("Unexpected Success: ~p",
+ [OtherVal]),
+ {error, {unexpected_success_value,
+ Val, OtherVal}};
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("Unexpected failure: ~p",
+ [Reason]),
+ ERROR
+ end
+ end},
+
+ %% *** Termination ***
+ #{desc => "close UDP socket",
+ cmd => fun(#{sock := Sock} = State0) ->
+ socket:close(Sock),
+ State1 = maps:remove(sock, State0),
+ {ok, State1}
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+ Domain = inet,
+
+ i("start tester evaluator"),
+ InitState = #{domain => Domain},
+ Tester = ?SEV_START("tester", TesterSeq, InitState),
+
+ i("await evaluator(s)"),
+ ok = ?SEV_AWAIT_FINISH([Tester]).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Tests the socket option linger. PLACEHOLDER!
+
+api_opt_sock_linger(suite) ->
+ [];
+api_opt_sock_linger(doc) ->
+ [];
+api_opt_sock_linger(_Config) when is_list(_Config) ->
+ ?TT(?SECS(10)),
+ tc_try(api_opt_sock_linger,
+ fun() -> not_yet_implemented() end,
+ fun() -> ok end).
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
%% Tests that the add_mambership and drop_membership ip options work.
%% We create one server and two clients. The server only send messages,
%% the clients only receives messages.
@@ -7853,9 +10098,9 @@ api_opt_ip_add_drop_membership(_Config) when is_list(_Config) ->
?TT(?SECS(30)),
tc_try(api_opt_ip_add_drop_membership,
fun() ->
- has_ip_add_membership_support(),
- has_ip_drop_membership_support(),
- has_ip_multicast_support()
+ has_support_ip_add_membership(),
+ has_support_ip_drop_membership(),
+ has_support_ip_multicast()
end,
fun() -> api_opt_ip_add_drop_membership() end).
@@ -8072,8 +10317,8 @@ api_opt_ip_add_drop_membership() ->
],
- i("get multicast address"),
Domain = inet,
+ i("get multicast address"),
MAddr = which_ip_multicast_address(),
MSA = #{family => Domain, addr => MAddr},
@@ -8106,6 +10351,7 @@ which_multicast_address(Domain) ->
which_multicast_address2(Domain, WhichMAddr);
Type ->
+ %% Actually, what is "not supported". is netstat!
not_supported({multicast, Type})
end.
@@ -8115,20 +10361,28 @@ which_multicast_address(Domain) ->
which_multicast_address2(Domain, WhichMAddr) ->
IfName = which_local_host_ifname(Domain),
- try
- begin
- %% On some platforms the netstat barfs out some crap on stderr
- %% before the actual info...
- NetstatGroupsStr = os:cmd("netstat -g 2>/dev/null | grep " ++ IfName),
- NetstatGroups0 = string:tokens(NetstatGroupsStr, [$\n]),
- NetstatGroups = [string:tokens(G, [$ ]) || G <- NetstatGroups0],
- MAddrs = [WhichMAddr(NetstatGroup) || NetstatGroup <-
- NetstatGroups],
- which_multicast_address3(Domain, MAddrs)
- end
- catch
- C:E:S ->
- not_supported({multicast, {C,E,S}})
+ %% On some platforms the netstat barfs out some crap on stderr
+ %% before the actual info...
+ case os:cmd("netstat -g 2>/dev/null | grep " ++ IfName) of
+ [] ->
+ %% Can't figure out if we support multicast or not...
+ not_supported(no_netstat);
+ NetstatGroupsStr ->
+ try
+ begin
+ NetstatGroups0 = string:tokens(NetstatGroupsStr, [$\n]),
+ NetstatGroups = [string:tokens(G, [$ ]) ||
+ G <- NetstatGroups0],
+ MAddrs = [WhichMAddr(NetstatGroup) ||
+ NetstatGroup <- NetstatGroups],
+ which_multicast_address3(Domain, MAddrs)
+ end
+ catch
+ throw:E:_ ->
+ throw(E);
+ C:E:S ->
+ not_supported({multicast, {C,E,S}})
+ end
end.
which_multicast_address3(_Domain, []) ->
@@ -8146,8 +10400,8 @@ which_multicast_address3(Domain, [MAddrStr|MAddrs]) ->
end.
which_local_host_ifname(Domain) ->
- case which_local_host_info(Domain) of
- {ok, {Name, _Addr, _Flags}} ->
+ case ?LIB:which_local_host_info(Domain) of
+ {ok, #{name := Name}} ->
Name;
{error, Reason} ->
not_supported({multicast, Reason})
@@ -9222,6 +11476,7 @@ api_to_send_tcp6(doc) ->
[];
api_to_send_tcp6(_Config) when is_list(_Config) ->
tc_try(api_to_send_tcp6,
+ fun() -> has_support_ipv6() end,
fun() ->
not_yet_implemented()%% ,
%% ok = api_to_send_tcp(inet6)
@@ -9254,6 +11509,7 @@ api_to_sendto_udp6(doc) ->
[];
api_to_sendto_udp6(_Config) when is_list(_Config) ->
tc_try(api_to_sendto_udp6,
+ fun() -> has_support_ipv6() end,
fun() ->
not_yet_implemented()%% ,
%% ok = api_to_sendto_to_udp(inet6)
@@ -9286,6 +11542,7 @@ api_to_sendmsg_tcp6(doc) ->
[];
api_to_sendmsg_tcp6(_Config) when is_list(_Config) ->
tc_try(api_to_sendmsg_tcp6,
+ fun() -> has_support_ipv6() end,
fun() ->
not_yet_implemented()%% ,
%% ok = api_to_sendmsg_tcp(inet6)
@@ -9320,6 +11577,7 @@ api_to_recv_udp6(doc) ->
[];
api_to_recv_udp6(_Config) when is_list(_Config) ->
tc_try(api_to_recv_udp6,
+ fun() -> has_support_ipv6() end,
fun() ->
not_yet_implemented()%% ,
%% ok = api_to_recv_udp(inet6)
@@ -9899,7 +12157,7 @@ sc_cpe_socket_cleanup_tcp4(suite) ->
sc_cpe_socket_cleanup_tcp4(doc) ->
[];
sc_cpe_socket_cleanup_tcp4(_Config) when is_list(_Config) ->
- ?TT(?SECS(5)),
+ ?TT(?SECS(30)),
tc_try(sc_cpe_socket_cleanup_tcp4,
fun() ->
InitState = #{domain => inet,
@@ -9919,7 +12177,7 @@ sc_cpe_socket_cleanup_tcp6(suite) ->
sc_cpe_socket_cleanup_tcp6(doc) ->
[];
sc_cpe_socket_cleanup_tcp6(_Config) when is_list(_Config) ->
- ?TT(?SECS(5)),
+ ?TT(?SECS(30)),
tc_try(sc_cpe_socket_cleanup_tcp6,
fun() -> has_support_ipv6() end,
fun() ->
@@ -9940,7 +12198,7 @@ sc_cpe_socket_cleanup_tcpL(suite) ->
sc_cpe_socket_cleanup_tcpL(doc) ->
[];
sc_cpe_socket_cleanup_tcpL(_Config) when is_list(_Config) ->
- ?TT(?SECS(5)),
+ ?TT(?SECS(30)),
tc_try(sc_cpe_socket_cleanup_tcpL,
fun() -> has_support_unix_domain_socket() end,
fun() ->
@@ -9961,7 +12219,7 @@ sc_cpe_socket_cleanup_udp4(suite) ->
sc_cpe_socket_cleanup_udp4(doc) ->
[];
sc_cpe_socket_cleanup_udp4(_Config) when is_list(_Config) ->
- ?TT(?SECS(5)),
+ ?TT(?SECS(30)),
tc_try(sc_cpe_socket_cleanup_udp4,
fun() ->
InitState = #{domain => inet,
@@ -9982,7 +12240,7 @@ sc_cpe_socket_cleanup_udp6(suite) ->
sc_cpe_socket_cleanup_udp6(doc) ->
[];
sc_cpe_socket_cleanup_udp6(_Config) when is_list(_Config) ->
- ?TT(?SECS(5)),
+ ?TT(?SECS(30)),
tc_try(sc_cpe_socket_cleanup_udp6,
fun() -> has_support_ipv6() end,
fun() ->
@@ -10003,7 +12261,7 @@ sc_cpe_socket_cleanup_udpL(suite) ->
sc_cpe_socket_cleanup_udpL(doc) ->
[];
sc_cpe_socket_cleanup_udpL(_Config) when is_list(_Config) ->
- ?TT(?SECS(5)),
+ ?TT(?SECS(30)),
tc_try(sc_cpe_socket_cleanup_udpL,
fun() -> has_support_unix_domain_socket() end,
fun() ->
@@ -10109,8 +12367,15 @@ sc_cpe_socket_cleanup(InitState) ->
ERROR
end
end},
+
+ ?SEV_SLEEP(?SECS(5)),
+
%% The reason we get closed, is that as long as there is a ref to
%% the resource (socket), then it will not be garbage collected.
+ %% Note that its still a race that the nif has processed that the
+ %% "controlling process" has terminated. There really is no
+ %% proper timeout for this, but the 5 seconds "should" be enough...
+ %% We should really have some way to subscribe to socket events...
#{desc => "verify no socket (closed)",
cmd => fun(#{owner := Pid, sock := Sock} = _State) ->
case socket:getopt(Sock, otp, controlling_process) of
@@ -13952,6 +16217,29 @@ traffic_send_and_recv_counters_tcp4(_Config) when is_list(_Config) ->
%% This test case is intended to (simply) test that the counters
%% for both read and write.
%% So that its easy to extend, we use fun's for read and write.
+%% We use TCP on IPv6.
+
+traffic_send_and_recv_counters_tcp6(suite) ->
+ [];
+traffic_send_and_recv_counters_tcp6(doc) ->
+ [];
+traffic_send_and_recv_counters_tcp6(_Config) when is_list(_Config) ->
+ ?TT(?SECS(15)),
+ tc_try(traffic_send_and_recv_counters_tcp6,
+ fun() -> has_support_ipv6() end,
+ fun() ->
+ InitState = #{domain => inet6,
+ proto => tcp,
+ recv => fun(S) -> socket:recv(S) end,
+ send => fun(S, D) -> socket:send(S, D) end},
+ ok = traffic_send_and_recv_tcp(InitState)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case is intended to (simply) test that the counters
+%% for both read and write.
+%% So that its easy to extend, we use fun's for read and write.
%% We use default (TCP) on local.
traffic_send_and_recv_counters_tcpL(suite) ->
@@ -13961,6 +16249,7 @@ traffic_send_and_recv_counters_tcpL(doc) ->
traffic_send_and_recv_counters_tcpL(_Config) when is_list(_Config) ->
?TT(?SECS(15)),
tc_try(traffic_send_and_recv_counters_tcpL,
+ fun() -> has_support_unix_domain_socket() end,
fun() ->
InitState = #{domain => local,
proto => default,
@@ -14007,6 +16296,40 @@ traffic_sendmsg_and_recvmsg_counters_tcp4(_Config) when is_list(_Config) ->
%% This test case is intended to (simply) test that the counters
%% for both read and write.
%% So that its easy to extend, we use fun's for read and write.
+%% We use TCP on IPv6.
+
+traffic_sendmsg_and_recvmsg_counters_tcp6(suite) ->
+ [];
+traffic_sendmsg_and_recvmsg_counters_tcp6(doc) ->
+ [];
+traffic_sendmsg_and_recvmsg_counters_tcp6(_Config) when is_list(_Config) ->
+ ?TT(?SECS(15)),
+ tc_try(traffic_sendmsg_and_recvmsg_counters_tcp6,
+ fun() -> has_support_ipv6() end,
+ fun() ->
+ InitState = #{domain => inet6,
+ proto => tcp,
+ recv => fun(S) ->
+ case socket:recvmsg(S) of
+ {ok, #{addr := _Source,
+ iov := [Data]}} ->
+ {ok, Data};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end,
+ send => fun(S, Data) ->
+ MsgHdr = #{iov => [Data]},
+ socket:sendmsg(S, MsgHdr)
+ end},
+ ok = traffic_send_and_recv_tcp(InitState)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case is intended to (simply) test that the counters
+%% for both read and write.
+%% So that its easy to extend, we use fun's for read and write.
%% We use default (TCP) on local.
traffic_sendmsg_and_recvmsg_counters_tcpL(suite) ->
@@ -14016,6 +16339,7 @@ traffic_sendmsg_and_recvmsg_counters_tcpL(doc) ->
traffic_sendmsg_and_recvmsg_counters_tcpL(_Config) when is_list(_Config) ->
?TT(?SECS(15)),
tc_try(traffic_sendmsg_and_recvmsg_counters_tcpL,
+ fun() -> has_support_unix_domain_socket() end,
fun() ->
InitState = #{domain => local,
proto => default,
@@ -14064,6 +16388,8 @@ traffic_send_and_recv_tcp(InitState) ->
case socket:open(Domain, stream, Proto) of
{ok, Sock} ->
{ok, State#{lsock => Sock}};
+ {error, eafnosupport = Reason} ->
+ {skip, Reason};
{error, _} = ERROR ->
ERROR
end
@@ -14408,6 +16734,8 @@ traffic_send_and_recv_tcp(InitState) ->
case socket:open(Domain, stream, Proto) of
{ok, Sock} ->
{ok, State#{sock => Sock}};
+ {error, eafnosupport = Reason} ->
+ {skip, Reason};
{error, _} = ERROR ->
ERROR
end
@@ -14928,6 +17256,33 @@ traffic_sendto_and_recvfrom_counters_udp4(_Config) when is_list(_Config) ->
%% This test case is intended to (simply) test that the counters
%% for both read and write.
%% So that its easy to extend, we use fun's for read and write.
+%% We use UDP on IPv6.
+
+traffic_sendto_and_recvfrom_counters_udp6(suite) ->
+ [];
+traffic_sendto_and_recvfrom_counters_udp6(doc) ->
+ [];
+traffic_sendto_and_recvfrom_counters_udp6(_Config) when is_list(_Config) ->
+ ?TT(?SECS(15)),
+ tc_try(traffic_sendto_and_recvfrom_counters_udp6,
+ fun() -> has_support_ipv6() end,
+ fun() ->
+ InitState = #{domain => inet6,
+ proto => udp,
+ recv => fun(S) ->
+ socket:recvfrom(S)
+ end,
+ send => fun(S, Data, Dest) ->
+ socket:sendto(S, Data, Dest)
+ end},
+ ok = traffic_send_and_recv_udp(InitState)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case is intended to (simply) test that the counters
+%% for both read and write.
+%% So that its easy to extend, we use fun's for read and write.
%% We use default (UDP) on local.
traffic_sendto_and_recvfrom_counters_udpL(suite) ->
@@ -14937,6 +17292,7 @@ traffic_sendto_and_recvfrom_counters_udpL(doc) ->
traffic_sendto_and_recvfrom_counters_udpL(_Config) when is_list(_Config) ->
?TT(?SECS(15)),
tc_try(traffic_sendto_and_recvfrom_counters_udp4,
+ fun() -> has_support_unix_domain_socket() end,
fun() ->
InitState = #{domain => local,
proto => default,
@@ -14988,6 +17344,41 @@ traffic_sendmsg_and_recvmsg_counters_udp4(_Config) when is_list(_Config) ->
%% This test case is intended to (simply) test that the counters
%% for both read and write.
%% So that its easy to extend, we use fun's for read and write.
+%% We use UDP on IPv6.
+
+traffic_sendmsg_and_recvmsg_counters_udp6(suite) ->
+ [];
+traffic_sendmsg_and_recvmsg_counters_udp6(doc) ->
+ [];
+traffic_sendmsg_and_recvmsg_counters_udp6(_Config) when is_list(_Config) ->
+ ?TT(?SECS(15)),
+ tc_try(traffic_sendmsg_and_recvmsg_counters_udp6,
+ fun() -> has_support_ipv6() end,
+ fun() ->
+ InitState = #{domain => inet6,
+ proto => udp,
+ recv => fun(S) ->
+ case socket:recvmsg(S) of
+ {ok, #{addr := Source,
+ iov := [Data]}} ->
+ {ok, {Source, Data}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end,
+ send => fun(S, Data, Dest) ->
+ MsgHdr = #{addr => Dest,
+ iov => [Data]},
+ socket:sendmsg(S, MsgHdr)
+ end},
+ ok = traffic_send_and_recv_udp(InitState)
+ end).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% This test case is intended to (simply) test that the counters
+%% for both read and write.
+%% So that its easy to extend, we use fun's for read and write.
%% We use default (UDP) on local.
traffic_sendmsg_and_recvmsg_counters_udpL(suite) ->
@@ -14997,6 +17388,7 @@ traffic_sendmsg_and_recvmsg_counters_udpL(doc) ->
traffic_sendmsg_and_recvmsg_counters_udpL(_Config) when is_list(_Config) ->
?TT(?SECS(15)),
tc_try(traffic_sendmsg_and_recvmsg_counters_udpL,
+ fun() -> has_support_unix_domain_socket() end,
fun() ->
InitState = #{domain => local,
proto => default,
@@ -15455,7 +17847,7 @@ traffic_send_and_recv_udp(InitState) ->
#{desc => "recv (1)",
cmd => fun(#{sock := Sock,
recv := Recv,
- server_sa := ServerSA} = State) ->
+ server_sa := #{family := local} = ServerSA} = State) ->
case Recv(Sock) of
{ok, {ServerSA, Data}} ->
?SEV_IPRINT("recv ~p bytes", [size(Data)]),
@@ -15463,6 +17855,17 @@ traffic_send_and_recv_udp(InitState) ->
read_byte => size(Data)}};
{error, _} = ERROR ->
ERROR
+ end;
+ (#{sock := Sock,
+ recv := Recv,
+ server_sa := #{addr := Addr, port := Port}} = State) ->
+ case Recv(Sock) of
+ {ok, {#{addr := Addr, port := Port}, Data}} ->
+ ?SEV_IPRINT("recv ~p bytes", [size(Data)]),
+ {ok, State#{read_pkg => 1,
+ read_byte => size(Data)}};
+ {error, _} = ERROR ->
+ ERROR
end
end},
#{desc => "validate (recv 1)",
@@ -15557,7 +17960,7 @@ traffic_send_and_recv_udp(InitState) ->
end},
#{desc => "recv (2)",
cmd => fun(#{sock := Sock,
- server_sa := ServerSA,
+ server_sa := #{family := local} = ServerSA,
recv := Recv,
read_pkg := RPkg,
read_byte := RByte} = State) ->
@@ -15568,6 +17971,19 @@ traffic_send_and_recv_udp(InitState) ->
read_byte => RByte + size(Data)}};
{error, _} = ERROR ->
ERROR
+ end;
+ (#{sock := Sock,
+ server_sa := #{addr := Addr, port := Port},
+ recv := Recv,
+ read_pkg := RPkg,
+ read_byte := RByte} = State) ->
+ case Recv(Sock) of
+ {ok, {#{addr := Addr, port := Port}, Data}} ->
+ ?SEV_IPRINT("recv ~p bytes", [size(Data)]),
+ {ok, State#{read_pkg => RPkg + 1,
+ read_byte => RByte + size(Data)}};
+ {error, _} = ERROR ->
+ ERROR
end
end},
#{desc => "validate (recv 2)",
@@ -17055,6 +19471,7 @@ traffic_ping_pong_large_send_and_recv_tcp4(_Config) when is_list(_Config) ->
Msg = l2b(?TPP_LARGE),
Num = ?TPP_LARGE_NUM,
tc_try(traffic_ping_pong_large_send_and_recv_tcp4,
+ fun() -> is_old_fedora16() end,
fun() ->
InitState = #{domain => inet,
proto => tcp,
@@ -17081,7 +19498,8 @@ traffic_ping_pong_large_send_and_recv_tcp6(_Config) when is_list(_Config) ->
Msg = l2b(?TPP_LARGE),
Num = ?TPP_LARGE_NUM,
tc_try(traffic_ping_pong_large_send_and_recv_tcp6,
- fun() -> has_support_ipv6() end,
+ fun() -> is_old_fedora16(),
+ has_support_ipv6() end,
fun() ->
?TT(?SECS(45)),
InitState = #{domain => inet6,
@@ -17130,9 +19548,28 @@ traffic_ping_pong_large_host_cond() ->
traffic_ping_pong_large_host_cond({unix, sunos}, _) ->
skip("TC does not work on platform");
+traffic_ping_pong_large_host_cond({unix, linux}, _) ->
+ traffic_ping_pong_large_host_cond2(string:trim(os:cmd("cat /etc/issue")));
traffic_ping_pong_large_host_cond(_, _) ->
ok.
+traffic_ping_pong_large_host_cond2("Welcome to SUSE Linux Enterprise Server 10 SP1 (i586)" ++ _) ->
+ skip("TC does not work on platform");
+traffic_ping_pong_large_host_cond2("Fedora release 16 " ++ _) ->
+ skip("Very slow VM");
+traffic_ping_pong_large_host_cond2(_) ->
+ ok.
+
+
+is_old_fedora16() ->
+ is_old_fedora16(string:trim(os:cmd("cat /etc/issue"))).
+
+%% We actually only have one host running this, a slow VM.
+is_old_fedora16("Fedora release 16 " ++ _) ->
+ skip("Very slow VM");
+is_old_fedora16(_) ->
+ ok.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This test case is intended to test that the sendto and recvfrom
@@ -17175,12 +19612,12 @@ traffic_ping_pong_small_sendto_and_recvfrom_udp6(suite) ->
traffic_ping_pong_small_sendto_and_recvfrom_udp6(doc) ->
[];
traffic_ping_pong_small_sendto_and_recvfrom_udp6(_Config) when is_list(_Config) ->
- ?TT(?SECS(45)),
Msg = l2b(?TPP_SMALL),
Num = ?TPP_SMALL_NUM,
tc_try(traffic_ping_pong_small_sendto_and_recvfrom_udp6,
fun() -> has_support_ipv6() end,
fun() ->
+ ?TT(?SECS(45)),
InitState = #{domain => inet6,
proto => udp,
msg => Msg,
@@ -17204,12 +19641,12 @@ traffic_ping_pong_small_sendto_and_recvfrom_udpL(suite) ->
traffic_ping_pong_small_sendto_and_recvfrom_udpL(doc) ->
[];
traffic_ping_pong_small_sendto_and_recvfrom_udpL(_Config) when is_list(_Config) ->
- ?TT(?SECS(45)),
Msg = l2b(?TPP_SMALL),
Num = ?TPP_SMALL_NUM,
tc_try(traffic_ping_pong_small_sendto_and_recvfrom_udpL,
fun() -> has_support_unix_domain_socket() end,
fun() ->
+ ?TT(?SECS(45)),
InitState = #{domain => local,
proto => default,
msg => Msg,
@@ -17433,7 +19870,7 @@ traffic_ping_pong_medium_sendmsg_and_recvmsg_tcp6(_Config) when is_list(_Config)
tc_try(traffic_ping_pong_medium_sendmsg_and_recvmsg_tcp6,
fun() -> has_support_ipv6() end,
fun() ->
- ?TT(?SECS(20)),
+ ?TT(?SECS(30)),
InitState = #{domain => inet6,
proto => tcp,
msg => Msg,
@@ -17461,7 +19898,7 @@ traffic_ping_pong_medium_sendmsg_and_recvmsg_tcpL(_Config) when is_list(_Config)
tc_try(traffic_ping_pong_medium_sendmsg_and_recvmsg_tcpL,
fun() -> has_support_unix_domain_socket() end,
fun() ->
- ?TT(?SECS(20)),
+ ?TT(?SECS(30)),
InitState = #{domain => local,
proto => default,
msg => Msg,
@@ -17489,7 +19926,7 @@ traffic_ping_pong_large_sendmsg_and_recvmsg_tcp4(_Config) when is_list(_Config)
tc_try(traffic_ping_pong_large_sendmsg_and_recvmsg_tcp4,
fun() -> traffic_ping_pong_large_sendmsg_and_recvmsg_cond() end,
fun() ->
- ?TT(?SECS(30)),
+ ?TT(?SECS(60)),
InitState = #{domain => inet,
proto => tcp,
msg => Msg,
@@ -17530,7 +19967,7 @@ traffic_ping_pong_large_sendmsg_and_recvmsg_tcp6(_Config) when is_list(_Config)
traffic_ping_pong_large_sendmsg_and_recvmsg_cond()
end,
fun() ->
- ?TT(?SECS(30)),
+ ?TT(?SECS(60)),
InitState = #{domain => inet6,
proto => tcp,
msg => Msg,
@@ -17559,7 +19996,7 @@ traffic_ping_pong_large_sendmsg_and_recvmsg_tcpL(_Config) when is_list(_Config)
tc_try(traffic_ping_pong_large_sendmsg_and_recvmsg_tcpL,
fun() -> has_support_unix_domain_socket() end,
fun() ->
- ?TT(?SECS(30)),
+ ?TT(?SECS(60)),
InitState = #{domain => local,
proto => default,
msg => Msg,
@@ -17615,7 +20052,7 @@ traffic_ping_pong_small_sendmsg_and_recvmsg_udp6(_Config) when is_list(_Config)
tc_try(traffic_ping_pong_small_sendmsg_and_recvmsg_udp6,
fun() -> has_support_ipv6() end,
fun() ->
- ?TT(?SECS(30)),
+ ?TT(?SECS(60)),
InitState = #{domain => inet6,
proto => udp,
msg => Msg,
@@ -17643,7 +20080,7 @@ traffic_ping_pong_small_sendmsg_and_recvmsg_udpL(_Config) when is_list(_Config)
tc_try(traffic_ping_pong_small_sendmsg_and_recvmsg_udpL,
fun() -> has_support_unix_domain_socket() end,
fun() ->
- ?TT(?SECS(30)),
+ ?TT(?SECS(60)),
InitState = #{domain => local,
proto => default,
msg => Msg,
@@ -17670,7 +20107,7 @@ traffic_ping_pong_medium_sendmsg_and_recvmsg_udp4(_Config) when is_list(_Config)
Num = ?TPP_MEDIUM_NUM,
tc_try(traffic_ping_pong_medium_sendmsg_and_recvmsg_udp4,
fun() ->
- ?TT(?SECS(30)),
+ ?TT(?SECS(60)),
InitState = #{domain => inet,
proto => udp,
msg => Msg,
@@ -17698,7 +20135,7 @@ traffic_ping_pong_medium_sendmsg_and_recvmsg_udp6(_Config) when is_list(_Config)
tc_try(traffic_ping_pong_medium_sendmsg_and_recvmsg_udp6,
fun() -> has_support_ipv6() end,
fun() ->
- ?TT(?SECS(20)),
+ ?TT(?SECS(60)),
InitState = #{domain => inet6,
proto => udp,
msg => Msg,
@@ -17727,7 +20164,7 @@ traffic_ping_pong_medium_sendmsg_and_recvmsg_udpL(_Config) when is_list(_Config)
tc_try(traffic_ping_pong_medium_sendmsg_and_recvmsg_udpL,
fun() -> has_support_unix_domain_socket() end,
fun() ->
- ?TT(?SECS(20)),
+ ?TT(?SECS(60)),
InitState = #{domain => local,
proto => default,
msg => Msg,
@@ -17799,12 +20236,14 @@ traffic_ping_pong_send_and_receive_tcp(#{msg := Msg} = InitState) ->
?SEV_IPRINT("RcvBuf is ~p (needs atleast ~p)",
[RcvSz, 16+size(Msg)]),
if (RcvSz < size(Msg)) ->
- case socket:setopt(Sock,
- socket, rcvbuf, 1024+size(Msg)) of
+ NewRcvSz = 1024+size(Msg),
+ case socket:setopt(Sock, socket, rcvbuf, NewRcvSz) of
ok ->
ok;
{error, enobufs} ->
- skip({failed_change, rcvbuf});
+ skip(?F("Change ~w buffer size (to ~w) "
+ "not allowed",
+ [rcvbuf, NewRcvSz]));
{error, Reason1} ->
?FAIL({rcvbuf, Reason1})
end;
@@ -17815,12 +20254,14 @@ traffic_ping_pong_send_and_receive_tcp(#{msg := Msg} = InitState) ->
?SEV_IPRINT("SndBuf is ~p (needs atleast ~p)",
[SndSz, 16+size(Msg)]),
if (SndSz < size(Msg)) ->
- case socket:setopt(Sock,
- socket, sndbuf, 1024+size(Msg)) of
+ NewSndSz = 1024+size(Msg),
+ case socket:setopt(Sock, socket, sndbuf, NewSndSz) of
ok ->
ok;
{error, enobufs} ->
- skip({failed_change, sndbuf});
+ skip(?F("Change ~w buffer size (to ~w) "
+ "not allowed",
+ [sndbuf, NewSndSz]));
{error, Reason2} ->
?FAIL({sndbuf, Reason2})
end;
@@ -19408,7 +21849,9 @@ tpp_udp_client_handler_msg_exchange_loop(_Sock, _Dest, _Send, _Recv, _Msg,
Start) ->
Stop = ?LIB:timestamp(),
{Sent, Received, Start, Stop};
-tpp_udp_client_handler_msg_exchange_loop(Sock, Dest, Send, Recv, Data,
+tpp_udp_client_handler_msg_exchange_loop(Sock,
+ #{family := local} = Dest,
+ Send, Recv, Data,
Num, N, Sent, Received, Start) ->
case tpp_udp_send_req(Sock, Send, Data, Dest) of
{ok, SendSz} ->
@@ -19427,6 +21870,28 @@ tpp_udp_client_handler_msg_exchange_loop(Sock, Dest, Send, Recv, Data,
{error, SReason} ->
?SEV_EPRINT("send (~w of ~w): ~p", [N, Num, SReason]),
exit({send, SReason, N})
+ end;
+tpp_udp_client_handler_msg_exchange_loop(Sock,
+ #{addr := Addr, port := Port} = Dest0,
+ Send, Recv, Data,
+ Num, N, Sent, Received, Start) ->
+ case tpp_udp_send_req(Sock, Send, Data, Dest0) of
+ {ok, SendSz} ->
+ case tpp_udp_recv_rep(Sock, Recv) of
+ {ok, NewData, RecvSz, #{addr := Addr, port := Port} = Dest1} ->
+ tpp_udp_client_handler_msg_exchange_loop(Sock, Dest1,
+ Send, Recv,
+ NewData, Num, N+1,
+ Sent+SendSz,
+ Received+RecvSz,
+ Start);
+ {error, RReason} ->
+ ?SEV_EPRINT("recv (~w of ~w): ~p", [N, Num, RReason]),
+ exit({recv, RReason, N})
+ end;
+ {error, SReason} ->
+ ?SEV_EPRINT("send (~w of ~w): ~p", [N, Num, SReason]),
+ exit({send, SReason, N})
end.
@@ -25450,7 +27915,7 @@ ttest_tcp(TC,
fun() ->
if
(Domain =:= local) -> has_support_unix_domain_socket();
- (Domain =:= inet6) -> has_support_ipv6();
+ (Domain =:= inet6) -> has_support_ipv6();
true -> ok
end
end,
@@ -25913,16 +28378,22 @@ ttest_tcp(InitState) ->
?SEV_FINISH_NORMAL
],
+ Domain = maps:get(domain, InitState),
+ LHost = local_host(),
+ LAddr = which_local_addr(Domain),
+
i("start server evaluator"),
- ServerInitState = #{host => local_host(),
- domain => maps:get(domain, InitState),
+ ServerInitState = #{host => LHost,
+ addr => LAddr,
+ domain => Domain,
mod => maps:get(server_mod, InitState),
active => maps:get(server_active, InitState)},
Server = ?SEV_START("server", ServerSeq, ServerInitState),
i("start client evaluator"),
- ClientInitState = #{host => local_host(),
- domain => maps:get(domain, InitState),
+ ClientInitState = #{host => LHost,
+ addr => LAddr,
+ domain => Domain,
mod => maps:get(client_mod, InitState),
active => maps:get(client_active, InitState),
msg_id => maps:get(msg_id, InitState),
@@ -25931,9 +28402,14 @@ ttest_tcp(InitState) ->
Client = ?SEV_START("client", ClientSeq, ClientInitState),
i("start 'tester' evaluator"),
- TesterInitState = #{domain => maps:get(domain, InitState),
- server => Server#ev.pid,
- client => Client#ev.pid},
+ TesterInitState = #{domain => Domain,
+ msg_id => maps:get(msg_id, InitState),
+ client => Client#ev.pid,
+ client_mod => maps:get(client_mod, InitState),
+ client_active => maps:get(client_active, InitState),
+ server => Server#ev.pid,
+ server_mod => maps:get(server_mod, InitState),
+ server_active => maps:get(server_active, InitState)},
Tester = ?SEV_START("tester", TesterSeq, TesterInitState),
i("await evaluator(s)"),
@@ -25941,8 +28417,9 @@ ttest_tcp(InitState) ->
-ttest_tcp_server_start(Node, _Domain, gen, Active) ->
- Transport = socket_test_ttest_tcp_gen,
+ttest_tcp_server_start(Node, Domain, gen, Active) ->
+ TransportMod = socket_test_ttest_tcp_gen,
+ Transport = {TransportMod, #{domain => Domain}},
socket_test_ttest_tcp_server:start_monitor(Node, Transport, Active);
ttest_tcp_server_start(Node, Domain, sock, Active) ->
TransportMod = socket_test_ttest_tcp_socket,
@@ -25956,9 +28433,10 @@ ttest_tcp_server_stop(Pid) ->
ttest_tcp_client_start(Node,
Notify,
- _Domain, gen,
+ Domain, gen,
ServerInfo, Active, MsgID, MaxOutstanding, RunTime) ->
- Transport = socket_test_ttest_tcp_gen,
+ TransportMod = socket_test_ttest_tcp_gen,
+ Transport = {TransportMod, #{domain => Domain}},
socket_test_ttest_tcp_client:start_monitor(Node,
Notify,
Transport,
@@ -26253,39 +28731,6 @@ sock_sockname(Sock) ->
?FAIL({sockname, C, E, S})
end.
-
-%% sock_listen(Sock) ->
-%% sock_listen2(fun() -> socket:listen(Sock) end).
-
-%% sock_listen(Sock, BackLog) ->
-%% sock_listen2(fun() -> socket:listen(Sock, BackLog) end).
-
-%% sock_listen2(Listen) ->
-%% try Listen() of
-%% ok ->
-%% ok;
-%% {error, Reason} ->
-%% ?FAIL({listen, Reason})
-%% catch
-%% C:E:S ->
-%% ?FAIL({listen, C, E, S})
-%% end.
-
-
-%% sock_accept(LSock) ->
-%% try socket:accept(LSock) of
-%% {ok, Sock} ->
-%% Sock;
-%% {error, Reason} ->
-%% i("sock_accept -> error: ~p", [Reason]),
-%% ?FAIL({accept, Reason})
-%% catch
-%% C:E:S ->
-%% i("sock_accept -> failed: ~p, ~p, ~p", [C, E, S]),
-%% ?FAIL({accept, C, E, S})
-%% end.
-
-
sock_close(Sock) ->
try socket:close(Sock) of
ok ->
@@ -26351,12 +28796,12 @@ which_local_socket_addr(local = Domain) ->
#{family => Domain,
path => mk_unique_path()};
-%% This gets the local address (not 127.0...)
+%% This gets the local socket address (not 127.0...)
%% We should really implement this using the (new) net module,
%% but until that gets the necessary functionality...
which_local_socket_addr(Domain) ->
- case which_local_host_info(Domain) of
- {ok, {_Name, _Flags, Addr}} ->
+ case ?LIB:which_local_host_info(Domain) of
+ {ok, #{addr := Addr}} ->
#{family => Domain,
addr => Addr};
{error, Reason} ->
@@ -26364,55 +28809,15 @@ which_local_socket_addr(Domain) ->
end.
-%% Returns the interface (name), flags and address (not 127...)
-%% of the local host.
-which_local_host_info(Domain) ->
- case inet:getifaddrs() of
- {ok, IFL} ->
- which_local_host_info(Domain, IFL);
- {error, _} = ERROR ->
- ERROR
- end.
-which_local_host_info(_Domain, []) ->
- ?FAIL(no_address);
-which_local_host_info(Domain, [{"lo" ++ _, _}|IFL]) ->
- which_local_host_info(Domain, IFL);
-which_local_host_info(Domain, [{"docker" ++ _, _}|IFL]) ->
- which_local_host_info(Domain, IFL);
-which_local_host_info(Domain, [{"br-" ++ _, _}|IFL]) ->
- which_local_host_info(Domain, IFL);
-which_local_host_info(Domain, [{Name, IFO}|IFL]) ->
- case which_local_host_info2(Domain, IFO) of
- {ok, {Flags, Addr}} ->
- {ok, {Name, Flags, Addr}};
- {error, _} ->
- which_local_host_info(Domain, IFL)
- end;
-which_local_host_info(Domain, [_|IFL]) ->
- which_local_host_info(Domain, IFL).
-
-which_local_host_info2(Domain, IFO) ->
- case lists:keysearch(flags, 1, IFO) of
- {value, {flags, Flags}} ->
- which_local_host_info2(Domain, IFO, Flags);
- false ->
- {error, no_flags}
- end.
-
-which_local_host_info2(_Domain, [], _Flags) ->
- {error, no_address};
-which_local_host_info2(inet = _Domain, [{addr, Addr}|_IFO], Flags)
- when (size(Addr) =:= 4) andalso (element(1, Addr) =/= 127) ->
- {ok, {Flags, Addr}};
-which_local_host_info2(inet6 = _Domain, [{addr, Addr}|_IFO], Flags)
- when (size(Addr) =:= 8) andalso
- (element(1, Addr) =/= 0) andalso
- (element(1, Addr) =/= 16#fe80) ->
- {ok, {Flags, Addr}};
-which_local_host_info2(Domain, [_|IFO], Flags) ->
- which_local_host_info2(Domain, IFO, Flags).
+which_local_addr(local = _Domain) ->
+ mk_unique_path();
+%% This gets the local address (not 127.0...)
+%% We should really implement this using the (new) net module,
+%% but until that gets the necessary functionality...
+which_local_addr(Domain) ->
+ ?LIB:which_local_addr(Domain).
@@ -26425,12 +28830,12 @@ which_local_host_info2(Domain, [_|IFO], Flags) ->
%% We don't do that here, but since we can only do that (find a
%% multicast address) for specific platforms, we check that we are
%% on of those platforms here.
-has_ip_multicast_support() ->
+has_support_ip_multicast() ->
case os:type() of
{unix, OsName} when (OsName =:= linux) orelse
(OsName =:= sunos) ->
- case which_local_host_info(inet) of
- {ok, {_Name, Flags, _Addr}} ->
+ case ?LIB:which_local_host_info(inet) of
+ {ok, #{flags := Flags}} ->
case lists:member(multicast, Flags) of
true ->
ok;
@@ -26440,26 +28845,64 @@ has_ip_multicast_support() ->
{error, Reason} ->
not_supported({multicast, Reason})
end;
+ {unix, OsName} ->
+ skip(?F("Not Supported: platform ~w", [OsName]));
Type ->
- not_supported({multicast, Type})
+ skip(?F("Not Supported: platform ~p", [Type]))
end.
-has_ip_add_membership_support() ->
- has_socket_option_ip_support(add_membership).
+has_support_sock_acceptconn() ->
+ has_support_socket_option_sock(acceptconn).
+
+has_support_sock_bindtodevice() ->
+ has_support_socket_option_sock(bindtodevice).
+
+has_support_sock_broadcast() ->
+ has_support_socket_option_sock(broadcast),
+ case ?LIB:which_local_host_info(inet) of
+ {ok, #{flags := Flags}} ->
+ case lists:member(broadcast, Flags) of
+ true ->
+ ok;
+ false ->
+ not_supported({broadcast, Flags})
+ end;
+ {error, Reason} ->
+ not_supported({broadcast, Reason})
+ end.
+
+has_support_sock_debug() ->
+ has_support_socket_option_sock(debug).
+
+has_support_sock_domain() ->
+ has_support_socket_option_sock(domain).
+
+has_support_sock_dontroute() ->
+ has_support_socket_option_sock(dontroute).
+
+has_support_sock_keepalive() ->
+ has_support_socket_option_sock(keepalive).
+
+
+has_support_ip_add_membership() ->
+ has_support_socket_option_ip(add_membership).
+
+has_support_ip_drop_membership() ->
+ has_support_socket_option_ip(drop_membership).
-has_ip_drop_membership_support() ->
- has_socket_option_ip_support(drop_membership).
+has_support_socket_option_ip(Opt) ->
+ has_support_socket_option(ip, Opt).
-has_socket_option_ip_support(Opt) ->
- has_socket_option_support(ip, Opt).
+has_support_socket_option_sock(Opt) ->
+ has_support_socket_option(socket, Opt).
-has_socket_option_support(Level, Option) ->
+has_support_socket_option(Level, Option) ->
case socket:supports(options, Level, Option) of
true ->
ok;
false ->
- not_supported({options, Level, Option})
+ skip(?F("Not Supported: ~w option ~w", [Level, Option]))
end.
@@ -26491,13 +28934,7 @@ has_support_unix_domain_socket() ->
%% support for IPv6. If not, there is no point in running IPv6 tests.
%% Currently we just skip.
has_support_ipv6() ->
- %% case socket:supports(ipv6) of
- %% true ->
- %% ok;
- %% false ->
- %% {error, not_supported}
- %% end.
- not_yet_implemented().
+ ?LIB:has_support_ipv6().
diff --git a/erts/emulator/test/socket_test_lib.erl b/erts/emulator/test/socket_test_lib.erl
index 4e65c4f3c0..39cbf0c79f 100644
--- a/erts/emulator/test/socket_test_lib.erl
+++ b/erts/emulator/test/socket_test_lib.erl
@@ -32,6 +32,12 @@
%% String and format
f/2,
+ %% Generic 'has support' test function(s)
+ has_support_ipv6/0,
+
+ which_local_host_info/1,
+ which_local_addr/1,
+
%% Skipping
not_yet_implemented/0,
skip/1
@@ -40,6 +46,11 @@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+-define(FAIL(R), exit(R)).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
pi(Item) when is_atom(Item) ->
pi(self(), Item).
@@ -88,6 +99,196 @@ f(F, A) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+has_support_ipv6() ->
+ case socket:supports(ipv6) of
+ true ->
+ ok;
+ false ->
+ skip("IPv6 Not Supported")
+ end,
+ Domain = inet6,
+ LocalAddr =
+ case which_local_addr(Domain) of
+ {ok, Addr} ->
+ Addr;
+ {error, R1} ->
+ skip(f("Local Address eval failed: ~p", [R1]))
+ end,
+ ServerSock =
+ case socket:open(Domain, dgram, udp) of
+ {ok, SS} ->
+ SS;
+ {error, R2} ->
+ skip(f("(server) socket open failed: ~p", [R2]))
+ end,
+ LocalSA = #{family => Domain, addr => LocalAddr},
+ ServerPort =
+ case socket:bind(ServerSock, LocalSA) of
+ {ok, P1} ->
+ P1;
+ {error, R3} ->
+ socket:close(ServerSock),
+ skip(f("(server) socket bind failed: ~p", [R3]))
+ end,
+ ServerSA = LocalSA#{port => ServerPort},
+ ClientSock =
+ case socket:open(Domain, dgram, udp) of
+ {ok, CS} ->
+ CS;
+ {error, R4} ->
+ skip(f("(client) socket open failed: ~p", [R4]))
+ end,
+ case socket:bind(ClientSock, LocalSA) of
+ {ok, _} ->
+ ok;
+ {error, R5} ->
+ socket:close(ServerSock),
+ socket:close(ClientSock),
+ skip(f("(client) socket bind failed: ~p", [R5]))
+ end,
+ case socket:sendto(ClientSock, <<"hejsan">>, ServerSA) of
+ ok ->
+ ok;
+ {error, R6} ->
+ socket:close(ServerSock),
+ socket:close(ClientSock),
+ skip(f("failed socket sendto test: ~p", [R6]))
+ end,
+ case socket:recvfrom(ServerSock) of
+ {ok, {_, <<"hejsan">>}} ->
+ socket:close(ServerSock),
+ socket:close(ClientSock),
+ ok;
+ {error, R7} ->
+ socket:close(ServerSock),
+ socket:close(ClientSock),
+ skip(f("failed socket recvfrom test: ~p", [R7]))
+ end.
+
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% This gets the local address (not {127, _} or {0, ...} or {16#fe80, ...})
+%% We should really implement this using the (new) net module,
+%% but until that gets the necessary functionality...
+which_local_addr(Domain) ->
+ case which_local_host_info(Domain) of
+ {ok, #{addr := Addr}} ->
+ {ok, Addr};
+ {error, _Reason} = ERROR ->
+ ERROR
+ end.
+
+
+%% Returns the interface (name), flags and address (not 127...)
+%% of the local host.
+which_local_host_info(Domain) ->
+ case inet:getifaddrs() of
+ {ok, IFL} ->
+ which_local_host_info(Domain, IFL);
+ {error, _} = ERROR ->
+ ERROR
+ end.
+
+which_local_host_info(_Domain, []) ->
+ {error, no_address};
+which_local_host_info(Domain, [{"lo" ++ _, _}|IFL]) ->
+ which_local_host_info(Domain, IFL);
+which_local_host_info(Domain, [{"docker" ++ _, _}|IFL]) ->
+ which_local_host_info(Domain, IFL);
+which_local_host_info(Domain, [{"br-" ++ _, _}|IFL]) ->
+ which_local_host_info(Domain, IFL);
+which_local_host_info(Domain, [{Name, IFO}|IFL]) ->
+ try which_local_host_info2(Domain, IFO) of
+ Info ->
+ {ok, Info#{name => Name}}
+ catch
+ throw:_:_ ->
+ which_local_host_info(Domain, IFL)
+ end;
+which_local_host_info(Domain, [_|IFL]) ->
+ which_local_host_info(Domain, IFL).
+
+%% which_local_host_info2(Domain, IFO) ->
+%% case lists:keysearch(flags, 1, IFO) of
+%% {value, {flags, Flags}} ->
+%% which_local_host_info2(Domain, IFO, Flags);
+%% false ->
+%% {error, no_flags}
+%% end.
+
+
+%% which_local_host_info2(_Domain, [], _Flags) ->
+%% {error, no_address};
+%% which_local_host_info2(inet = _Domain, [{addr, Addr}|_IFO], Flags)
+%% when (size(Addr) =:= 4) andalso (element(1, Addr) =/= 127) ->
+%% {ok, {Flags, Addr}};
+%% which_local_host_info2(inet6 = _Domain, [{addr, Addr}|_IFO], Flags)
+%% when (size(Addr) =:= 8) andalso
+%% (element(1, Addr) =/= 0) andalso
+%% (element(1, Addr) =/= 16#fe80) ->
+%% {ok, {Flags, Addr}};
+%% which_local_host_info2(Domain, [_|IFO], Flags) ->
+%% which_local_host_info2(Domain, IFO, Flags).
+
+%% foo(Info, inet = Domain, IFO) ->
+%% foo(Info, Domain, IFO, [flags, addr, netmask, broadaddr, hwaddr]);
+%% foo(Info, inet6 = Domain, IFO) ->
+%% foo(Info, Domain, IFO, [flags, addr, netmask, hwaddr]).
+
+which_local_host_info2(inet = _Domain, IFO) ->
+ Addr = which_local_host_info3(addr, IFO,
+ fun({A, _, _, _}) when (A =/= 127) -> true;
+ (_) -> false
+ end),
+ NetMask = which_local_host_info3(netmask, IFO,
+ fun({_, _, _, _}) -> true;
+ (_) -> false
+ end),
+ BroadAddr = which_local_host_info3(broadaddr, IFO,
+ fun({_, _, _, _}) -> true;
+ (_) -> false
+ end),
+ Flags = which_local_host_info3(flags, IFO, fun(_) -> true end),
+ #{flags => Flags,
+ addr => Addr,
+ broadaddr => BroadAddr,
+ netmask => NetMask};
+which_local_host_info2(inet6 = _Domain, IFO) ->
+ Addr = which_local_host_info3(addr, IFO,
+ fun({A, _, _, _, _, _, _, _})
+ when (A =/= 0) andalso
+ (A =/= 16#fe80) -> true;
+ (_) -> false
+ end),
+ NetMask = which_local_host_info3(netmask, IFO,
+ fun({_, _, _, _, _, _, _, _}) -> true;
+ (_) -> false
+ end),
+ Flags = which_local_host_info3(flags, IFO, fun(_) -> true end),
+ #{flags => Flags,
+ addr => Addr,
+ netmask => NetMask}.
+
+which_local_host_info3(_Key, [], _) ->
+ throw({error, no_address});
+which_local_host_info3(Key, [{Key, Val}|IFO], Check) ->
+ case Check(Val) of
+ true ->
+ Val;
+ false ->
+ which_local_host_info3(Key, IFO, Check)
+ end;
+which_local_host_info3(Key, [_|IFO], Check) ->
+ which_local_host_info3(Key, IFO, Check).
+
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
not_yet_implemented() ->
skip("not yet implemented").
diff --git a/erts/emulator/test/socket_test_ttest_tcp_client.erl b/erts/emulator/test/socket_test_ttest_tcp_client.erl
index b5c5300fd0..f28819ca69 100644
--- a/erts/emulator/test/socket_test_ttest_tcp_client.erl
+++ b/erts/emulator/test/socket_test_ttest_tcp_client.erl
@@ -266,8 +266,9 @@ init(Quiet,
(catch Mod:close(Sock)),
exit(normal);
{error, Reason} ->
- ?E("connect failed: ~p", [Reason]),
- exit({connect, Reason})
+ ?E("connect failed: ~p"
+ "~n ~p", [Reason, ServerInfo]),
+ exit({connect, Reason, ServerInfo})
end.
process_transport(Mod) when is_atom(Mod) ->
diff --git a/erts/emulator/test/socket_test_ttest_tcp_client_socket.erl b/erts/emulator/test/socket_test_ttest_tcp_client_socket.erl
index ca7eff4437..2fb1242028 100644
--- a/erts/emulator/test/socket_test_ttest_tcp_client_socket.erl
+++ b/erts/emulator/test/socket_test_ttest_tcp_client_socket.erl
@@ -34,24 +34,24 @@ start(Method, Async, Active, ServerInfo)
when is_list(ServerInfo) ->
Domain = local,
socket_test_ttest_tcp_client:start_monitor(?MOD(Domain, Async, Method),
- Active, ServerInfo);
+ ServerInfo, Active);
start(Method, Async, Active, ServerInfo = {Addr, _})
when is_tuple(Addr) andalso (size(Addr) =:= 4) ->
Domain = inet,
socket_test_ttest_tcp_client:start_monitor(?MOD(Domain, Async, Method),
- Active, ServerInfo);
+ ServerInfo, Active);
start(Method, Async, Active, ServerInfo = {Addr, _})
when is_tuple(Addr) andalso (size(Addr) =:= 8) ->
Domain = inet6,
socket_test_ttest_tcp_client:start_monitor(?MOD(Domain, Async, Method),
- Active, ServerInfo).
+ ServerInfo, Active).
start(Method, Async, Active, ServerInfo, MsgID)
when is_list(ServerInfo) ->
%% This is just a simplification
Domain = local,
socket_test_ttest_tcp_client:start(?MOD(Domain, Async, Method),
- Active, ServerInfo, MsgID);
+ ServerInfo, Active, MsgID);
start(Method, Async, Active, ServerInfo = {Addr, _}, MsgID)
when is_tuple(Addr) andalso (size(Addr) =:= 4) ->
%% This is just a simplification
@@ -62,14 +62,14 @@ start(Method, Async, Active, ServerInfo = {Addr, _}, MsgID)
when is_tuple(Addr) andalso (size(Addr) =:= 8) ->
Domain = inet6,
socket_test_ttest_tcp_client:start(?MOD(Domain, Async, Method),
- Active, ServerInfo, MsgID).
+ ServerInfo, Active, MsgID).
start(Method, Async, Active, ServerInfo, MsgID, MaxOutstanding, RunTime)
when is_list(ServerInfo) ->
Domain = local,
socket_test_ttest_tcp_client:start(false,
?MOD(Domain, Async, Method),
- Active, ServerInfo,
+ ServerInfo, Active,
MsgID, MaxOutstanding, RunTime);
start(Method, Async, Active, ServerInfo = {Addr, _},
MsgID, MaxOutstanding, RunTime)
@@ -77,7 +77,7 @@ start(Method, Async, Active, ServerInfo = {Addr, _},
Domain = inet,
socket_test_ttest_tcp_client:start(false,
?MOD(Domain, Async, Method),
- Active, ServerInfo,
+ ServerInfo, Active,
MsgID, MaxOutstanding, RunTime);
start(Method, Async, Active, ServerInfo = {Addr, _},
MsgID, MaxOutstanding, RunTime)
@@ -85,7 +85,7 @@ start(Method, Async, Active, ServerInfo = {Addr, _},
Domain = inet6,
socket_test_ttest_tcp_client:start(false,
?MOD(Domain, Async, Method),
- Active, ServerInfo,
+ ServerInfo, Active,
MsgID, MaxOutstanding, RunTime).
start(Quiet, Async, Active, Method, ServerInfo, MsgID, MaxOutstanding, RunTime)
@@ -93,7 +93,7 @@ start(Quiet, Async, Active, Method, ServerInfo, MsgID, MaxOutstanding, RunTime)
Domain = local,
socket_test_ttest_tcp_client:start(Quiet,
?MOD(Domain, Async, Method),
- Active, ServerInfo,
+ ServerInfo, Active,
MsgID, MaxOutstanding, RunTime);
start(Quiet, Async, Active, Method, ServerInfo = {Addr, _},
MsgID, MaxOutstanding, RunTime)
@@ -101,7 +101,7 @@ start(Quiet, Async, Active, Method, ServerInfo = {Addr, _},
Domain = inet,
socket_test_ttest_tcp_client:start(Quiet,
?MOD(Domain, Async, Method),
- Active, ServerInfo,
+ ServerInfo, Active,
MsgID, MaxOutstanding, RunTime);
start(Quiet, Async, Active, Method, ServerInfo = {Addr, _},
MsgID, MaxOutstanding, RunTime)
@@ -109,7 +109,7 @@ start(Quiet, Async, Active, Method, ServerInfo = {Addr, _},
Domain = inet6,
socket_test_ttest_tcp_client:start(Quiet,
?MOD(Domain, Async, Method),
- Active, ServerInfo,
+ ServerInfo, Active,
MsgID, MaxOutstanding, RunTime).
stop(Pid) ->
diff --git a/erts/emulator/test/socket_test_ttest_tcp_gen.erl b/erts/emulator/test/socket_test_ttest_tcp_gen.erl
index 05b250e3d9..e59bd881e7 100644
--- a/erts/emulator/test/socket_test_ttest_tcp_gen.erl
+++ b/erts/emulator/test/socket_test_ttest_tcp_gen.erl
@@ -36,19 +36,7 @@
]).
-%% ==========================================================================
-
-%% getopt(Sock, Opt) when is_atom(Opt) ->
-%% case inet:getopts(Sock, [Opt]) of
-%% {ok, [{Opt, Value}]} ->
-%% {ok, Value};
-%% {error, _} = ERROR ->
-%% ERROR
-%% end.
-
-%% setopt(Sock, Opt, Value) when is_atom(Opt) ->
-%% inet:setopts(Sock, [{Opt, Value}]).
-
+-define(LIB, socket_test_lib).
%% ==========================================================================
@@ -106,12 +94,12 @@ listen(Port) ->
listen(Port, #{domain => inet}).
listen(Port, #{domain := Domain}) when is_integer(Port) andalso (Port >= 0) ->
- Opts = [Domain,
- binary, {ip, {0,0,0,0}}, {packet, raw}, {active, false},
- {buffer, 32*1024}],
- case gen_tcp:listen(Port, Opts) of
- {ok, Sock} ->
- {ok, Sock};
+ case ?LIB:which_local_host_info(Domain) of
+ {ok, {_, _, Addr}} ->
+ Opts = [Domain,
+ binary, {ip, Addr}, {packet, raw}, {active, false},
+ {buffer, 32*1024}],
+ gen_tcp:listen(Port, Opts);
{error, _} = ERROR ->
ERROR
end.
diff --git a/erts/emulator/test/socket_test_ttest_tcp_server.erl b/erts/emulator/test/socket_test_ttest_tcp_server.erl
index 27b561d4b7..2394dc7924 100644
--- a/erts/emulator/test/socket_test_ttest_tcp_server.erl
+++ b/erts/emulator/test/socket_test_ttest_tcp_server.erl
@@ -134,12 +134,18 @@ server_init(Starter, Parent, Transport, Active) ->
if
is_integer(PortOrPath) ->
%% This is just for convenience
- Addr = which_addr(),
- ?I("listening on:"
- "~n Addr: ~p (~s)"
- "~n Port: ~w"
- "~n", [Addr, inet:ntoa(Addr), PortOrPath]),
- {Addr, PortOrPath};
+ case Mod:sockname(LSock) of
+ {ok, {Addr, _}} ->
+ ?I("listening on:"
+ "~n Addr: ~p (~s)"
+ "~n Port: ~w"
+ "~n", [Addr,
+ inet:ntoa(Addr),
+ PortOrPath]),
+ {Addr, PortOrPath};
+ {error, SNReason} ->
+ exit({sockname, SNReason})
+ end;
is_list(PortOrPath) ->
?I("listening on:"
"~n Path: ~s"
@@ -569,51 +575,51 @@ handler_maybe_activate(_, _, _) ->
%% ==========================================================================
-which_addr() ->
- case inet:getifaddrs() of
- {ok, IfAddrs} ->
- which_addrs(inet, IfAddrs);
- {error, Reason} ->
- exit({getifaddrs, Reason})
- end.
+%% which_addr() ->
+%% case inet:getifaddrs() of
+%% {ok, IfAddrs} ->
+%% which_addrs(inet, IfAddrs);
+%% {error, Reason} ->
+%% exit({getifaddrs, Reason})
+%% end.
-which_addrs(_Family, []) ->
- exit({getifaddrs, not_found});
-which_addrs(Family, [{"lo", _} | IfAddrs]) ->
- %% Skip
- which_addrs(Family, IfAddrs);
-which_addrs(Family, [{"docker" ++ _, _} | IfAddrs]) ->
- %% Skip docker
- which_addrs(Family, IfAddrs);
-which_addrs(Family, [{"br-" ++ _, _} | IfAddrs]) ->
- %% Skip docker
- which_addrs(Family, IfAddrs);
-which_addrs(Family, [{"en" ++ _, IfOpts} | IfAddrs]) ->
- %% Maybe take this one
- case which_addr(Family, IfOpts) of
- {ok, Addr} ->
- Addr;
- error ->
- which_addrs(Family, IfAddrs)
- end;
-which_addrs(Family, [{_IfName, IfOpts} | IfAddrs]) ->
- case which_addr(Family, IfOpts) of
- {ok, Addr} ->
- Addr;
- error ->
- which_addrs(Family, IfAddrs)
- end.
+%% which_addrs(_Family, []) ->
+%% exit({getifaddrs, not_found});
+%% which_addrs(Family, [{"lo", _} | IfAddrs]) ->
+%% %% Skip
+%% which_addrs(Family, IfAddrs);
+%% which_addrs(Family, [{"docker" ++ _, _} | IfAddrs]) ->
+%% %% Skip docker
+%% which_addrs(Family, IfAddrs);
+%% which_addrs(Family, [{"br-" ++ _, _} | IfAddrs]) ->
+%% %% Skip docker
+%% which_addrs(Family, IfAddrs);
+%% which_addrs(Family, [{"en" ++ _, IfOpts} | IfAddrs]) ->
+%% %% Maybe take this one
+%% case which_addr(Family, IfOpts) of
+%% {ok, Addr} ->
+%% Addr;
+%% error ->
+%% which_addrs(Family, IfAddrs)
+%% end;
+%% which_addrs(Family, [{_IfName, IfOpts} | IfAddrs]) ->
+%% case which_addr(Family, IfOpts) of
+%% {ok, Addr} ->
+%% Addr;
+%% error ->
+%% which_addrs(Family, IfAddrs)
+%% end.
-which_addr(_, []) ->
- error;
-which_addr(inet, [{addr, Addr}|_])
- when is_tuple(Addr) andalso (size(Addr) =:= 4) ->
- {ok, Addr};
-which_addr(inet6, [{addr, Addr}|_])
- when is_tuple(Addr) andalso (size(Addr) =:= 8) ->
- {ok, Addr};
-which_addr(Family, [_|IfOpts]) ->
- which_addr(Family, IfOpts).
+%% which_addr(_, []) ->
+%% error;
+%% which_addr(inet, [{addr, Addr}|_])
+%% when is_tuple(Addr) andalso (size(Addr) =:= 4) ->
+%% {ok, Addr};
+%% which_addr(inet6, [{addr, Addr}|_])
+%% when is_tuple(Addr) andalso (size(Addr) =:= 8) ->
+%% {ok, Addr};
+%% which_addr(Family, [_|IfOpts]) ->
+%% which_addr(Family, IfOpts).
%% ==========================================================================
diff --git a/erts/emulator/test/socket_test_ttest_tcp_socket.erl b/erts/emulator/test/socket_test_ttest_tcp_socket.erl
index 3aa3b2c504..9112748b4c 100644
--- a/erts/emulator/test/socket_test_ttest_tcp_socket.erl
+++ b/erts/emulator/test/socket_test_ttest_tcp_socket.erl
@@ -247,10 +247,16 @@ listen(Path, #{domain := local = Domain} = Opts)
listen(Port, #{domain := Domain} = Opts)
when is_integer(Port) andalso (Port >= 0) ->
%% Bind fills in the rest
- SA = #{family => Domain,
- port => Port},
- Cleanup = fun() -> ok end,
- do_listen(SA, Cleanup, Opts#{proto => tcp}).
+ case ?LIB:which_local_host_info(Domain) of
+ {ok, {_, _, Addr}} ->
+ SA = #{family => Domain,
+ addr => Addr,
+ port => Port},
+ Cleanup = fun() -> ok end,
+ do_listen(SA, Cleanup, Opts#{proto => tcp});
+ {error, _} = ERROR ->
+ ERROR
+ end.
do_listen(SA,
Cleanup,
diff --git a/erts/emulator/test/trace_call_time_SUITE.erl b/erts/emulator/test/trace_call_time_SUITE.erl
index 26f96a1766..9bab4cbbd8 100644
--- a/erts/emulator/test/trace_call_time_SUITE.erl
+++ b/erts/emulator/test/trace_call_time_SUITE.erl
@@ -254,8 +254,7 @@ combo(Config) when is_list(Config) ->
2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, [], [local]),
2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, true, [call_time]),
2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, MetaMs, [{meta,MetaTracer}]),
- %% not implemented
- %2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, true, [call_count]),
+ 2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, true, [call_count]),
1 = erlang:trace(Self, true, [{tracer,LocalTracer} | Flags]),
%%
@@ -284,9 +283,7 @@ combo(Config) when is_list(Config) ->
{value,{match_spec,[]}} = lists:keysearch(match_spec, 1, TraceInfoBif),
{value,{meta, MetaTracer}} = lists:keysearch(meta, 1, TraceInfoBif),
{value,{meta_match_spec,MetaMs}} = lists:keysearch(meta_match_spec, 1, TraceInfoBif),
- %% not implemented
- {value,{call_count,false}} = lists:keysearch(call_count, 1, TraceInfoBif),
- %{value,{call_count,0}} = lists:keysearch(call_count, 1, TraceInfoBif),
+ {value,{call_count,0}} = lists:keysearch(call_count, 1, TraceInfoBif),
{value,{call_time,[]}} = lists:keysearch(call_time, 1, TraceInfoBif),
%%
@@ -429,6 +426,8 @@ called_function(Config) when is_list(Config) ->
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
dead_tracer(Config) when is_list(Config) ->
+ TracedMFAs = dead_tracer_mfas(),
+
Self = self(),
FirstTracer = tracer(),
StartTracing = fun() -> turn_on_tracing(Self) end,
@@ -443,14 +442,14 @@ dead_tracer(Config) when is_list(Config) ->
erlang:yield(),
%% Collect and check that we only get call_time info for the current process.
- Info1 = collect_all_info(),
+ Info1 = collect_all_info(TracedMFAs),
[] = other_than_self(Info1),
io:format("~p\n", [Info1]),
%% Note that we have not turned off tracing for the current process,
%% but that the tracer has terminated. No more call_time information should be recorded.
[1,2,3] = seq(1, 3, fun(I) -> I + 1 end),
- [] = collect_all_info(),
+ [] = collect_all_info(TracedMFAs),
%% When we start a second tracer process, that tracer process must
%% not inherit the tracing flags and the dead tracer (even though
@@ -459,7 +458,7 @@ dead_tracer(Config) when is_list(Config) ->
tell_tracer(SecondTracer, StartTracing),
Seq20 = lists:seq(1, 20),
Seq20 = seq(1, 20, fun(I) -> I + 1 end),
- Info2 = collect_all_info(),
+ Info2 = collect_all_info(TracedMFAs),
io:format("~p\n", [Info2]),
[] = other_than_self(Info2),
SecondTracer ! quit,
@@ -495,9 +494,21 @@ turn_on_tracing(Pid) ->
_ = now(),
ok.
-collect_all_info() ->
- collect_all_info([{?MODULE,F,A} || {F,A} <- module_info(functions)] ++
- erlang:system_info(snifs)).
+%% We want to trace functions local to this module as well as all BIFs, and for
+%% the latter we need to ensure that their modules are loaded.
+dead_tracer_mfas() ->
+ Modules = [M || {M,_F,_A} <- erlang:system_info(snifs)],
+ Whitelist0 = gb_sets:from_list(Modules),
+ Whitelist = case code:ensure_modules_loaded(Modules) of
+ {error, Reasons} ->
+ Blacklist = gb_sets:from_list([M || {M, _} <- Reasons]),
+ gb_sets:subtract(Whitelist0, Blacklist);
+ ok ->
+ Whitelist0
+ end,
+ EligibleSNIFs = [MFA || {M,_F,_A}=MFA <- erlang:system_info(snifs),
+ gb_sets:is_element(M, Whitelist)],
+ [{?MODULE,F,A} || {F,A} <- module_info(functions)] ++ EligibleSNIFs.
collect_all_info([MFA|T]) ->
CallTime = erlang:trace_info(MFA, call_time),
@@ -567,21 +578,29 @@ seq_r(Start, Stop, Succ, R) ->
seq_r(Succ(Start), Stop, Succ, [Start | R]).
% Check call time tracing data and print mismatches
-check_trace_info(Mfa, [{Pid, C,_,_}] = Expect, Time) ->
- case erlang:trace_info(Mfa, call_time) of
- % Time tests are somewhat problematic. We want to know if Time (EXPECTED_TIME) and S*1000000 + Us (ACTUAL_TIME)
- % is the same.
- % If the ratio EXPECTED_TIME/ACTUAL_TIME is ~ 1 or if EXPECTED_TIME - ACTUAL_TIME is near zero, the test is ok.
- {call_time,[{Pid,C,S,Us}]} when S >= 0, Us >= 0, abs(1 - Time/(S*1000000 + Us)) < ?R_ERROR; abs(Time - S*1000000 - Us) < ?US_ERROR ->
+check_trace_info(Mfa, [{Pid, ExpectedC,_,_}] = Expect, Time) ->
+ {call_time,[{Pid,C,S,Us}]} = erlang:trace_info(Mfa, call_time),
+ {Mod, Name, Arity} = Mfa,
+ IsBuiltin = erlang:is_builtin(Mod, Name, Arity),
+ if
+ %% Call count on BIFs may exceed number of calls as they often trap to
+ %% themselves.
+ IsBuiltin, C >= ExpectedC, S >= 0, Us >= 0,
+ abs(1 - Time/(S*1000000 + Us)) < ?R_ERROR;
+ abs(Time - S*1000000 - Us) < ?US_ERROR ->
ok;
- {call_time,[{Pid,C,S,Us}]} ->
+ not IsBuiltin, C =:= ExpectedC, S >= 0, Us >= 0,
+ abs(1 - Time/(S*1000000 + Us)) < ?R_ERROR;
+ abs(Time - S*1000000 - Us) < ?US_ERROR ->
+ ok;
+ true ->
Sum = S*1000000 + Us,
- io:format("Expected ~p -> {call_time, ~p (Time ~p us)}~n - got ~w s. ~w us. = ~w us. - ~w -> delta ~w (ratio ~.2f, should be 1.0)~n",
- [Mfa, Expect, Time, S, Us, Sum, Time, Sum - Time, Time/Sum]),
- time_error;
- Other ->
- io:format("Expected ~p -> {call_time, ~p (Time ~p us)}~n - got ~p~n", [ Mfa, Expect, Time, Other]),
- time_count_error
+ io:format("Expected ~p -> {call_time, ~p (Time ~p us)}~n - got ~w "
+ "s. ~w us. = ~w us. - ~w -> delta ~w (ratio ~.2f, "
+ "should be 1.0)~n",
+ [Mfa, Expect, Time,
+ S, Us, Sum, Time, Sum - Time, Time/Sum]),
+ time_error
end;
check_trace_info(Mfa, Expect, _) ->
case erlang:trace_info(Mfa, call_time) of
@@ -670,9 +689,12 @@ loop() ->
quit ->
ok;
{Pid, execute, Fun } when is_function(Fun) ->
+ %% Make sure we always run with the same amount of reductions.
+ erlang:yield(),
Pid ! {self(), answer, erlang:apply(Fun, [])},
loop();
{Pid, execute, {M, F, A}} ->
+ erlang:yield(),
Pid ! {self(), answer, erlang:apply(M, F, A)},
loop()
end.
diff --git a/erts/emulator/utils/beam_makeops b/erts/emulator/utils/beam_makeops
index 605a402f2a..cc21f9b5b4 100755
--- a/erts/emulator/utils/beam_makeops
+++ b/erts/emulator/utils/beam_makeops
@@ -1315,7 +1315,7 @@ sub combine_instruction_group {
my $inc = 0;
unless ($i == $#slots) {
- $flags = "-no_next";
+ $flags = "-micro_instruction";
my $next_offset = $label_to_offset{$next};
$inc = ($offset + $size) - $next_offset;
$transfer_to_next = "I += $inc;\n" if $inc;
@@ -1553,8 +1553,10 @@ sub code_gen {
my $dispatch_next;
my $instr_offset = $group_size + $offset + 1;
- if ($flags =~ /-no_next/) {
+ if ($flags =~ /-micro_instruction/) {
$dispatch_next = "";
+ } elsif ($flags =~ /-no_next/) {
+ $dispatch_next = "ASSERT(!\"Fell through '$name' (-no_next)\");";
} elsif ($flags =~ /-no_prefetch/) {
$dispatch_next = "\nI += $instr_offset;\n" .
"ASSERT(VALID_INSTR(*I));\n" .
diff --git a/erts/emulator/utils/make_tables b/erts/emulator/utils/make_tables
index deee5c2344..ef0df28fc8 100755
--- a/erts/emulator/utils/make_tables
+++ b/erts/emulator/utils/make_tables
@@ -35,7 +35,6 @@ use File::Basename;
# Output:
# <-src>/erl_am.c
# <-src>/erl_bif_table.c
-# <-src>/erl_bif_wrap.c
# <-src>/erl_dirty_bif_wrap.c
# <-src>/erl_guard_bifs.c
# <-src>/hipe_nbif_impl.c
@@ -92,7 +91,7 @@ while (<>) {
my($type, @args) = split;
if ($type eq 'atom') {
save_atoms(@args);
- } elsif ($type eq 'bif' or $type eq 'ubif' or $type eq 'gcbif') {
+ } elsif ($type eq 'bif' or $type eq 'ubif' or $type eq 'hbif') {
if (@args > 2) {
error("$type only allows two arguments");
}
@@ -124,14 +123,22 @@ while (<>) {
error("invalid sched_type: $sched_type");
}
- my $wrapper;
- if ($type eq 'bif') {
- $wrapper = "wrap_$alias";
- } else {
- $wrapper = $alias;
- }
+ my $kind;
+ if ($type eq 'bif') {
+ $kind = 'BIF_KIND_REGULAR';
+ }
+ elsif ($type eq 'hbif') {
+ $kind = 'BIF_KIND_HEAVY';
+ }
+ elsif ($type eq 'ubif') {
+ $kind = 'BIF_KIND_GUARD';
+ }
+ else {
+ error("invalid bif_type: $type");
+ }
+
push(@bif, ["am_$atom_alias{$mod}","am_$atom_alias{$name}",$arity,
- $alias3,$wrapper,$alias]);
+ $alias3,$alias,$kind]);
push(@bif_info, [$type, $sched_type, $alias3, $alias]);
} elsif ($type eq 'dirty-cpu' or $type eq 'dirty-io'
or $type eq 'dirty-cpu-test' or $type eq 'dirty-io-test') {
@@ -196,7 +203,7 @@ open_file("$include/erl_bif_list.h");
my $i;
for ($i = 0; $i < @bif; $i++) {
# module atom, function atom, arity, C function, table index
- print "BIF_LIST($bif[$i]->[0],$bif[$i]->[1],$bif[$i]->[2],$bif[$i]->[3],$bif[$i]->[5],$i)\n";
+ print "BIF_LIST($bif[$i]->[0],$bif[$i]->[1],$bif[$i]->[2],$bif[$i]->[3],$bif[$i]->[4],$i)\n";
}
#
@@ -208,15 +215,24 @@ my $bif_size = @bif;
print <<EOF;
#ifndef __ERL_BIF_TABLE_H__
#define __ERL_BIF_TABLE_H__
+
+#include "sys.h"
+
typedef void *BifFunction;
+typedef enum {
+ BIF_KIND_REGULAR,
+ BIF_KIND_HEAVY,
+ BIF_KIND_GUARD
+} BifKind;
+
typedef struct bif_entry {
Eterm module;
Eterm name;
int arity;
BifFunction f;
- BifFunction traced;
BifFunction impl;
+ BifKind kind;
} BifEntry;
typedef struct erts_gc_bif {
@@ -232,7 +248,6 @@ typedef struct erts_u_bif {
extern BifEntry bif_table[];
extern Export* bif_export[];
-extern const ErtsGcBif erts_gc_bifs[];
extern const ErtsUBif erts_u_bifs[];
#define BIF_SIZE $bif_size
@@ -250,7 +265,6 @@ for ($i = 0; $i < @bif; $i++) {
my $args = join(', ', 'Process*', 'Eterm*', 'UWord*');
my $name = $bif_info[$i]->[3];
print "Eterm $name($args);\n";
- print "Eterm wrap_$name($args);\n";
print "Eterm erts_gc_$name(Process* p, Eterm* reg, Uint live);\n"
if $bif_info[$i]->[0] eq 'gcbif';
print "Eterm $bif_info[$i]->[2]($args);\n"
@@ -283,25 +297,6 @@ for ($i = 0; $i < @bif; $i++) {
print "};\n\n";
#
-# Generate the bif wrappers file.
-#
-
-open_file("$src/erl_bif_wrap.c");
-my $i;
-includes("export.h", "sys.h", "erl_vm.h", "global.h", "erl_process.h", "bif.h",
- "erl_bif_table.h", "erl_atom_table.h");
-for ($i = 0; $i < @bif; $i++) {
- next if $bif[$i]->[3] eq $bif[$i]->[4]; # Skip unwrapped bifs
- my $arity = $bif[$i]->[2];
- my $func = $bif_info[$i]->[3];
- print "Eterm\n";
- print "wrap_$func(Process* p, Eterm* args, UWord* I)\n";
- print "{\n";
- print " return erts_bif_trace($i, p, args, I);\n";
- print "}\n\n";
-}
-
-#
# Generate erl_gc_bifs.c.
#
@@ -309,18 +304,11 @@ open_file("$src/erl_guard_bifs.c");
my $i;
includes("export.h", "sys.h", "erl_vm.h", "global.h", "erl_process.h", "bif.h",
"erl_bif_table.h");
-print "const ErtsGcBif erts_gc_bifs[] = {\n";
-for ($i = 0; $i < @bif; $i++) {
- next unless $bif_info[$i]->[0] eq 'gcbif';
- print " {$bif[$i]->[3], erts_gc_$bif[$i]->[3], BIF_$bif[$i]->[5]},\n";
-}
-print " {NULL, NULL, -1}\n";
-print "};\n";
print "const ErtsUBif erts_u_bifs[] = {\n";
for ($i = 0; $i < @bif; $i++) {
next unless $bif_info[$i]->[0] eq 'ubif';
- print " {$bif[$i]->[3], BIF_$bif[$i]->[5]},\n";
+ print " {$bif[$i]->[3], BIF_$bif[$i]->[4]},\n";
}
print " {NULL, -1}\n";
print "};\n";
@@ -368,7 +356,7 @@ EOF
my $i;
for ($i = 0; $i < @bif; $i++) {
print <<EOF;
-Eterm nbif_impl_$bif[$i]->[5](Process *c_p, Eterm *regs);
+Eterm nbif_impl_$bif[$i]->[4](Process *c_p, Eterm *regs);
EOF
}
@@ -388,9 +376,9 @@ EOF
for ($i = 0; $i < @bif; $i++) {
print <<EOF;
-Eterm nbif_impl_$bif[$i]->[5](Process *c_p, Eterm *regs)
+Eterm nbif_impl_$bif[$i]->[4](Process *c_p, Eterm *regs)
{
- return $bif[$i]->[3](c_p, regs, (UWord *) bif_export\[BIF_$bif[$i]->[5]\]);
+ return $bif[$i]->[3](c_p, regs, (UWord *) bif_export\[BIF_$bif[$i]->[4]\]);
}
EOF
diff --git a/erts/etc/common/Makefile.in b/erts/etc/common/Makefile.in
index 1f35cef669..3a03374fbf 100644
--- a/erts/etc/common/Makefile.in
+++ b/erts/etc/common/Makefile.in
@@ -54,6 +54,8 @@ ERTS_INCL = -I$(ERL_TOP)/erts/include \
-I$(ERL_TOP)/erts/include/internal \
-I$(ERL_TOP)/erts/include/internal/$(TARGET)
+EI_INCL = -I$(ERL_TOP)/lib/erl_interface/include
+
CC = @CC@
WFLAGS = @WFLAGS@
CFLAGS = @CFLAGS@ @DEFS@ $(TYPE_FLAGS) @WFLAGS@ -I$(SYSOSDIR) -I$(EMUDIR) -I. \
@@ -87,6 +89,10 @@ DRVDIR = $(ERL_TOP)/erts/emulator/drivers/@ERLANG_OSTYPE@
UXETC = ../unix
WINETC = ../win32
+# Threads flags and libs
+THR_DEFS=@THR_DEFS@
+THR_LIBS=@THR_LIBS@
+
ifeq ($(TARGET), win32)
ETC = $(WINETC)
else
@@ -108,6 +114,8 @@ endif
ERTS_LIB = $(ERL_TOP)/erts/lib_src/obj/$(TARGET)/$(TYPE)/MADE
+EI_LIB = -L$(ERL_TOP)/lib/erl_interface/obj/$(TARGET) -lei $(THR_LIBS)
+
# ----------------------------------------------------
# Release directory specification
# ----------------------------------------------------
@@ -426,10 +434,10 @@ $(OBJDIR)/$(ERLEXEC).o: $(ERLEXECDIR)/$(ERLEXEC).c $(RC_GENERATED)
endif
$(BINDIR)/erlc@EXEEXT@: $(OBJDIR)/erlc.o $(ERTS_LIB)
- $(ld_verbose)$(PURIFY) $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/erlc.o -L$(OBJDIR) $(LIBS) $(ERTS_INTERNAL_LIBS)
+ $(ld_verbose)$(PURIFY) $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/erlc.o -L$(OBJDIR) $(LIBS) $(ERTS_INTERNAL_LIBS) $(EI_LIB)
$(OBJDIR)/erlc.o: erlc.c $(RC_GENERATED)
- $(V_CC) $(CFLAGS) -o $@ -c erlc.c
+ $(V_CC) $(CFLAGS) $(THR_DEFS) $(EI_INCL) $(MT_FLAG) -o $@ -c erlc.c
$(BINDIR)/dialyzer@EXEEXT@: $(OBJDIR)/dialyzer.o $(ERTS_LIB)
$(ld_verbose)$(PURIFY) $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/dialyzer.o -L$(OBJDIR) $(LIBS) $(ERTS_INTERNAL_LIBS)
diff --git a/erts/etc/common/erlc.c b/erts/etc/common/erlc.c
index aa99c69100..56060497f3 100644
--- a/erts/etc/common/erlc.c
+++ b/erts/etc/common/erlc.c
@@ -20,14 +20,32 @@
/*
* Purpose: Common compiler front-end.
*/
+
+#ifdef __WIN32__
+# include <winsock2.h>
+#else
+# include <stdio.h>
+# include <limits.h>
+# include <stdlib.h>
+# include <signal.h>
+# include <sys/stat.h>
+#endif
+
#include "etc_common.h"
+#include "ei.h"
#define NO 0
#define YES 1
#define ASIZE(a) (sizeof(a)/sizeof(a[0]))
-static int debug = 0; /* Bit flags for debug printouts. */
+#ifndef PATH_MAX
+# define PATH_MAX 4096
+#endif
+
+static int debug = 0; /* Debug level. */
+static int use_server = 0; /* Use compile server. */
+static char* source_file = "<no source>"; /* Source file (last argument). */
static char** eargv_base; /* Base of vector. */
static char** eargv; /* First argument for erl. */
@@ -57,6 +75,7 @@ static int pause_after_execution = 0;
* Local functions.
*/
+static void get_env_compile_server(void);
static char* process_opt(int* pArgc, char*** pArgv, int offset);
static void error(char* format, ...);
static void* emalloc(size_t size);
@@ -66,7 +85,17 @@ static void efree(void *p);
static char* strsave(char* string);
static void push_words(char* src);
static int run_erlang(char* name, char** argv);
+static void call_compile_server(char** argv);
+static void encode_env(ei_x_buff* buf);
+#ifndef __WIN32__
+static char* find_executable(char* progname);
+static char* safe_realpath(char* file);
+#endif
+static char* get_encoding(void);
+static char* decode_binary(const char* buf, int* dec_index, int* dec_size);
+static void start_compile_server(char* node_name, char** argv);
static char* get_default_emulator(char* progname);
+static char* possibly_unquote(char* arg);
#ifdef __WIN32__
static char* possibly_quote(char* arg);
static void* erealloc(void *p, size_t size);
@@ -194,6 +223,10 @@ int main(int argc, char** argv)
argv[argc] = NULL;
#endif
+ get_env_compile_server();
+
+ ei_init();
+
env = get_env("ERLC_EMULATOR");
emulator = env ? env : get_default_emulator(argv[0]);
@@ -203,9 +236,29 @@ int main(int argc, char** argv)
/*
* Add scriptname to env
*/
+
set_env("ESCRIPT_NAME", argv[0]);
/*
+ * Save a piece of configuration in an environment variable. The
+ * point is that the compile server needs to know that the same
+ * Erlang/OTP system would be started. On Unix, we save the full
+ * path to the Erlang emulator. On Windows, we save the value of
+ * the environment variable PATH. If the compile server finds that
+ * another Erlang/OTP system would be started, it will terminate
+ * itself.
+ */
+
+#ifdef __WIN32__
+ set_env("ERLC_CONFIGURATION", get_env("PATH"));
+#else
+ {
+ char* full_path_emulator = find_executable(emulator);
+ set_env("ERLC_CONFIGURATION", full_path_emulator);
+ }
+#endif
+
+ /*
* Allocate the argv vector to be used for arguments to Erlang.
* Arrange for starting to pushing information in the middle of
* the array, to allow easy adding of emulator options (like -pa)
@@ -252,6 +305,7 @@ int main(int argc, char** argv)
* Options starting with '+' are passed on to Erlang.
*/
+ source_file = "<no source>";
switch (argv[1][0]) {
case '+':
PUSH(argv[1]);
@@ -260,11 +314,18 @@ int main(int argc, char** argv)
switch (argv[1][1]) {
case 'd':
if (argv[1][2] == '\0') {
- debug = 1;
+ debug++;
} else {
PUSH(argv[1]);
}
break;
+ case 'n':
+ if (strcmp(argv[1], "-no-server") == 0) {
+ use_server = 0;
+ } else {
+ PUSH(argv[1]);
+ }
+ break;
case 'p':
{
int c = argv[1][2];
@@ -290,6 +351,8 @@ int main(int argc, char** argv)
case 's':
if (strcmp(argv[1], "-smp") == 0) {
UNSHIFT(argv[1]);
+ } else if (strcmp(argv[1], "-server") == 0) {
+ use_server = 1;
} else {
PUSH(argv[1]);
}
@@ -300,6 +363,7 @@ int main(int argc, char** argv)
}
break;
default:
+ source_file = argv[1];
PUSH(argv[1]);
break;
}
@@ -320,6 +384,9 @@ int main(int argc, char** argv)
*/
PUSH(NULL);
+ if (use_server) {
+ call_compile_server(eargv);
+ }
return run_erlang(eargv[0], eargv);
}
@@ -350,6 +417,45 @@ process_opt(int* pArgc, char*** pArgv, int offset)
}
static void
+get_env_compile_server(void)
+{
+ char* us = get_env("ERLC_USE_SERVER");
+
+ if (us == NULL) {
+ return; /* Keep default */
+ }
+
+ switch (us[0]) {
+ case 'f':
+ if (strcmp(us+1, "alse") == 0) {
+ use_server = 0;
+ return;
+ }
+ break;
+ case 'n':
+ if (strcmp(us+1, "o") == 0) {
+ use_server = 0;
+ return;
+ }
+ break;
+ case 't':
+ if (strcmp(us+1, "rue") == 0) {
+ use_server = 1;
+ return;
+ }
+ break;
+ case 'y':
+ if (strcmp(us+1, "es") == 0) {
+ use_server = 1;
+ return;
+ }
+ break;
+ }
+ fprintf(stderr, "erlc: Warning: Ignoring unrecognized value '%s' "
+ "for environment value ERLC_USE_SERVER\n", us);
+}
+
+static void
push_words(char* src)
{
char sbuf[MAXPATHLEN];
@@ -369,11 +475,12 @@ push_words(char* src)
if (sbuf[0])
PUSH(strsave(sbuf));
}
+
#ifdef __WIN32__
wchar_t *make_commandline(char **argv)
{
- static wchar_t *buff = NULL;
- static int siz = 0;
+ wchar_t *buff = NULL;
+ int siz = 0;
int num = 0, len;
char **arg;
wchar_t *p;
@@ -400,17 +507,17 @@ wchar_t *make_commandline(char **argv)
}
*(--p) = L'\0';
- if (debug) {
- printf("Processed command line:%S\n",buff);
+ if (debug > 1) {
+ fprintf(stderr, "Processed command line: %S\n", buff);
}
return buff;
}
-int my_spawnvp(char **argv)
+int my_spawnvp(int wait, char **argv)
{
STARTUPINFOW siStartInfo;
PROCESS_INFORMATION piProcInfo;
- DWORD ec;
+ DWORD ec = 0;
memset(&siStartInfo,0,sizeof(STARTUPINFOW));
siStartInfo.cb = sizeof(STARTUPINFOW);
@@ -436,12 +543,14 @@ int my_spawnvp(char **argv)
}
CloseHandle(piProcInfo.hThread);
- WaitForSingleObject(piProcInfo.hProcess,INFINITE);
- if (!GetExitCodeProcess(piProcInfo.hProcess,&ec)) {
- return 0;
+ if (wait) {
+ WaitForSingleObject(piProcInfo.hProcess,INFINITE);
+ if (!GetExitCodeProcess(piProcInfo.hProcess,&ec)) {
+ return 0;
+ }
}
return (int) ec;
-}
+}
#endif /* __WIN32__ */
@@ -452,11 +561,18 @@ run_erlang(char* progname, char** argv)
int status;
#endif
- if (debug) {
+ if (debug > 0) {
+ fprintf(stderr, "spawning erl for %s", source_file);
+ }
+ if (debug > 1) {
int i = 0;
- while (argv[i] != NULL)
- printf(" %s", argv[i++]);
- printf("\n");
+ fprintf(stderr, ":\n ");
+ while (argv[i] != NULL) {
+ fprintf(stderr, "%s ", argv[i++]);
+ }
+ }
+ if (debug) {
+ putc('\n', stderr);
}
#ifdef __WIN32__
@@ -466,7 +582,7 @@ run_erlang(char* progname, char** argv)
* we are finished and print a prompt and read keyboard input.
*/
- status = my_spawnvp(argv)/*_spawnvp(_P_WAIT,progname,argv)*/;
+ status = my_spawnvp(1, argv);
if (status == -1) {
fprintf(stderr, "erlc: Error executing '%s': %d", progname,
GetLastError());
@@ -485,6 +601,360 @@ run_erlang(char* progname, char** argv)
}
static void
+call_compile_server(char** argv)
+{
+ ei_cnode ec;
+ char* user;
+ char node_name[MAXNODELEN+1];
+ char remote[MAXNODELEN+1];
+ short creation = 1;
+ int fd;
+ char cwd[MAXPATHLEN+1];
+ ei_x_buff args;
+ ei_x_buff reply;
+ int reply_size;
+ int dec_size, dec_index;
+ char atom[MAXATOMLEN];
+ int argc;
+
+#ifdef __WIN32__
+ if (_getcwd(cwd, sizeof(cwd)) == 0) {
+ fprintf(stderr, "erlc: failed to get current working directory\n");
+ exit(2);
+ }
+#else
+ if (getcwd(cwd, sizeof(cwd)) == 0) {
+ fprintf(stderr, "erlc: failed to get current working directory\n");
+ exit(2);
+ }
+#endif
+
+#ifndef __WIN32__
+ {
+ struct sigaction act;
+
+ /*
+ * If the node is terminating when ei_rpc() is executed, the process
+ * may receive a SIGPIPE signal. Make sure it does not kill this process.
+ */
+ act.sa_handler = SIG_IGN;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+ sigaction(SIGPIPE, &act, NULL);
+ }
+#endif
+
+ /* Get user name */
+ user = get_env("USERNAME"); /* Windows */
+ if (!user) {
+ user = get_env("LOGNAME"); /* Unix */
+ }
+ if (!user) {
+ user = get_env("USER"); /* Unix */
+ }
+ if (!user) {
+ user = "nouser";
+ }
+
+ /* Create my own node name. */
+#ifdef __WIN32__
+ sprintf(node_name, "erlc_client_%s_%lu", user, (unsigned long) GetCurrentProcessId());
+#else
+ sprintf(node_name, "erlc_client_%s_%d", user, getpid());
+#endif
+
+ if (ei_connect_init(&ec, node_name, NULL, creation) < 0) {
+ /*
+ * There is probably no .erlang.cookie file.
+ */
+ if (debug > 1) {
+ fprintf(stderr, "\ncan't create C node %s: %s\n",
+ node_name, strerror(erl_errno));
+ }
+ sprintf(remote, "erl_compile_server_%s@host", user);
+ goto start_compile_server;
+ }
+
+ /* Create node name for compile server. */
+
+ sprintf(remote, "erl_compile_server_%s@%s", user, ei_thishostname(&ec));
+
+ if ((fd = ei_connect(&ec, remote)) < 0) {
+ if (debug > 1) {
+ fprintf(stderr, "failed to connect to compile server %s: %s\n",
+ remote, strerror(erl_errno));
+ }
+ goto start_compile_server;
+ }
+
+ /*
+ * Encode the request to the compile server.
+ */
+
+ ei_x_new_with_version(&args);
+ ei_x_encode_list_header(&args, 1);
+ ei_x_encode_map_header(&args, 4);
+ ei_x_encode_atom(&args, "encoding");
+ ei_x_encode_atom(&args, get_encoding());
+ ei_x_encode_atom(&args, "cwd");
+ ei_x_encode_string(&args, cwd);
+ ei_x_encode_atom(&args, "env");
+ encode_env(&args);
+ ei_x_encode_atom(&args, "command_line");
+ argc = 0;
+ while (argv[argc]) {
+ ei_x_encode_list_header(&args, 1);
+ ei_x_encode_string(&args, possibly_unquote(argv[argc]));
+ argc++;
+ }
+ ei_x_encode_empty_list(&args); /* End of command_line */
+ ei_x_encode_empty_list(&args); /* End of argument list for apply */
+
+ /*
+ * Do a RPC to the compile server.
+ */
+
+ ei_x_new_with_version(&reply);
+ reply_size = ei_rpc(&ec, fd, "erl_compile_server", "compile",
+ args.buff+1, args.index-1, &reply);
+ if (reply_size < 0) {
+ if (debug > 1) {
+ fprintf(stderr, "failed to rpc to node %s: %s\n",
+ remote, strerror(erl_errno));
+ }
+ goto start_compile_server;
+ }
+
+ /*
+ * Decode the answer.
+ */
+
+ dec_index = 0;
+ if (ei_decode_atom(reply.buff, &dec_index, atom) == 0 &&
+ strcmp(atom, "wrong_config") == 0) {
+ if (debug > 1) {
+ fprintf(stderr, "wrong configuration\n");
+ }
+ goto start_compile_server;
+ } else if (ei_decode_tuple_header(reply.buff, &dec_index, &dec_size) == 0) {
+ atom[0] = '\0';
+ if (dec_size >= 2) {
+ ei_decode_atom(reply.buff, &dec_index, atom);
+ }
+ if (dec_size == 2) {
+ if (strcmp(atom, "ok") == 0) {
+ char* output = decode_binary(reply.buff, &dec_index, &dec_size);
+ if (debug) {
+ fprintf(stderr, "called server for %s => ok\n", source_file);
+ }
+ if (output) {
+ fwrite(output, dec_size, 1, stdout);
+ exit(0);
+ }
+ }
+ } else if (dec_size == 3 && strcmp(atom, "error") == 0) {
+ int std_size, err_size;
+ char* std;
+ char* err;
+
+ if (debug) {
+ fprintf(stderr, "called server for %s => error\n", source_file);
+ }
+ std = decode_binary(reply.buff, &dec_index, &std_size);
+ err = decode_binary(reply.buff, &dec_index, &err_size);
+ if (std && err) {
+ fwrite(err, err_size, 1, stderr);
+ fwrite(std, std_size, 1, stdout);
+ exit(1);
+ }
+ }
+ }
+
+ /*
+ * Unrecognized term, probably because the node was shutting down.
+ */
+
+ if (debug > 1) {
+ fprintf(stderr, "unrecognized term returned by compilation server:\n");
+ dec_index = 0;
+ ei_print_term(stderr, reply.buff, &dec_index);
+ putc('\n', stderr);
+ }
+
+ start_compile_server:
+ *strchr(remote, '@') = '\0';
+ start_compile_server(remote, argv);
+}
+
+static void
+encode_env(ei_x_buff* buf)
+{
+ char* env_names[] = {"ERL_AFLAGS",
+ "ERL_FLAGS",
+ "ERL_ZFLAGS",
+ "ERL_COMPILER_OPTIONS",
+ "ERL_LIBS",
+ "ERLC_CONFIGURATION",
+ 0};
+ char** p = env_names;
+ while (p[0]) {
+ char* val;
+
+ if ((val = get_env(p[0])) != 0) {
+ ei_x_encode_list_header(buf, 1);
+ ei_x_encode_tuple_header(buf, 2);
+ ei_x_encode_string(buf, p[0]);
+ ei_x_encode_string(buf, val);
+ }
+ p++;
+ }
+ ei_x_encode_empty_list(buf);
+}
+
+#ifndef __WIN32__
+static char*
+find_executable(char* progname)
+{
+ char* path;
+ char* start_path;
+ char* real_name;
+ char buf[PATH_MAX];
+ size_t len_component;
+ size_t len_prog;
+
+ if (strchr(progname, '/')) {
+ return progname;
+ }
+
+ len_prog = strlen(progname);
+
+ if (!(path = getenv("PATH"))) {
+ path = "/bin:/usr/bin";
+ }
+
+ do {
+ for (start_path = path; *path != '\0' && *path != ':'; path++) {
+ ;
+ }
+ if (start_path == path) {
+ start_path = ".";
+ len_component = 1;
+ } else {
+ len_component = path - start_path;
+ }
+ memcpy(buf, start_path, len_component);
+ buf[len_component] = '/';
+ memcpy(buf + len_component + 1, progname, len_prog);
+ buf[len_component + len_prog + 1] = '\0';
+ if ((real_name = safe_realpath(buf)) != 0) {
+ struct stat s;
+ if (stat(real_name, &s) == 0 && s.st_mode & S_IFREG) {
+ return real_name;
+ }
+ }
+ } while (*path++ == ':');
+ return progname;
+}
+
+static char*
+safe_realpath(char* file)
+{
+ /*
+ * Always allocate a buffer for the result of realpath().
+ * realpath() on old versions of MacOS X will crash if the buffer
+ * argument is NULL, and realpath() will fail on old versions of
+ * Solaris.
+ */
+ char* real_name = emalloc(PATH_MAX + 1);
+ return realpath(file, real_name);
+}
+#endif
+
+static char*
+get_encoding(void)
+{
+#ifdef __WIN32__
+ return "latin1";
+#else
+ char* p;
+ p = get_env("LC_ALL");
+ if (!p) {
+ p = get_env("LC_CTYPE");
+ }
+ if (!p) {
+ p = get_env("LANG");
+ }
+ if (!p) {
+ return "latin1";
+ } else {
+ return strstr(p, "UTF-8") ? "utf8" : "latin1";
+ }
+#endif
+}
+
+static char*
+decode_binary(const char* buf, int* dec_index, int* dec_size)
+{
+ int dec_type;
+ char* bin;
+
+ ei_get_type(buf, dec_index, &dec_type, dec_size);
+ bin = emalloc(*dec_size);
+ if (ei_decode_binary(buf, dec_index, bin, NULL) < 0) {
+ return NULL;
+ }
+ return bin;
+}
+
+static void
+start_compile_server(char* node_name, char** argv)
+{
+ char* eargv[100];
+ int eargc = 0;
+ char* progname = argv[0];
+
+ while (strcmp(argv[0], "-mode") != 0) {
+ eargv[eargc++] = *argv++;
+ }
+ PUSH2("-boot", "no_dot_erlang");
+ PUSH2("-sname", node_name);
+ PUSH("-hidden");
+ PUSH("-detached");
+ PUSH3("-kernel", "start_compile_server", "true");
+
+ /*
+ * If this is an older Erlang system (before 22.1) that does not
+ * support the compile server, terminate immediately.
+ */
+ PUSH2("-eval", "is_pid(whereis(erl_compile_server)) orelse halt(1)");
+
+ PUSH(NULL);
+
+ if (debug == 1) {
+ fprintf(stderr, "starting compile server %s\n", node_name);
+ } else if (debug > 1) {
+ int i = 0;
+ fprintf(stderr, "starting compile server %s:\n", node_name);
+ while (eargv[i] != NULL) {
+ fprintf(stderr, "%s ", eargv[i++]);
+ }
+ putc('\n', stderr);
+ }
+
+#ifdef __WIN32__
+ if (my_spawnvp(0, eargv) == -1) {
+ fprintf(stderr, "erlc: Error executing '%s': %d", progname,
+ GetLastError());
+ }
+#else
+ if (fork() == 0) {
+ execvp(eargv[0], eargv);
+ error("Error %d executing \'%s\'.", errno, progname);
+ }
+#endif
+}
+
+static void
error(char* format, ...)
{
char sbuf[1024];
@@ -565,6 +1035,42 @@ get_default_emulator(char* progname)
return ERL_NAME;
}
+
+static char*
+possibly_unquote(char* arg)
+{
+#ifndef __WIN32__
+ /* Nothing to do if not Windows. */
+ return arg;
+#else
+ char* unquoted;
+ char* dstp;
+
+ if (arg[0] != '"') {
+ /* Not quoted. Nothing to do. */
+ return arg;
+ }
+
+ /*
+ * Remove the quotes and remove backslashes before quotes.
+ */
+
+ unquoted = emalloc(strlen(arg) + 1);
+ arg++;
+ dstp = unquoted;
+ while (*arg) {
+ if (arg[0] == '\\' && arg[1] == '"') {
+ *dstp++ = '"';
+ arg += 2;
+ } else {
+ *dstp++ = *arg++;
+ }
+ }
+ *--dstp = 0;
+ return unquoted;
+#endif
+}
+
#ifdef __WIN32__
static char*
possibly_quote(char* arg)
@@ -623,4 +1129,5 @@ possibly_quote(char* arg)
*s = '\0';
return narg;
}
+
#endif /* __WIN32__ */
diff --git a/erts/etc/unix/etp-commands.in b/erts/etc/unix/etp-commands.in
index 8b6abb5336..283f859651 100644
--- a/erts/etc/unix/etp-commands.in
+++ b/erts/etc/unix/etp-commands.in
@@ -1187,12 +1187,14 @@ document etp-cp
%---------------------------------------------------------------------------
% etp-cp Eterm
%
-% Take a code continuation pointer and print
+% Take a code continuation pointer or instruction pointer and print
% module, function, arity and offset.
%
-% Code continuation pointers can be found in the process structure e.g
-% process_tab[i]->cp and process_tab[i]->i, the second is the
-% program counter, which is the same thing as a continuation pointer.
+% Code continuation pointers can be found on the stack. The instruction
+% pointer is found in the process struct. For example:
+%
+% c_p->i
+% process_tab[i]->i
%---------------------------------------------------------------------------
end
@@ -1462,10 +1464,6 @@ define etp-stack-preamble
printf "I: "
etp ((Eterm)($arg0)->i)
end
- if ($arg0)->cp != 0
- printf "cp:"
- etp ((Eterm)($arg0)->cp)
- end
end
define etp-stack-preamble-emu
@@ -1474,10 +1472,6 @@ define etp-stack-preamble-emu
printf "%% Stacktrace (%u)\n", $etp_stack_end-$etp_stack_p
printf "I: "
etp ((BeamInstr)I)
- if ($arg0)->cp != 0
- printf "cp: "
- etp ((Eterm)($arg0)->cp)
- end
end
define etp-stacktrace-1
@@ -2183,13 +2177,6 @@ define etp-process-info-int
else
printf "unknown\n"
end
- printf " CP: "
- if ($etp_proc->cp)
- etp-cp-1 $etp_proc->cp
- printf "\n"
- else
- printf "unknown\n"
- end
printf " I: "
if ($etp_proc->i)
etp-cp-1 $etp_proc->i
@@ -2419,11 +2406,6 @@ define etp-process-memory-info
end
end
- if ($etp_pmem_proc->cp)
- printf " CP: "
- etp-cp-1 $etp_pmem_proc->cp
- printf " "
- end
printf "\n"
end
end
@@ -3993,22 +3975,79 @@ end
# ETS table debug
#
+define etp-ets-tab-status-int
+# Args:
+#
+# Non-reentrant
+ if ($arg0 & 0x1)
+ printf "priv"
+ end
+ if ($arg0 & 0x2)
+ printf "prot"
+ end
+ if ($arg0 & 0x4)
+ printf "pub"
+ end
+ if ($arg0 & 0x8)
+ printf "|del"
+ end
+ if ($arg0 & 0x10)
+ printf "|set"
+ end
+ if ($arg0 & 0x20)
+ printf "|bag"
+ end
+ if ($arg0 & 0x40)
+ printf "|dbag"
+ end
+ if ($arg0 & 0x80)
+ printf "|oset"
+ end
+ if ($arg0 & 0x100)
+ printf "|caoset"
+ end
+ if ($arg0 & 0x200)
+ printf "|flocked"
+ end
+ if ($arg0 & 0x400)
+ printf "|fread"
+ end
+ if ($arg0 & 0x800)
+ printf "|named"
+ end
+ if ($arg0 & 0x1000)
+ printf "|busy"
+ end
+end
+
define etp-ets-tables
# Args:
#
# Non-reentrant
- printf "%% Dumping < %lu ETS tables\n", (unsigned long)db_max_tabs
- while $etp_ets_tables_i < db_max_tabs
- if (meta_main_tab[$etp_ets_tables_i].u.next_free & 3) == 0
- printf "%% %d:", $etp_ets_tables_i
- etp-1 ((Eterm)(meta_main_tab[$etp_ets_tables_i].u.tb->common.id)) 0
+ set $sched_ix = 0
+ while $sched_ix < erts_no_schedulers
+ set $ets_tabs = &erts_aligned_scheduler_data[$sched_ix].esd.ets_tables
+ set $no_ets_tabs = *(unsigned long *)&($ets_tabs->count)
+ printf "\n%% %lu ETS tables created on scheduler %lu...\n\n", $no_ets_tabs, (unsigned long)$sched_ix+1
+ set $ets_tab = $ets_tabs->clist
+ set $first_ets_tab = $ets_tab
+ while $ets_tab
+ set $refn = &((ErtsMagicBinary *)$ets_tab->common->btid)->refn
+ printf "%% "
+ etp-1 $ets_tab->common.the_name 0
+ printf " #Ref<0.%u.%u.%u> ", (unsigned)$refn[2], (unsigned)$refn[1], (unsigned)$refn[0]
+ etp-1 $ets_tab->common.owner 0
printf " "
- etp-1 ((Eterm)(meta_main_tab[$etp_ets_tables_i].u.tb->common.owner)) 0
- printf "\n"
+ etp-ets-tab-status-int $ets_tab->common.status
+ printf " (DbTable*)%p\n", $ets_tab
+ if $ets_tab->common.all.next == $first_ets_tab
+ set $ets_tab = (DbTable *) 0
+ else
+ set $ets_tab = $ets_tab->common.all.next
+ end
end
- set $etp_ets_tables_i++
+ set $sched_ix++
end
- set $etp_ets_tables_i = 0
end
document etp-ets-tables
diff --git a/erts/lib_src/pthread/ethread.c b/erts/lib_src/pthread/ethread.c
index b4b12fcd86..b567ed81b0 100644
--- a/erts/lib_src/pthread/ethread.c
+++ b/erts/lib_src/pthread/ethread.c
@@ -208,9 +208,9 @@ ethr_x86_cpuid__(int *eax, int *ebx, int *ecx, int *edx)
"popl %%eax\n\t"
"movl $0x0, %0\n\t"
"xorl %%ecx, %%eax\n\t"
- "jz no_cpuid\n\t"
+ "jz 1f\n\t"
"movl $0x1, %0\n\t"
- "no_cpuid:\n\t"
+ "1:\n\t"
: "=r"(have_cpuid)
:
: "%eax", "%ecx", "cc");
diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam
index 866f9df79f..1530982393 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 dd08111aad..d5f6c16f09 100644
--- a/erts/preloaded/ebin/erts_internal.beam
+++ b/erts/preloaded/ebin/erts_internal.beam
Binary files differ
diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam
index a2c5f2f336..0152a6091f 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 f67b660a08..f77e9e863d 100644
--- a/erts/preloaded/ebin/prim_inet.beam
+++ b/erts/preloaded/ebin/prim_inet.beam
Binary files differ
diff --git a/erts/preloaded/ebin/socket.beam b/erts/preloaded/ebin/socket.beam
index 25eb0b2f4a..73a0bd4f72 100644
--- a/erts/preloaded/ebin/socket.beam
+++ b/erts/preloaded/ebin/socket.beam
Binary files differ
diff --git a/erts/preloaded/src/Makefile b/erts/preloaded/src/Makefile
index e1bb2ee5c4..38b85915cc 100644
--- a/erts/preloaded/src/Makefile
+++ b/erts/preloaded/src/Makefile
@@ -95,6 +95,15 @@ ERL_COMPILE_FLAGS += +debug_info -I$(KERNEL_SRC) -I$(KERNEL_INCLUDE)
DIA_PLT = erts-preloaded.plt
DIA_ANALYSIS = $(basename $(DIA_PLT)).dialyzer_analysis
+ifeq ($(DIAW_EH),true)
+DIA_WARNINGS += -Werror_handling
+endif
+ifeq ($(DIAW_US),true)
+DIA_WARNINGS += -Wunderspecs
+endif
+ifeq ($(DIAW_UR),true)
+DIA_WARNINGS += -Wunmatched_returns
+endif
debug opt: $(TARGET_FILES)
@@ -144,6 +153,7 @@ dialyzer: $(DIA_PLT)
@echo "Running dialyzer on $(basename $(DIA_PLT))"
@dialyzer --plt $< \
../ebin \
+ $(DIA_WARNINGS) \
--verbose
#
diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl
index 12c256169c..94595ab78f 100644
--- a/erts/preloaded/src/erlang.erl
+++ b/erts/preloaded/src/erlang.erl
@@ -109,8 +109,11 @@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-export([adler32/1, adler32/2, adler32_combine/3, append_element/2]).
--export([atom_to_binary/2, atom_to_list/1, binary_part/2, binary_part/3]).
--export([binary_to_atom/2, binary_to_existing_atom/2, binary_to_float/1]).
+-export([atom_to_binary/1, atom_to_binary/2]).
+-export([atom_to_list/1, binary_part/2, binary_part/3]).
+-export([binary_to_atom/1, binary_to_atom/2]).
+-export([binary_to_existing_atom/1, binary_to_existing_atom/2]).
+-export([binary_to_float/1]).
-export([binary_to_integer/1,binary_to_integer/2]).
-export([binary_to_list/1]).
-export([binary_to_list/3, binary_to_term/1, binary_to_term/2]).
@@ -186,6 +189,27 @@
-export([dt_get_tag/0, dt_get_tag_data/0, dt_prepend_vm_tag_data/1, dt_append_vm_tag_data/1,
dt_put_tag/1, dt_restore_tag/1, dt_spread_tag/1]).
+%% Operators
+
+-export(['=='/2, '=:='/2,
+ '/='/2, '=/='/2,
+ '=<'/2, '>='/2,
+ '<'/2, '>'/2]).
+
+-export(['-'/1, '+'/1,
+ '-'/2, '+'/2,
+ '/'/2, '*'/2,
+ 'div'/2, 'rem'/2,
+ 'bsl'/2, 'bsr'/2,
+ 'bor'/2, 'band'/2,
+ 'bxor'/2, 'bnot'/1]).
+
+-export(['and'/2, 'or'/2,
+ 'xor'/2, 'not'/1]).
+
+-export(['--'/2, '++'/2]).
+
+-export(['!'/2]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Simple native code BIFs
@@ -341,6 +365,12 @@ adler32_combine(_FirstAdler, _SecondAdler, _SecondSize) ->
append_element(_Tuple1, _Term) ->
erlang:nif_error(undefined).
+%% atom_to_binary/1
+-spec atom_to_binary(Atom) -> binary() when
+ Atom :: atom().
+atom_to_binary(Atom) ->
+ erlang:atom_to_binary(Atom, utf8).
+
%% atom_to_binary/2
-spec atom_to_binary(Atom, Encoding) -> binary() when
Atom :: atom(),
@@ -371,6 +401,12 @@ binary_part(_Subject, _PosLen) ->
binary_part(_Subject, _Start, _Length) ->
erlang:nif_error(undefined).
+%% binary_to_atom/1
+-spec binary_to_atom(Binary) -> atom() when
+ Binary :: binary().
+binary_to_atom(Binary) ->
+ erlang:binary_to_atom(Binary, utf8).
+
%% binary_to_atom/2
-spec binary_to_atom(Binary, Encoding) -> atom() when
Binary :: binary(),
@@ -378,6 +414,12 @@ binary_part(_Subject, _Start, _Length) ->
binary_to_atom(_Binary, _Encoding) ->
erlang:nif_error(undefined).
+%% binary_to_existing_atom/1
+-spec binary_to_existing_atom(Binary) -> atom() when
+ Binary :: binary().
+binary_to_existing_atom(Binary) ->
+ erlang:binary_to_existing_atom(Binary, utf8).
+
%% binary_to_existing_atom/2
-spec binary_to_existing_atom(Binary, Encoding) -> atom() when
Binary :: binary(),
@@ -3911,3 +3953,98 @@ gc_info(Ref, N, {OrigColls,OrigRecl}) ->
{Ref, {_,Colls, Recl}} ->
gc_info(Ref, N-1, {Colls+OrigColls,Recl+OrigRecl})
end.
+
+%% Operators
+
+-spec erlang:'=='(term(), term()) -> boolean().
+'=='(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'=:='(term(), term()) -> boolean().
+'=:='(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'/='(term(), term()) -> boolean().
+'/='(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'=/='(term(), term()) -> boolean().
+'=/='(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'=<'(term(), term()) -> boolean().
+'=<'(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'>='(term(), term()) -> boolean().
+'>='(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'<'(term(), term()) -> boolean().
+'<'(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'>'(term(), term()) -> boolean().
+'>'(_A, _B) ->
+ erlang:nif_error(undefined).
+
+-spec erlang:'-'(number()) -> number().
+'-'(_A) ->
+ erlang:nif_error(undefined).
+-spec erlang:'+'(number()) -> number().
+'+'(_A) ->
+ erlang:nif_error(undefined).
+-spec erlang:'-'(number(), number()) -> number().
+'-'(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'+'(number(), number()) -> number().
+'+'(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'/'(number(), number()) -> float().
+'/'(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'*'(number(), number()) -> number().
+'*'(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'div'(integer(), integer()) -> integer().
+'div'(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'rem'(integer(), integer()) -> integer().
+'rem'(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'bsl'(integer(), integer()) -> integer().
+'bsl'(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'bsr'(integer(), integer()) -> integer().
+'bsr'(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'bor'(integer(), integer()) -> integer().
+'bor'(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'band'(integer(), integer()) -> integer().
+'band'(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'bxor'(integer(), integer()) -> integer().
+'bxor'(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'bnot'(integer()) -> integer().
+'bnot'(_A) ->
+ erlang:nif_error(undefined).
+
+-spec erlang:'--'(list(), list()) -> list().
+'--'(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'++'(list(), term()) -> term().
+'++'(_A, _B) ->
+ erlang:nif_error(undefined).
+
+-spec erlang:'and'(boolean(), boolean()) -> boolean().
+'and'(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'or'(boolean(), boolean()) -> boolean().
+'or'(_A, _B) ->
+ erlang:nif_error(undefined).
+
+-spec erlang:'xor'(boolean(), boolean()) -> boolean().
+'xor'(_A, _B) ->
+ erlang:nif_error(undefined).
+-spec erlang:'not'(boolean()) -> boolean().
+'not'(_A) ->
+ erlang:nif_error(undefined).
+
+-spec erlang:'!'(dst(), term()) -> term().
+'!'(_Dst, _Msg) ->
+ erlang:nif_error(undefined).
diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl
index 305b524438..33363a3c82 100644
--- a/erts/preloaded/src/erts_internal.erl
+++ b/erts/preloaded/src/erts_internal.erl
@@ -99,6 +99,9 @@
-export([spawn_system_process/3]).
+-export([ets_lookup_binary_info/2, ets_super_user/1, ets_info_binary/1,
+ ets_raw_first/1, ets_raw_next/2]).
+
%%
%% Await result of send to port
%%
@@ -735,3 +738,71 @@ counters_info(_Ref) ->
Args :: list().
spawn_system_process(_Mod, _Func, _Args) ->
erlang:nif_error(undefined).
+
+
+%%
+%% ETS info internals...
+%%
+
+-spec ets_lookup_binary_info(Tab, Key) -> BinInfo when
+ Tab :: ets:tab(),
+ Key :: term(),
+ BinInfo :: [{non_neg_integer(), non_neg_integer(), non_neg_integer()}].
+
+ets_lookup_binary_info(_Tab, _Key) ->
+ erlang:nif_error(undef).
+
+-spec ets_super_user(Bool) -> 'ok' when
+ Bool :: boolean().
+
+ets_super_user(_Bool) ->
+ erlang:nif_error(undef).
+
+-spec ets_raw_first(Tab) -> term() when
+ Tab :: ets:tab().
+
+ets_raw_first(_Tab) ->
+ erlang:nif_error(undef).
+
+-spec ets_raw_next(Tab, Key) -> term() when
+ Tab :: ets:tab(),
+ Key :: term().
+
+ets_raw_next(_Tab, _Key) ->
+ erlang:nif_error(undef).
+
+-spec ets_info_binary(Tab) -> BinInfo when
+ Tab :: ets:tab(),
+ BinInfo :: [{non_neg_integer(), non_neg_integer(), non_neg_integer()}].
+
+ets_info_binary(Tab) ->
+ try
+ erts_internal:ets_super_user(true),
+ ets:safe_fixtable(Tab, true),
+ ets_info_binary_iter(Tab, erts_internal:ets_raw_first(Tab), [])
+ catch
+ C:R:S ->
+ ets_info_binary_error(Tab, C, R, S)
+ after
+ ets:safe_fixtable(Tab, false),
+ erts_internal:ets_super_user(false)
+ end.
+
+ets_info_binary_error(Tab, C, R, []) ->
+ erlang:raise(C, R, [{ets, info, [Tab, binary], []}]);
+ets_info_binary_error(Tab, C, R, [SF|SFs]) when
+ element(1, SF) == erts_internal,
+ element(2, SF) == ets_info_binary ->
+ erlang:raise(C, R, [{ets, info, [Tab, binary], []}|SFs]);
+ets_info_binary_error(Tab, C, R, [_SF|SFs]) ->
+ ets_info_binary_error(Tab, C, R, SFs).
+
+ets_info_binary_iter(_Tab, '$end_of_table', Acc) ->
+ Acc;
+ets_info_binary_iter(Tab, Key, Acc) ->
+ NewAcc = case erts_internal:ets_lookup_binary_info(Tab, Key) of
+ [] -> Acc;
+ [BI] -> [BI|Acc];
+ [_|_] = BIL -> BIL ++ Acc
+ end,
+ ets_info_binary_iter(Tab, erts_internal:ets_raw_next(Tab, Key), NewAcc).
diff --git a/erts/preloaded/src/prim_file.erl b/erts/preloaded/src/prim_file.erl
index 1aa5d85c64..1982424191 100644
--- a/erts/preloaded/src/prim_file.erl
+++ b/erts/preloaded/src/prim_file.erl
@@ -34,6 +34,7 @@
-export([read_link/1, read_link_all/1,
read_link_info/1, read_link_info/2,
read_file_info/1, read_file_info/2,
+ read_handle_info/1, read_handle_info/2,
write_file_info/2, write_file_info/3]).
-export([list_dir/1, list_dir_all/1]).
@@ -497,6 +498,8 @@ get_handle_nif(_FileRef) ->
erlang:nif_error(undef).
delayed_close_nif(_FileRef) ->
erlang:nif_error(undef).
+read_handle_info_nif(_FileRef) ->
+ erlang:nif_error(undef).
%%
%% Quality-of-life helpers
@@ -598,20 +601,37 @@ read_link_info(Name, Opts) ->
read_info_1(Name, FollowLinks, TimeType) ->
try
case read_info_nif(encode_path(Name), FollowLinks) of
- {error, Reason} ->
- {error, Reason};
- FileInfo ->
- CTime = from_posix_seconds(FileInfo#file_info.ctime, TimeType),
- MTime = from_posix_seconds(FileInfo#file_info.mtime, TimeType),
- ATime = from_posix_seconds(FileInfo#file_info.atime, TimeType),
- {ok, FileInfo#file_info{ ctime = CTime,
- mtime = MTime,
- atime = ATime }}
+ {error, Reason} -> {error, Reason};
+ FileInfo -> {ok, adjust_times(FileInfo, TimeType)}
+ end
+ catch
+ error:_ -> {error, badarg}
+ end.
+
+read_handle_info(Fd) ->
+ read_handle_info_1(Fd, local).
+read_handle_info(Fd, Opts) ->
+ read_handle_info_1(Fd, proplist_get_value(time, Opts, local)).
+
+read_handle_info_1(Fd, TimeType) ->
+ try
+ #{ handle := FRef } = get_fd_data(Fd),
+ case read_handle_info_nif(FRef) of
+ {error, Reason} -> {error, Reason};
+ FileInfo -> {ok, adjust_times(FileInfo, TimeType)}
end
catch
error:_ -> {error, badarg}
end.
+adjust_times(FileInfo, TimeType) ->
+ CTime = from_posix_seconds(FileInfo#file_info.ctime, TimeType),
+ MTime = from_posix_seconds(FileInfo#file_info.mtime, TimeType),
+ ATime = from_posix_seconds(FileInfo#file_info.atime, TimeType),
+ FileInfo#file_info{ ctime = CTime,
+ mtime = MTime,
+ atime = ATime }.
+
write_file_info(Filename, Info) ->
write_file_info_1(Filename, Info, local).
write_file_info(Filename, Info, Opts) ->
diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl
index 374facb2a3..4dab3de006 100644
--- a/erts/preloaded/src/prim_inet.erl
+++ b/erts/preloaded/src/prim_inet.erl
@@ -427,7 +427,7 @@ accept_opts(L, S, FamilyOpts) ->
case
getopts(
L,
- [active, nodelay, keepalive, delay_send, priority]
+ [active, nodelay, keepalive, delay_send, priority, linger]
++ FamilyOpts)
of
{ok, Opts} ->
diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl
index be94e3a867..07e720c44d 100644
--- a/erts/preloaded/src/socket.erl
+++ b/erts/preloaded/src/socket.erl
@@ -313,7 +313,8 @@
path := binary() | string()}.
-type sockaddr_in4() :: #{family := inet,
port := port_number(),
- addr := any | loopback | ip4_address()}.
+ %% The 'broadcast' here is the "limited broadcast"
+ addr := any | broadcast | loopback | ip4_address()}.
-type sockaddr_in6() :: #{family := inet6,
port := port_number(),
addr := any | loopback | ip6_address(),
@@ -334,7 +335,7 @@
-define(SOCKADDR_IN6_DEFAULTS, ?SOCKADDR_IN6_DEFAULTS(any)).
-define(SOCKADDR_IN6_DEFAULT(A), (?SOCKADDR_IN6_DEFAULTS(A))#{family => inet6}).
-%% otp - The option is internal to our (OTP) imeplementation.
+%% otp - This option is internal to our (OTP) implementation.
%% socket - The socket layer (SOL_SOCKET).
%% ip - The IP layer (SOL_IP or is it IPPROTO_IP?).
%% ipv6 - The IPv6 layer (SOL_IPV6).
@@ -342,6 +343,7 @@
%% udp - The UDP (User Datagram Protocol) layer (IPPROTO_UDP).
%% sctp - The SCTP (Stream Control Transmission Protocol) layer (IPPROTO_SCTP).
%% Int - Raw level, sent down and used "as is".
+%% Its up to the caller to make sure this is correct!
-type sockopt_level() :: otp |
socket |
ip | ipv6 | tcp | udp | sctp |
@@ -625,11 +627,11 @@
-opaque select_tag() :: atom().
-opaque select_ref() :: reference().
--record(select_info, {tag :: select_tag(), ref :: select_ref()}).
+%% -record(select_info, {tag :: select_tag(), ref :: select_ref()}).
--type select_info() :: #select_info{}.
+-type select_info() :: {select_info, select_tag(), select_ref()}.
--define(SELECT_INFO(T, R), #select_info{tag = T, ref = R}).
+-define(SELECT_INFO(T, R), {select_info, T, R}).
-define(SELECT(T, R), {select, ?SELECT_INFO(T, R)}).
@@ -1146,19 +1148,25 @@ open(Domain, Type, Protocol, Extra) when is_map(Extra) ->
%%
%% bind - bind a name to a socket
%%
+%% Note that Addr can only have the value of broadcast *if* Domain =:= inet!
+%%
-spec bind(Socket, Addr) -> ok | {error, Reason} when
Socket :: socket(),
- Addr :: any | loopback | sockaddr(),
+ Addr :: any | broadcast | loopback | sockaddr(),
Reason :: term().
bind(#socket{ref = SockRef}, Addr)
- when ((Addr =:= any) orelse (Addr =:= loopback)) ->
+ when ((Addr =:= any) orelse
+ (Addr =:= broadcast) orelse
+ (Addr =:= loopback)) ->
try which_domain(SockRef) of
inet ->
nif_bind(SockRef, ?SOCKADDR_IN4_DEFAULT(Addr));
- inet6 ->
- nif_bind(SockRef, ?SOCKADDR_IN6_DEFAULT(Addr))
+ inet6 when (Addr =:= any) orelse (Addr =:= loopback) ->
+ nif_bind(SockRef, ?SOCKADDR_IN6_DEFAULT(Addr));
+ _ ->
+ einval()
catch
%% <WIN32-TEMPORARY>
error:notsup:S ->
@@ -1301,7 +1309,7 @@ connect(#socket{ref = SockRef}, #{family := Fam} = SockAddr, Timeout)
((Timeout =:= nowait) orelse
(Timeout =:= infinity) orelse is_integer(Timeout)) ->
TS = timestamp(Timeout),
- case nif_connect(SockRef, SockAddr) of
+ case nif_connect(SockRef, ensure_sockaddr(SockAddr)) of
ok ->
%% Connected!
ok;
@@ -1559,7 +1567,7 @@ do_send(SockRef, Data, EFlags, Timeout) ->
ok | {error, Reason} when
Socket :: socket(),
Data :: binary(),
- Dest :: null | sockaddr(),
+ Dest :: sockaddr(),
Reason :: term().
sendto(Socket, Data, Dest) ->
@@ -1568,7 +1576,7 @@ sendto(Socket, Data, Dest) ->
-spec sendto(Socket, Data, Dest, Flags) -> ok | {error, Reason} when
Socket :: socket(),
Data :: binary(),
- Dest :: null | sockaddr(),
+ Dest :: sockaddr(),
Flags :: send_flags(),
Reason :: term()
; (Socket, Data, Dest, Timeout :: nowait) -> ok |
@@ -1576,13 +1584,13 @@ sendto(Socket, Data, Dest) ->
{error, Reason} when
Socket :: socket(),
Data :: iodata(),
- Dest :: null | sockaddr(),
+ Dest :: sockaddr(),
SelectInfo :: select_info(),
Reason :: term()
; (Socket, Data, Dest, Timeout) -> ok | {error, Reason} when
Socket :: socket(),
Data :: iodata(),
- Dest :: null | sockaddr(),
+ Dest :: sockaddr(),
Timeout :: timeout(),
Reason :: term().
@@ -1597,14 +1605,14 @@ sendto(Socket, Data, Dest, Timeout) ->
{error, Reason} when
Socket :: socket(),
Data :: binary(),
- Dest :: null | sockaddr(),
+ Dest :: sockaddr(),
Flags :: send_flags(),
SelectInfo :: select_info(),
Reason :: term()
; (Socket, Data, Dest, Flags, Timeout) -> ok | {error, Reason} when
Socket :: socket(),
Data :: binary(),
- Dest :: null | sockaddr(),
+ Dest :: sockaddr(),
Flags :: send_flags(),
Timeout :: timeout(),
Reason :: term().
@@ -1612,15 +1620,6 @@ sendto(Socket, Data, Dest, Timeout) ->
sendto(Socket, Data, Dest, Flags, Timeout) when is_list(Data) ->
Bin = erlang:list_to_binary(Data),
sendto(Socket, Bin, Dest, Flags, Timeout);
-sendto(#socket{ref = SockRef}, Data, Dest, Flags, Timeout)
- when is_binary(Data) andalso
- (Dest =:= null) andalso
- is_list(Flags) andalso
- ((Timeout =:= nowait) orelse
- (Timeout =:= infinity) orelse
- (is_integer(Timeout) andalso (Timeout > 0))) ->
- EFlags = enc_send_flags(Flags),
- do_sendto(SockRef, Data, Dest, EFlags, Timeout);
sendto(#socket{ref = SockRef}, Data, #{family := Fam} = Dest, Flags, Timeout)
when is_binary(Data) andalso
((Fam =:= inet) orelse (Fam =:= inet6) orelse (Fam =:= local)) andalso
@@ -1629,7 +1628,7 @@ sendto(#socket{ref = SockRef}, Data, #{family := Fam} = Dest, Flags, Timeout)
(Timeout =:= infinity) orelse
(is_integer(Timeout) andalso (Timeout > 0))) ->
EFlags = enc_send_flags(Flags),
- do_sendto(SockRef, Data, Dest, EFlags, Timeout).
+ do_sendto(SockRef, Data, ensure_sockaddr(Dest), EFlags, Timeout).
do_sendto(SockRef, Data, Dest, EFlags, Timeout) ->
TS = timestamp(Timeout),
@@ -1820,8 +1819,12 @@ do_sendmsg_rest([B|IOVec], Written) ->
ensure_msghdr(#{ctrl := []} = M) ->
ensure_msghdr(maps:remove(ctrl, M));
-ensure_msghdr(#{iov := IOV} = M) when is_list(IOV) andalso (IOV =/= []) ->
- M#{iov := erlang:iolist_to_iovec(IOV)};
+ensure_msghdr(#{iov := IOV, addr := Addr} = M)
+ when is_list(IOV) andalso (IOV =/= []) ->
+ M#{iov => erlang:iolist_to_iovec(IOV), addr => ensure_sockaddr(Addr)};
+ensure_msghdr(#{iov := IOV} = M)
+ when is_list(IOV) andalso (IOV =/= []) ->
+ M#{iov => erlang:iolist_to_iovec(IOV)};
ensure_msghdr(_) ->
einval().
@@ -2682,7 +2685,7 @@ peername(#socket{ref = SockRef}) ->
SelectInfo :: select_info(),
Reason :: term().
-cancel(#socket{ref = SockRef}, #select_info{tag = Tag, ref = Ref}) ->
+cancel(#socket{ref = SockRef}, ?SELECT_INFO(Tag, Ref)) ->
cancel(SockRef, Tag, Ref).
@@ -2693,17 +2696,17 @@ cancel(#socket{ref = SockRef}, #select_info{tag = Tag, ref = Ref}) ->
%%
%% ===========================================================================
--spec enc_domain(Domain) -> non_neg_integer() when
- Domain :: domain().
+%% -spec enc_domain(Domain) -> non_neg_integer() when
+%% Domain :: domain().
enc_domain(local) -> ?SOCKET_DOMAIN_LOCAL;
enc_domain(inet) -> ?SOCKET_DOMAIN_INET;
enc_domain(inet6) -> ?SOCKET_DOMAIN_INET6;
enc_domain(Domain) -> invalid_domain(Domain).
--spec enc_type(Domain, Type) -> non_neg_integer() when
- Domain :: domain(),
- Type :: type().
+%% -spec enc_type(Domain, Type) -> non_neg_integer() when
+%% Domain :: domain(),
+%% Type :: type().
%% What combos are valid?
enc_type(_, stream) -> ?SOCKET_TYPE_STREAM;
@@ -3373,10 +3376,12 @@ enc_sockopt_key(otp = L, Opt, _, _, _, _) ->
%% +++ SOCKET socket options +++
enc_sockopt_key(socket = _L, acceptconn = _Opt, get = _Dir, _D, _T, _P) ->
?SOCKET_OPT_SOCK_ACCEPTCONN;
+enc_sockopt_key(socket = L, acceptconn = Opt, Dir, _D, _T, _P) ->
+ not_supported({L, Opt, Dir});
enc_sockopt_key(socket = L, acceptfilter = Opt, _Dir, _D, _T, _P) ->
not_supported({L, Opt});
-%% Before linux 3.8, this socket option could be set.
-%% Maximum size of buffer for name: IFNAMSZIZ
+%% Before linux 3.8, this socket option could be set but not get.
+%% Maximum size of buffer for name: IFNAMSIZ
%% So, we let the implementation decide.
enc_sockopt_key(socket = _L, bindtodevice = _Opt, _Dir, _D, _T, _P) ->
?SOCKET_OPT_SOCK_BINDTODEVICE;
diff --git a/erts/test/erlc_SUITE.erl b/erts/test/erlc_SUITE.erl
index 0c5b9f8358..c01506b1cd 100644
--- a/erts/test/erlc_SUITE.erl
+++ b/erts/test/erlc_SUITE.erl
@@ -32,22 +32,34 @@
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
+ [{group,with_server},{group,without_server}].
+
+groups() ->
+ Tests = tests(),
+ [{with_server,[],Tests},
+ {without_server,[],Tests}].
+
+tests() ->
[compile_erl, compile_yecc, compile_script, compile_mib,
good_citizen, deep_cwd, arg_overflow, make_dep_options].
-groups() ->
- [].
-
init_per_suite(Config) ->
Config.
end_per_suite(_Config) ->
ok.
-init_per_group(_GroupName, Config) ->
+init_per_group(with_server, Config) ->
+ os:putenv("ERLC_USE_SERVER", "yes"),
+ Config;
+init_per_group(without_server, Config) ->
+ os:putenv("ERLC_USE_SERVER", "no"),
+ Config;
+init_per_group(_, Config) ->
Config.
end_per_group(_GroupName, Config) ->
+ os:unsetenv("ERLC_USE_SERVER"),
Config.
%% Copy from erlc_SUITE_data/include/erl_test.hrl.
@@ -199,8 +211,7 @@ deep_cwd(Config) when is_list(Config) ->
deep_cwd_1(PrivDir) ->
DeepDir0 = filename:join(PrivDir, lists:duplicate(128, $a)),
DeepDir = filename:join(DeepDir0, lists:duplicate(128, $b)),
- ok = file:make_dir(DeepDir0),
- ok = file:make_dir(DeepDir),
+ ok = filelib:ensure_dir(filename:join(DeepDir,"any_file")),
ok = file:set_cwd(DeepDir),
ok = file:write_file("test.erl", "-module(test).\n\n"),
io:format("~s\n", [os:cmd("erlc test.erl")]),
diff --git a/erts/test/otp_SUITE.erl b/erts/test/otp_SUITE.erl
index 2372e8b9ac..c5e0dfe649 100644
--- a/erts/test/otp_SUITE.erl
+++ b/erts/test/otp_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2018. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -79,8 +79,7 @@ undefined_functions(Config) when is_list(Config) ->
[UndefS,ExcludeFrom]),
{ok,Undef0} = xref:q(Server, lists:flatten(Q)),
Undef1 = hipe_filter(Undef0),
- Undef2 = ssl_crypto_filter(Undef1),
- Undef3 = edoc_filter(Undef2),
+ Undef3 = ssl_crypto_filter(Undef1),
Undef4 = eunit_filter(Undef3),
Undef5 = dialyzer_filter(Undef4),
Undef6 = wx_filter(Undef5),
@@ -157,12 +156,6 @@ ssl_crypto_filter(Undef) ->
{_,_} -> Undef
end.
-edoc_filter(Undef) ->
- %% Filter away function call that is catched.
- filter(fun({{edoc_lib,uri_get_http,1},{http,request_sync,2}}) -> false;
- (_) -> true
- end, Undef).
-
eunit_filter(Undef) ->
filter(fun({{eunit_test,wrapper_test_exported_,0},
{eunit_test,nonexisting_function,0}}) -> false;
diff --git a/erts/vsn.mk b/erts/vsn.mk
index f06fd08540..d1db2ceceb 100644
--- a/erts/vsn.mk
+++ b/erts/vsn.mk
@@ -18,7 +18,7 @@
# %CopyrightEnd%
#
-VSN = 10.4.4
+VSN = 10.5
# Port number 4365 in 4.2
# Port number 4366 in 4.3
diff --git a/lib/asn1/Makefile b/lib/asn1/Makefile
index 63cb770043..10a8795703 100644
--- a/lib/asn1/Makefile
+++ b/lib/asn1/Makefile
@@ -54,12 +54,7 @@ SPECIAL_TARGETS =
include $(ERL_TOP)/make/otp_subdir.mk
-.PHONY: info version
-
-info:
- @echo "APP_RELEASE_DIR: $(APP_RELEASE_DIR)"
- @echo "APP_DIR: $(APP_DIR)"
- @echo "APP_TAR_FILE: $(APP_TAR_FILE)"
+.PHONY: version
version:
@echo "$(VSN)"
diff --git a/lib/common_test/Makefile b/lib/common_test/Makefile
index 35739462c5..5ac76f0044 100644
--- a/lib/common_test/Makefile
+++ b/lib/common_test/Makefile
@@ -45,4 +45,7 @@ SPECIAL_TARGETS =
#
include $(ERL_TOP)/make/otp_subdir.mk
+DIA_PLT_APPS=compiler tools crypto runtime_tools syntax_tools ftp inets \
+ debugger sasl snmp ssh reltool observer xmerl
+
include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/common_test/doc/src/ct_netconfc.xml b/lib/common_test/doc/src/ct_netconfc.xml
index 8fbe5f3df6..f4d98b611b 100644
--- a/lib/common_test/doc/src/ct_netconfc.xml
+++ b/lib/common_test/doc/src/ct_netconfc.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2010</year><year>2017</year>
+ <year>2010</year><year>2019</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -37,51 +37,64 @@
<description>
- <p>NETCONF client module.</p>
-
- <p>The NETCONF client is compliant with RFC 4741 NETCONF Configuration
- Protocol and RFC 4742 Using the NETCONF Configuration Protocol over
- Secure SHell (SSH).</p>
+ <p>NETCONF client module compliant with RFC 6241, NETCONF Configuration
+ Protocol, and RFC 6242, Using the NETCONF Configuration Protocol over
+ Secure SHell (SSH), and with support for RFC 5277, NETCONF Event
+ Notifications.</p>
<marker id="Connecting"/>
<p><em>Connecting to a NETCONF server</em></p>
- <p>NETCONF sessions can either be opened by a single call
- to <seealso marker="#open-1"><c>open/1,2</c></seealso> or by a call
- to <seealso marker="#connect-1"><c>connect/1,2</c></seealso> followed
- by one or more calls to
- <seealso marker="#session-1"><c>session/1,2,3</c></seealso>.</p>
-
- <p>The properties of the sessions will be exactly the same, except
- that when
- using <seealso marker="#connect-1"><c>connect/1,2</c></seealso>, you
- may start multiple sessions over the same SSH connection. Each
- session is implemented as an SSH channel.</p>
-
- <p><seealso marker="#open-1"><c>open/1,2</c></seealso> will establish one
- SSH connection with one SSH channel implementing one NETCONF
- session. You may start mutiple sessions by
- calling <seealso marker="#open-1"><c>open/1,2</c></seealso> multiple
- times, but then a new SSH connection will be established for each
- session.</p>
-
- <p>For each server to test against, the following entry can be added to a
- configuration file:</p>
+ <p>Call <seealso marker="#connect-1"><c>connect/1,2</c></seealso>
+ to establish a connection to a server, then pass the returned
+ handle to <seealso marker="#session-1"><c>session/1-3</c></seealso> to
+ establish a NETCONF session on a new SSH channel.
+ Each call to
+ <seealso marker="#session-1"><c>session/1-3</c></seealso> establishes a
+ new session on the same connection, and results in a hello message
+ to the server.</p>
+
+ <p>Alternately,
+ <seealso marker="#open-1"><c>open/1,2</c></seealso> can be used to
+ establish a single session on a dedicated connection.
+ (Or, equivalently,
+ <seealso marker="#only_open-1"><c>only_open/1,2</c></seealso>
+ followed by <seealso marker="#hello-1"><c>hello/1-3</c></seealso>.)</p>
+
+ <p>Connect/session options can be specified in a configuration
+ file with entries like the following.</p>
<pre>
- {server_id(),options()}.</pre>
+ {server_id(), [option()]}.</pre>
<p>The <seealso marker="#type-server_id"><c>server_id()</c></seealso>
- or an associated
- <seealso marker="ct#type-target_name"><c>ct:target_name()</c></seealso>
- must then be used in calls to
- <seealso marker="#connect-2"><c>connect/2</c></seealso>
- or <seealso marker="#open-2"><c>open/2</c></seealso>.</p>
-
- <p>If no configuration exists for a server,
- use <seealso marker="#connect-1"><c>connect/1</c></seealso>
- or <seealso marker="#open-1"><c>open/1</c></seealso> instead,
- and specify all necessary options in the <c>Options</c> parameter.</p>
+ or an associated
+ <seealso marker="ct#type-target_name"><c>ct:target_name()</c></seealso>
+ can then be passed to the aforementioned functions to use the
+ referenced configuration.</p>
+
+ <marker id="Signaling"/>
+ <p><em>Signaling</em></p>
+
+ <p>Protocol operations in the NETCONF protocol are realized as remote
+ procedure calls (RPCs) from client to server and a corresponding
+ reply from server to client.
+ RPCs are sent using like-named functions (eg.
+ <seealso marker="#edit_config-3"><c>edit_config/3-5</c></seealso>
+ to send an edit-config RPC), with the server reply
+ as return value.
+ There are functions for each RPC defined in RFC 6241 and
+ the create-subscription RPC from RFC 5277, all of which are
+ wrappers on <seealso marker="#send_rpc-2"><c>send_rpc/2,3</c></seealso>,
+ that can be used to send an arbitrary RPC
+ not defined in RFC 6241 or RFC 5277.</p>
+
+ <p>All of the signaling functions have one variant with a
+ <c>Timeout</c> argument and one without, corresponding to an
+ infinite timeout.
+ The latter is inappropriate in most cases since a non-response by
+ the server or a missing message-id causes the call to hang
+ indefinitely.</p>
<marker id="Logging"/>
<p><em>Logging</em></p>
@@ -93,7 +106,7 @@
<pre>
suite() -&gt;
- [{ct_hooks, [{cth_conn_log, [{<seealso marker="ct#type-conn_log_mod"><c>ct:conn_log_mod()</c></seealso>,<seealso marker="ct#type-conn_log_options"><c>ct:conn_log_options()</c></seealso>}]}]}].</pre>
+ [{ct_hooks, [{cth_conn_log, [{<seealso marker="ct#type-conn_log_mod"><c>ct:conn_log_mod()</c></seealso>, <seealso marker="ct#type-conn_log_options"><c>ct:conn_log_options()</c></seealso>}]}]}].</pre>
<p><c>conn_log_mod()</c> is the name of the <c>Common Test</c> module
implementing the connection protocol, for example, <c>ct_netconfc</c>.</p>
@@ -133,7 +146,7 @@
configuration variable <c>ct_conn_log</c>:</p>
<pre>
- {ct_conn_log,[{<seealso marker="ct#type-conn_log_mod"><c>ct:conn_log_mod()</c></seealso>,<seealso marker="ct#type-conn_log_options"><c>ct:conn_log_options()</c></seealso>}]}.</pre>
+ {ct_conn_log,[{<seealso marker="ct#type-conn_log_mod"><c>ct:conn_log_mod()</c></seealso>, <seealso marker="ct#type-conn_log_options"><c>ct:conn_log_options()</c></seealso>}]}.</pre>
<p>For example:</p>
@@ -185,100 +198,111 @@
would cause HTML logging of all NETCONF connections in to the test
case HTML log.</p>
- <marker id="Notifications"/>
- <p><em>Notifications</em></p>
-
- <p>The NETCONF client is also compliant with RFC 5277 NETCONF Event
- Notifications, which defines a mechanism for an asynchronous message
- notification delivery service for the NETCONF protocol.</p>
-
- <p>Specific functions to support this are
- <seealso marker="#create_subscription-1"><c>create_subscription/1-6</c></seealso>
- and
- <seealso marker="#get_event_streams-1"><c>get_event_streams/1-3</c></seealso>.</p>
-
- <marker id="Default_timeout"/>
- <p><em>Default Timeout</em></p>
-
- <p>Most of the functions in this module have one variant with
- a <c>Timeout</c> parameter, and one without. If nothing else is
- specified, the default value <c>infinity</c> is used when
- the <c>Timeout</c> parameter is not given.</p>
-
</description>
+ <!-- ====================================================================== -->
+
<datatypes>
<datatype>
<name name="client"/>
- </datatype>
- <datatype>
- <name name="error_reason"/>
- </datatype>
- <datatype>
- <name name="event_time"/>
+ <desc>
+ <p>Handle to a NETCONF session, as required by signaling
+ functions.</p>
+ </desc>
</datatype>
<datatype>
<name name="handle"/>
<desc>
- <p>Opaque reference for a connection to a NETCONF server or a
- NETCONF session.</p>
+ <p>Handle to a connection to a NETCONF server as
+ returned by
+ <seealso marker="#connect-1"><c>connect/1,2</c></seealso>,
+ or to a session as returned by
+ <seealso marker="#session-1"><c>session/1-3</c></seealso>,
+ <seealso marker="#open-1"><c>open/1,2</c></seealso>,
+ or <seealso marker="#only_open-1"><c>only_open/1,2</c></seealso>.</p>
</desc>
</datatype>
<datatype>
- <name name="host"/>
- </datatype>
- <datatype>
- <name name="netconf_db"/>
+ <name name="xs_datetime"/>
+ <desc>
+ <p>Date and time of a startTime/stopTime element in an RFC
+ 5277 create-subscription request. Of XML primitive type
+ <c>dateTime</c>, which has the (informal) form</p>
+ <pre>
+ [-]YYYY-MM-DDThh:mm:ss[.s][Z|(+|-)hh:mm]</pre>
+ <p>where <c>T</c> and <c>Z</c> are literal and <c>.s</c> is
+ one or more fractional seconds.</p>
+ </desc>
</datatype>
<datatype>
- <name name="notification"/>
+ <name name="event_time"/>
</datatype>
<datatype>
<name name="notification_content"/>
</datatype>
<datatype>
- <name name="option"/>
+ <name name="notification"/>
<desc>
- <p><c>SshConnectOption</c> is any valid option to
- <seealso marker="ssh:ssh#connect-3"><c>ssh:connect/3,4</c></seealso>.
- Common options used are <c>user</c>, <c>password</c>
- and <c>user_dir</c>. The <c>SshConnectOptions</c> are
- verfied by the SSH application.</p>
+ <p>Event notification messages sent as a result of calls to
+ <seealso marker="#create_subscription-2"><c>create_subscription/2,3</c></seealso>.</p>
</desc>
</datatype>
<datatype>
- <name name="options"/>
+ <name name="option"/>
<desc>
- <p>Options used for setting up an SSH connection to a NETCONF
- server.</p>
+ <p>Options <c>host</c> and <c>port</c> specify the
+ server endpoint to which to connect, and are passed directly
+ to <seealso
+ marker="ssh:ssh#connect-3"><c>ssh:connect/4</c></seealso>,
+ as are arbitrary ssh options. Common options are <c>user</c>,
+ <c>password</c> and <c>user_dir</c>.</p>
+
+ <p>Option <c>timeout</c> specifies the number of
+ milliseconds to allow for connection establishment and, if the
+ function in question results in an outgoing hello message,
+ reception of the server hello. The timeout applies to
+ connection and hello independently;
+ one timeout for connection establishment, another for hello
+ reception.</p>
+
+ <p>Option <c>capability</c> specifies the content of a
+ corresponding element in an outgoing hello message, each
+ option specifying the content of a single element.
+ If no base NETCONF capability is configured then the RFC 4741
+ 1.0 capability, "urn:ietf:params:netconf:base:1.0", is added,
+ otherwise not.
+ In particular, the RFC 6241 1.1 capability must be explicitly
+ configured.
+ NETCONF capabilities can be specified using the shorthand notation
+ defined in RFC 6241, any capability string starting with a
+ colon being prefixed by either "urn:ietf:params:netconf" or
+ "urn:ietf:params:netconf:capability", as appropriate.</p>
+
+ <p>Capability options are ignored by connect/1-3 and only_open/1-2,
+ which don't result in an outgoing hello message.</p>
</desc>
</datatype>
<datatype>
<name name="server_id"/>
<desc>
- <p>The identity of a server, specified in a configuration
- file.</p>
- </desc>
- </datatype>
- <datatype>
- <name name="simple_xml"/>
- <desc>
- <p>This type is further described in application
- <seealso marker="xmerl:index"><c>xmerl</c></seealso>.</p>
+ <p>Identity of connection or session configuration in a
+ configuration file.</p>
</desc>
</datatype>
<datatype>
<name name="stream_data"/>
- <desc>
- <p>For details about the data format for the string values, see
- "XML Schema for Event Notifications" in RFC 5277.</p>
- </desc>
</datatype>
<datatype>
<name name="stream_name"/>
</datatype>
<datatype>
<name name="streams"/>
+ <desc>
+ <p>Stream information as returned by
+ <seealso marker="#get_event_streams-1"><c>get_event_streams/1-3</c></seealso>.
+ See RFC 5277, "XML Schema for Event Notifications", for detail
+ on the format of the string values.</p>
+ </desc>
</datatype>
<datatype>
<name name="xml_attribute_tag"/>
@@ -296,20 +320,28 @@
<name name="xml_tag"/>
</datatype>
<datatype>
+ <name name="simple_xml"/>
+ <desc>
+ <p>Representation of XML, as described in application
+ <seealso marker="xmerl:index"><c>xmerl</c></seealso>.</p>
+ </desc>
+ </datatype>
+ <datatype>
<name name="xpath"/>
</datatype>
<datatype>
- <name name="xs_datetime"/>
- <desc>
- <p>This date and time identifier has the same format as the XML type
- <c>dateTime</c> and is compliant with RFC 3339 Date and Time on
- the Internet Timestamps. The format is as follows:</p>
- <pre>
- [-]CCYY-MM-DDThh:mm:ss[.s][Z|(+|-)hh:mm]</pre>
- </desc>
+ <name name="error_reason"/>
+ </datatype>
+ <datatype>
+ <name name="host"/>
+ </datatype>
+ <datatype>
+ <name name="netconf_db"/>
</datatype>
</datatypes>
+ <!-- ====================================================================== -->
+
<funcs>
<func>
<name name="action" arity="2" since="OTP R15B02"/>
@@ -352,11 +384,7 @@
reference returned from this
function is required as connection identifier when opening
sessions over this connection, see
- <seealso marker="#session-1"><c>session/1,2,3</c></seealso>.</p>
-
- <p>Option <c>timeout</c> (milliseconds) is used when setting up the
- SSH connection. It is not used for any other purposes during the
- lifetime of the connection.</p>
+ <seealso marker="#session-1"><c>session/1-3</c></seealso>.</p>
</desc>
</func>
@@ -371,10 +399,9 @@
<c>target_name()</c> associated with such an Id, then the options
for this server are fetched from the configuration file.</p>
- <p>Argument <c><anno>ExtraOptions</anno></c> is added to the
- options found in the configuration file. If the same options
- are specified, the values from the configuration file
- overwrite <c><anno>ExtraOptions</anno></c>.</p>
+ <p>The options list is added to those of the
+ configuration file. If an option is specified in both lists,
+ the configuration file takes precedence.</p>
<p>If the server is not specified in a configuration file, use
<seealso marker="#connect-1"><c>connect/1</c></seealso>
@@ -384,17 +411,13 @@
reference returned from this
function can be used as connection identifier when opening
sessions over this connection, see
- <seealso marker="#session-1"><c>session/1,2,3</c></seealso>.
+ <seealso marker="#session-1"><c>session/1-3</c></seealso>.
However, if <c><anno>KeyOrName</anno></c> is a
<c>target_name()</c>, that is, if the server is named through a
call to <seealso marker="ct#require-2"><c>ct:require/2</c></seealso>
or a <c>require</c> statement in the test suite, then this name can
be used instead of
<seealso marker="#type-handle"><c>handle()</c></seealso>.</p>
-
- <p>Option <c>timeout</c> (milliseconds) is used when setting up the
- SSH connection. It is not used for any other purposes during the
- lifetime of the connection.</p>
</desc>
</func>
@@ -412,80 +435,61 @@
</func>
<func>
- <name since="OTP R15B02">create_subscription(Client) -> Result</name>
- <name since="OTP R15B02">create_subscription(Client, Stream) -> Result</name>
- <name since="OTP R15B02">create_subscription(Client, Stream, Filter) -> Result</name>
- <name since="OTP R15B02">create_subscription(Client, Stream, Filter, Timeout) -> Result</name>
- <name name="create_subscription" arity="5" clause_i="2" since="OTP R15B02"/>
- <name name="create_subscription" arity="6" since="OTP R15B02"/>
+ <name name="create_subscription" arity="2" clause_i="1" since="OTP 22.1"/>
+ <name name="create_subscription" arity="3" clause_i="1" since="OTP 22.1"/>
<fsummary>Creates a subscription for event notifications.</fsummary>
<desc>
- <p>Creates a subscription for event notifications.</p>
-
- <p>This function sets up a subscription for NETCONF event
- notifications of the specified stream type, matching the specified
- filter. The calling process receives notifications as messages of
- type <seealso marker="#type-notification"><c>notification()</c></seealso>.</p>
-
- <p>Only a subset of the function clauses are show above. The
- full set of valid combinations of input parameters is as
- follows:</p>
-
-<pre>create_subscription(Client)
+ <p>Creates a subscription for event notifications by sending
+ an RFC 5277 create-subscription RPC to the server.
+ The calling process receives events as messages of
+ type <seealso marker="#type-notification"><c>notification()</c></seealso>.</p>
-create_subscription(Client, Timeout)
-create_subscription(Client, Stream)
-create_subscription(Client, Filter)
-
-create_subscription(Client, Stream, Timeout)
-create_subscription(Client, Filter, Timeout)
-create_subscription(Client, Stream, Filter)
-create_subscription(Client, StartTime, StopTime)
-
-create_subscription(Client, Stream, Filter, Timeout)
-create_subscription(Client, StartTime, StopTime, Timeout)
-create_subscription(Client, Stream, StartTime, StopTime)
-create_subscription(Client, Filter, StartTime, StopTime)
-
-create_subscription(Client, Stream, StartTime, StopTime, Timeout)
-create_subscription(Client, Stream, Filter, StartTime, StopTime)
-create_subscription(Client, Stream, Filter, StartTime, StopTime, Timeout)</pre>
+ <p>From RFC 5722, 2.1 Subscribing to Receive Event Notifications:</p>
<taglist>
<tag><c><anno>Stream</anno></c></tag>
- <item><p>Optional parameter that indicates which stream of event
+ <item><p>Indicates which stream of event
is of interest. If not present, events in the default NETCONF
stream are sent.</p></item>
<tag><c><anno>Filter</anno></c></tag>
- <item><p>Optional parameter that indicates which subset of all
+ <item><p>Indicates which subset of all
possible events is of interest. The parameter format is the
same as that of the filter parameter in the NETCONF protocol
operations. If not present, all events not precluded by other
parameters are sent.</p></item>
<tag><c><anno>StartTime</anno></c></tag>
- <item><p>Optional parameter used to trigger the replay feature and
+ <item><p>Used to trigger the replay feature and
indicate that the replay is to start at the time specified.
If <c><anno>StartTime</anno></c> is not present, this is not a
- replay subscription.</p>
- <p>It is not valid to specify start times that are later than
+ replay subscription.
+ It is not valid to specify start times that are later than
the current time. If <c><anno>StartTime</anno></c> is specified
earlier than the log can support, the replay begins with the
- earliest available notification.</p>
- <p>This parameter is of type <c>dateTime</c> and compliant to
+ earliest available notification.
+ This parameter is of type <c>dateTime</c> and compliant to
RFC 3339. Implementations must support time zones.</p></item>
<tag><c><anno>StopTime</anno></c></tag>
- <item><p>Optional parameter used with the optional replay feature
+ <item><p>Used with the optional replay feature
to indicate the newest notifications of interest. If
<c><anno>StopTime</anno></c> is not present, the notifications
- continues until the subscription is terminated.</p>
- <p>Must be used with and be later than <c>StartTime</c>. Values
+ continues until the subscription is terminated.
+ Must be used with and be later than <c>StartTime</c>. Values
of <c><anno>StopTime</anno></c> in the future are valid. This
parameter is of type <c>dateTime</c> and compliant to RFC 3339.
Implementations must support time zones.</p></item>
</taglist>
- <p>For more details about the event notification mechanism, see
- RFC 5277.</p>
+ <p>See RFC 5277 for more details. The requirement that
+ <c>StopTime</c> must only be used with <c>StartTime</c> is not
+ enforced, to allow an invalid request to be sent to the
+ server.</p>
+
+ <p>Prior to OTP 22.1, this function was documented as having
+ 15 variants in 6 arities. These are still exported for
+ backwards compatibility, but no longer documented.
+ The map-based variants documented above provide the same
+ functionality with simpler arguments.</p>
+
</desc>
</func>
@@ -561,23 +565,7 @@ create_subscription(Client, Stream, Filter, StartTime, StopTime, Timeout)</pre>
<name name="get_capabilities" arity="2" since="OTP R15B02"/>
<fsummary>Returns the server side capabilities.</fsummary>
<desc>
- <p>Returns the server side capabilities.</p>
-
- <p>The following capability identifiers, defined in RFC 4741 NETCONF
- Configuration Protocol, can be returned:</p>
-
- <list>
- <item><p><c>"urn:ietf:params:netconf:base:1.0"</c></p></item>
- <item><p><c>"urn:ietf:params:netconf:capability:writable-running:1.0"</c></p></item>
- <item><p><c>"urn:ietf:params:netconf:capability:candidate:1.0"</c></p></item>
- <item><p><c>"urn:ietf:params:netconf:capability:confirmed-commit:1.0"</c></p></item>
- <item><p><c>"urn:ietf:params:netconf:capability:rollback-on-error:1.0"</c></p></item>
- <item><p><c>"urn:ietf:params:netconf:capability:startup:1.0"</c></p></item>
- <item><p><c>"urn:ietf:params:netconf:capability:url:1.0"</c></p></item>
- <item><p><c>"urn:ietf:params:netconf:capability:xpath:1.0"</c></p></item>
- </list>
-
- <p>More identifiers can exist, for example, server-side namespace.</p>
+ <p>Returns the server capabilities as received in its hello message.</p>
</desc>
</func>
@@ -652,10 +640,12 @@ create_subscription(Client, Stream, Filter, StartTime, StopTime, Timeout)</pre>
<name name="hello" arity="3" since="OTP 17.5.3"/>
<fsummary>Exchanges hello messages with the server.</fsummary>
<desc>
- <p>Exchanges <c>hello</c> messages with the server.</p>
+ <p>Exchanges <c>hello</c> messages with the server. Returns
+ when the server hello has been received or after the
+ specified timeout.</p>
- <p>Adds optional capabilities and sends a <c>hello</c> message to the
- server and waits for the return.</p>
+ <p>Note that capabilities for an outgoing hello can be passed
+ directly to <seealso marker="#open-2"><c>open/2</c></seealso>.</p>
</desc>
</func>
@@ -740,11 +730,6 @@ create_subscription(Client, Stream, Filter, StartTime, StopTime, Timeout)</pre>
reference returned from this
function is required as client identifier when calling any other
function in this module.</p>
-
- <p>Option <c>timeout</c> (milliseconds) is used when setting up the
- SSH connection and when waiting for the <c>hello</c> message from
- the server. It is not used for any other purposes during the
- lifetime of the connection.</p>
</desc>
</func>
@@ -761,10 +746,9 @@ create_subscription(Client, Stream, Filter, StartTime, StopTime, Timeout)</pre>
<c>target_name()</c> associated with such an Id, then the options
for this server are fetched from the configuration file.</p>
- <p>Argument <c><anno>ExtraOptions</anno></c> is added to the
- options found in the configuration file. If the same
- options are specified, the values from the configuration
- file overwrite <c><anno>ExtraOptions</anno></c>.</p>
+ <p>The options list is added to those of the
+ configuration file. If an option is specified in both lists,
+ the configuration file take precedence.</p>
<p>If the server is not specified in a configuration file, use
<seealso marker="#open-1"><c>open/1</c></seealso>
@@ -780,11 +764,6 @@ create_subscription(Client, Stream, Filter, StartTime, StopTime, Timeout)</pre>
be used instead of
<seealso marker="#type-handle"><c>handle()</c></seealso>.</p>
- <p>Option <c>timeout</c> (milliseconds) is used when setting up the
- SSH connection and when waiting for the <c>hello</c> message from
- the server. It is not used for any other purposes during the
- lifetime of the connection.</p>
-
<p>See also
<seealso marker="ct#require-2"><c>ct:require/2</c></seealso>.</p>
</desc>
@@ -827,7 +806,6 @@ create_subscription(Client, Stream, Filter, StartTime, StopTime, Timeout)</pre>
<fsummary>Opens a NETCONF session as a channel on the given SSH
connection, and exchanges hello messages with the
server.</fsummary>
- <type name="session_options"/>
<type name="session_option"/>
<desc>
<p>Opens a NETCONF session as a channel on the given SSH
diff --git a/lib/common_test/doc/src/notes.xml b/lib/common_test/doc/src/notes.xml
index c454608bbe..39388fd5ed 100644
--- a/lib/common_test/doc/src/notes.xml
+++ b/lib/common_test/doc/src/notes.xml
@@ -33,6 +33,64 @@
<file>notes.xml</file>
</header>
+<section><title>Common_Test 1.18</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ If a ct hook is installed in the <c>suite/0</c> function
+ in a test suite, then the hook's <c>terminate/1</c>
+ function would be called several times without it's
+ <c>init/2</c> function being called first. This is now
+ corrected.</p>
+ <p>
+ Own Id: OTP-15863 Aux Id: ERIERL-370 </p>
+ </item>
+ <item>
+ <p>
+ If <c>init_per_testcase</c> fails, the test itself is
+ skipped. According to the documentation, it should be
+ possible to change the result to failed in a hook
+ function. The only available hook function in this case
+ is <c>post_init_per_testcase</c>, but changing the return
+ value there did not affect the test case result. This is
+ now corrected.</p>
+ <p>
+ Own Id: OTP-15869 Aux Id: ERIERL-350 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Add ct_netconfc support for NETCONF 1.1 (RFC 6241). The
+ 1.1 base capability can be sent in hello, and RFC 6242
+ chunk framing is applied when both client and server
+ advertise 1.1 support.</p>
+ <p>
+ Own Id: OTP-15789</p>
+ </item>
+ <item>
+ <p>
+ Correct lib_dir paths in common_tests opaque data
+ structure that is passed to ct_release_test callback
+ modules in functions upgrade_init/2, upgrade_upgraded/2
+ and upgrade_downgraded/2. The incorrect paths may cause
+ confusion when debugging although it will not cause any
+ incorrect behavior on the part of common_test as it is
+ currently not used.</p>
+ <p>
+ Own Id: OTP-15934</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Common_Test 1.17.3</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/common_test/src/Makefile b/lib/common_test/src/Makefile
index 80eaed70bd..76689dab8c 100644
--- a/lib/common_test/src/Makefile
+++ b/lib/common_test/src/Makefile
@@ -62,9 +62,6 @@ MODULES= \
ct_repeat \
ct_telnet_client \
ct_make \
- vts \
- ct_webtool \
- ct_webtool_sup \
unix_telnet \
ct_config \
ct_config_plain \
diff --git a/lib/common_test/src/common_test.app.src b/lib/common_test/src/common_test.app.src
index efebea896c..271bb2335b 100644
--- a/lib/common_test/src/common_test.app.src
+++ b/lib/common_test/src/common_test.app.src
@@ -49,10 +49,7 @@
ct_telnet,
ct_testspec,
ct_util,
- ct_webtool,
- ct_webtool_sup,
unix_telnet,
- vts,
ct_config,
ct_config_plain,
ct_config_xml,
@@ -72,7 +69,6 @@
ct_util_server,
ct_config_server,
ct_make_ref,
- vts,
ct_master,
ct_master_logs,
test_server_ctrl,
diff --git a/lib/common_test/src/ct_event.erl b/lib/common_test/src/ct_event.erl
index 3689c6bc45..7c6ba28180 100644
--- a/lib/common_test/src/ct_event.erl
+++ b/lib/common_test/src/ct_event.erl
@@ -221,17 +221,7 @@ handle_event(Event,State=#state{receivers=RecvPids}) ->
%% report to master
report_event({master,Master},E=#event{name=_Name,node=_Node,data=_Data}) ->
- ct_master:status(Master,E);
-
-%% report to VTS
-report_event({vts,VTS},#event{name=Name,node=_Node,data=Data}) ->
- if Name == start_info ;
- Name == test_stats ;
- Name == test_done ->
- vts:test_info(VTS,Name,Data);
- true ->
- ok
- end.
+ ct_master:status(Master,E).
%%--------------------------------------------------------------------
diff --git a/lib/common_test/src/ct_framework.erl b/lib/common_test/src/ct_framework.erl
index bce6420042..367b5f5fdc 100644
--- a/lib/common_test/src/ct_framework.erl
+++ b/lib/common_test/src/ct_framework.erl
@@ -1550,8 +1550,7 @@ report(What,Data) ->
end;
_ ->
ok
- end,
- catch vts:report(What,Data).
+ end.
add_to_stats(Result) ->
Update = fun({Ok,Failed,Skipped={UserSkipped,AutoSkipped}}) ->
diff --git a/lib/common_test/src/ct_netconfc.erl b/lib/common_test/src/ct_netconfc.erl
index 6a758c4ea3..7ad6fa46e8 100644
--- a/lib/common_test/src/ct_netconfc.erl
+++ b/lib/common_test/src/ct_netconfc.erl
@@ -1,7 +1,7 @@
%%----------------------------------------------------------------------
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2012-2017. All Rights Reserved.
+%% Copyright Ericsson AB 2012-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -26,7 +26,7 @@
%% Netconf servers can be configured by adding the following statement
%% to a configuration file:
%%
-%% {server_id(),options()}.
+%% {server_id(), [option()]}.
%%
%% The server_id() or an associated ct:target_name() shall then be
%% used in calls to open/2 connect/2.
@@ -55,13 +55,14 @@
%% The netconf client is also compliant with RFC5277 NETCONF Event
%% Notifications, which defines a mechanism for an asynchronous
%% message notification delivery service for the netconf protocol.
-%%
-%% Specific functions to support this are create_subscription/6
-%% get_event_streams/3. (The functions also exist with other arities.)
+%% Functions supporting this are create_subscription/3
+%% get_event_streams/3.
%%
%%----------------------------------------------------------------------
-module(ct_netconfc).
+-dialyzer(no_improper_lists).
+
-include("ct_netconfc.hrl").
-include("ct_util.hrl").
-include_lib("xmerl/include/xmerl.hrl").
@@ -107,12 +108,8 @@
copy_config/4,
action/2,
action/3,
- create_subscription/1,
create_subscription/2,
create_subscription/3,
- create_subscription/4,
- create_subscription/5,
- create_subscription/6,
get_event_streams/1,
get_event_streams/2,
get_event_streams/3,
@@ -121,6 +118,12 @@
get_session_id/1,
get_session_id/2]).
+%% historic, no longer documented
+-export([create_subscription/1,
+ create_subscription/4,
+ create_subscription/5,
+ create_subscription/6]).
+
%%----------------------------------------------------------------------
%% Exported types
%%----------------------------------------------------------------------
@@ -163,6 +166,9 @@
(is_atom(Xml) orelse (is_tuple(Xml) andalso is_atom(element(1,Xml))))).
-define(is_string(S), (is_list(S) andalso is_integer(hd(S)))).
+%% Keys into the process dictionary.
+-define(KEY(T), {?MODULE, T}).
+
%%----------------------------------------------------------------------
%% Records
%%----------------------------------------------------------------------
@@ -173,9 +179,9 @@
capabilities,
session_id,
msg_id = 1,
- hello_status,
- no_end_tag_buff = <<>>,
- buff = <<>>,
+ hello_status, % undefined | received | #pending{}
+ % string() | {error, Reason}
+ buf = false, % binary() | list() | boolean()
pending = [], % [#pending]
event_receiver}).% pid
@@ -195,10 +201,9 @@
type}).
%% Pending replies from server
--record(pending, {tref, % timer ref (returned from timer:xxx)
- ref, % pending ref
+-record(pending, {tref :: false | reference(), % timer reference
msg_id,
- op,
+ op,
caller}).% pid which sent the request
%%----------------------------------------------------------------------
@@ -207,13 +212,14 @@
-type client() :: handle() | server_id() | ct:target_name().
-opaque handle() :: pid().
--type options() :: [option()].
--type option() :: {ssh,host()} | {port,inet:port_number()} | {user,string()} |
- {password,string()} | {user_dir,string()} |
- {timeout,timeout()}.
+-type option() :: {host | ssh, host()}
+ | {port, inet:port_number()}
+ | {timeout, timeout()}
+ | {capability, string() | [string()]}
+ | ssh:client_option().
--type session_options() :: [session_option()].
--type session_option() :: {timeout,timeout()}.
+-type session_option() :: {timeout,timeout()}
+ | {capability, string() | [string()]}.
-type host() :: inet:hostname() | inet:ip_address().
@@ -258,33 +264,44 @@
%% Open an SSH connection to a Netconf server
%% If the server options are specified in a configuration file, use
%% open/2.
+
+%% connect/1
+
-spec connect(Options) -> Result when
- Options :: options(),
- Result :: {ok,handle()} | {error,error_reason()}.
+ Options :: [option()],
+ Result :: {ok, handle()} | {error, error_reason()}.
connect(Options) ->
- do_connect(Options, #options{type=connection},[]).
+ connect(Options, #options{type = connection}, []).
+
+%% connect/2
--spec connect(KeyOrName,ExtraOptions) -> Result when
+-spec connect(KeyOrName, ExtraOptions) -> Result when
KeyOrName :: ct:key_or_name(),
- ExtraOptions :: options(),
- Result :: {ok,handle()} | {error,error_reason()}.
+ ExtraOptions :: [option()],
+ Result :: {ok, handle()} | {error, error_reason()}.
+
connect(KeyOrName, ExtraOptions) ->
- SortedExtra = lists:keysort(1,ExtraOptions),
- SortedConfig = lists:keysort(1,ct:get_config(KeyOrName,[])),
- AllOpts = lists:ukeymerge(1,SortedConfig,SortedExtra),
- do_connect(AllOpts,#options{name=KeyOrName,type=connection},[{name,KeyOrName}]).
-
-do_connect(OptList,InitOptRec,NameOpt) ->
- case check_options(OptList,InitOptRec) of
- {Host,Port,Options} ->
- ct_gen_conn:start({Host,Port},Options,?MODULE,
- NameOpt ++ [{reconnect,false},
- {use_existing_connection,false},
- {forward_messages,false}]);
- Error ->
- Error
+ connect(make_opts(KeyOrName, ExtraOptions),
+ #options{name = KeyOrName, type = connection},
+ [{name, KeyOrName}]).
+
+%% connect/3
+
+connect(Opts, InitRec, NameOpt) ->
+ case make_options(Opts, InitRec) of
+ #options{} = Rec ->
+ start(Rec, NameOpt, false);
+ {error, _} = No ->
+ No
end.
+%% make_opts/2
+
+make_opts(KeyOrName, ExtraOptions) ->
+ SortedExtra = lists:keysort(1, ExtraOptions),
+ SortedConfig = lists:keysort(1, ct:get_config(KeyOrName, [])),
+ lists:ukeymerge(1, SortedConfig, SortedExtra).
+
%%----------------------------------------------------------------------
%% Close the given SSH connection.
-spec disconnect(Conn) -> ok | {error,error_reason()} when
@@ -300,146 +317,185 @@ disconnect(Conn) ->
%%----------------------------------------------------------------------
%% Open a netconf session as a channel on the given SSH connection,
%% and exchange `hello' messages.
+
+%% session/1
+
-spec session(Conn) -> Result when
Conn :: handle(),
- Result :: {ok,handle()} | {error,error_reason()}.
+ Result :: {ok, handle()} | {error, error_reason()}.
+
session(Conn) ->
- do_session(Conn,[],#options{type=channel},[]).
+ session(Conn, [], #options{type = channel}, []).
--spec session(Conn,Options) -> Result when
+%% session/2
+
+-spec session(Conn, Options) -> Result when
Conn :: handle(),
- Options :: session_options(),
- Result :: {ok,handle()} | {error,error_reason()};
- (KeyOrName,Conn) -> Result when
+ Options :: [session_option()],
+ Result :: {ok, handle()} | {error, error_reason()};
+ (KeyOrName, Conn) -> Result when
KeyOrName :: ct:key_or_name(),
Conn :: handle(),
- Result :: {ok,handle()} | {error,error_reason()}.
-session(Conn,Options) when is_list(Options) ->
- do_session(Conn,Options,#options{type=channel},[]);
-session(KeyOrName,Conn) ->
- do_session(Conn,[],#options{name=KeyOrName,type=channel},[{name,KeyOrName}]).
+ Result :: {ok, handle()} | {error, error_reason()}.
+
+session(Conn, Options) when is_list(Options) ->
+ session(Conn, Options, #options{type = channel}, []);
--spec session(KeyOrName,Conn,Options) -> Result when
+session(KeyOrName, Conn) ->
+ session(Conn,
+ [],
+ #options{name = KeyOrName, type = channel},
+ [{name, KeyOrName}]).
+
+%% session/3
+
+-spec session(KeyOrName, Conn, Options) -> Result when
Conn :: handle(),
- Options :: session_options(),
+ Options :: [session_option()],
KeyOrName :: ct:key_or_name(),
- Result :: {ok,handle()} | {error,error_reason()}.
-session(KeyOrName,Conn,ExtraOptions) ->
- SortedExtra = lists:keysort(1,ExtraOptions),
- SortedConfig = lists:keysort(1,ct:get_config(KeyOrName,[])),
- AllOpts = lists:ukeymerge(1,SortedConfig,SortedExtra),
- do_session(Conn,AllOpts,#options{name=KeyOrName,type=channel},
- [{name,KeyOrName}]).
-
-do_session(Conn,OptList,InitOptRec,NameOpt) ->
- case call(Conn,get_ssh_connection) of
- {ok,SshConn} ->
- case check_session_options(OptList,InitOptRec) of
- {ok,Options} ->
- case ct_gen_conn:start(SshConn,Options,?MODULE,
- NameOpt ++
- [{reconnect,false},
- {use_existing_connection,false},
- {forward_messages,true}]) of
- {ok,Client} ->
- case hello(Client,Options#options.timeout) of
- ok ->
- {ok,Client};
- Error ->
- Error
- end;
- Error ->
- Error
- end;
- Error ->
- Error
- end;
- Error ->
- Error
+ Result :: {ok, handle()} | {error, error_reason()}.
+
+session(KeyOrName, Conn, ExtraOptions) ->
+ session(Conn,
+ make_opts(KeyOrName, ExtraOptions),
+ #options{name = KeyOrName, type = channel},
+ [{name, KeyOrName}]).
+
+%% session/4
+
+session(Conn, Opts, InitRec, NameOpt) ->
+ T = make_ref(),
+ try
+ [_ | {ok, SshConn}] = [T | call(Conn, get_ssh_connection)],
+ [_ | #options{} = Rec] = [T | make_session_options(Opts, InitRec)],
+ [_ | {ok, Client} = Ok] = [T | start(SshConn, Rec, NameOpt, true)],
+ [_ | ok] = [T | hello(Client, caps(Opts), Rec#options.timeout)],
+ Ok
+ catch
+ error: {badmatch, [T | Error]} ->
+ Error
end.
+%% caps/1
+
+caps(Opts) ->
+ [T || {capability, _} = T <- Opts].
+
%%----------------------------------------------------------------------
%% Open a netconf session and exchange 'hello' messages.
%% If the server options are specified in a configuration file, use
%% open/2.
+
+%% open/1
+
-spec open(Options) -> Result when
- Options :: options(),
- Result :: {ok,handle()} | {error,error_reason()}.
+ Options :: [option()],
+ Result :: {ok, handle()} | {error, error_reason()}.
+
open(Options) ->
- open(Options,#options{type=connection_and_channel},[],true).
+ open(Options,
+ #options{type = connection_and_channel},
+ [],
+ true).
--spec open(KeyOrName, ExtraOptions) -> Result when
+-spec open(KeyOrName, ExtraOption) -> Result when
KeyOrName :: ct:key_or_name(),
- ExtraOptions :: options(),
- Result :: {ok,handle()} | {error,error_reason()}.
+ ExtraOption :: [option()],
+ Result :: {ok, handle()} | {error, error_reason()}.
+
open(KeyOrName, ExtraOpts) ->
open(KeyOrName, ExtraOpts, true).
-open(KeyOrName, ExtraOpts, Hello) ->
- SortedExtra = lists:keysort(1,ExtraOpts),
- SortedConfig = lists:keysort(1,ct:get_config(KeyOrName,[])),
- AllOpts = lists:ukeymerge(1,SortedConfig,SortedExtra),
- open(AllOpts,#options{name=KeyOrName,type=connection_and_channel},
- [{name,KeyOrName}],Hello).
-
-open(OptList,InitOptRec,NameOpt,Hello) ->
- case check_options(OptList,InitOptRec) of
- {Host,Port,Options} ->
- case ct_gen_conn:start({Host,Port},Options,?MODULE,
- NameOpt ++ [{reconnect,false},
- {use_existing_connection,false},
- {forward_messages,true}]) of
- {ok,Client} when Hello==true ->
- case hello(Client,Options#options.timeout) of
- ok ->
- {ok,Client};
- Error ->
- Error
- end;
- Other ->
- Other
- end;
- Error ->
- Error
+%% open/3
+
+open(KeyOrName, ExtraOptions, Hello) ->
+ open(make_opts(KeyOrName, ExtraOptions),
+ #options{name = KeyOrName, type = connection_and_channel},
+ [{name, KeyOrName}],
+ Hello).
+
+%% open/4
+
+open(Opts, InitRec, NameOpt, Hello) ->
+ T = make_ref(),
+ try
+ [_, #options{} = Rec] = [T, make_options(Opts, InitRec)],
+ [_, {ok, Client} = Ok | true] = [T, start(Rec, NameOpt, true) | Hello],
+ [_, ok] = [T, hello(Client, caps(Opts), Rec#options.timeout)],
+ Ok
+ catch
+ error: {badmatch, [T, Res | _]} ->
+ Res
end.
+%% start/3
+
+start(#options{host = undefined}, _, _) ->
+ {error, no_host_address};
+
+start(#options{port = undefined}, _, _) ->
+ {error, no_port};
+
+start(#options{host = Host, port = Port} = Opts, NameOpt, Fwd) ->
+ start({Host, Port}, Opts, NameOpt, Fwd).
+
+%% start/4
+
+start(Ep, Opts, NameOpt, Fwd) ->
+ ct_gen_conn:start(Ep, Opts, ?MODULE, [{reconnect, false},
+ {use_existing_connection, false},
+ {forward_messages, Fwd}
+ | NameOpt]).
%%----------------------------------------------------------------------
-%% As open/1,2, except no 'hello' message is sent.
+%% Like open/1,2, but no 'hello' message is sent.
+
-spec only_open(Options) -> Result when
- Options :: options(),
- Result :: {ok,handle()} | {error,error_reason()}.
+ Options :: [option()],
+ Result :: {ok, handle()} | {error, error_reason()}.
+
only_open(Options) ->
- open(Options,#options{type=connection_and_channel},[],false).
+ open(Options, #options{type = connection_and_channel}, [], false).
--spec only_open(KeyOrName,ExtraOptions) -> Result when
+-spec only_open(KeyOrName, ExtraOptions) -> Result when
KeyOrName :: ct:key_or_name(),
- ExtraOptions :: options(),
- Result :: {ok,handle()} | {error,error_reason()}.
+ ExtraOptions :: [option()],
+ Result :: {ok, handle()} | {error, error_reason()}.
+
only_open(KeyOrName, ExtraOpts) ->
open(KeyOrName, ExtraOpts, false).
%%----------------------------------------------------------------------
%% Send a 'hello' message.
+
+%% hello/1
+
-spec hello(Client) -> Result when
Client :: handle(),
- Result :: ok | {error,error_reason()}.
+ Result :: ok | {error, error_reason()}.
+
hello(Client) ->
- hello(Client,[],?DEFAULT_TIMEOUT).
+ hello(Client, [], ?DEFAULT_TIMEOUT).
+
+%% hello/2
--spec hello(Client,Timeout) -> Result when
+-spec hello(Client, Timeout) -> Result when
Client :: handle(),
Timeout :: timeout(),
- Result :: ok | {error,error_reason()}.
-hello(Client,Timeout) ->
- hello(Client,[],Timeout).
+ Result :: ok | {error, error_reason()}.
+
+hello(Client, Timeout) ->
+ hello(Client, [], Timeout).
--spec hello(Client,Options,Timeout) -> Result when
+%% hello/3
+
+-spec hello(Client, Options, Timeout) -> Result when
Client :: handle(),
Options :: [{capability, [string()]}],
Timeout :: timeout(),
- Result :: ok | {error,error_reason()}.
-hello(Client,Options,Timeout) ->
+ Result :: ok | {error, error_reason()}.
+
+hello(Client, Options, Timeout) ->
call(Client, {hello, Options, Timeout}).
@@ -675,117 +731,122 @@ action(Client,Action,Timeout) ->
%%----------------------------------------------------------------------
%% Send a 'create-subscription' request
%% See RFC5277, NETCONF Event Notifications
--spec create_subscription(Client) -> Result when
- Client :: client(),
- Result :: ok | {error,error_reason()}.
-create_subscription(Client) ->
- create_subscription(Client,?DEFAULT_STREAM,?DEFAULT_TIMEOUT).
--spec create_subscription(Client, Stream | Filter | Timeout) -> Result when
+%% create_subscription/2
+
+-spec create_subscription(Client, Values) -> Result when
Client :: client(),
+ Values :: #{stream => Stream,
+ filter => Filter,
+ start => StartTime,
+ stop => StopTime},
Stream :: stream_name(),
Filter :: simple_xml() | [simple_xml()],
- Timeout :: timeout(),
+ StartTime :: xs_datetime(),
+ StopTime :: xs_datetime(),
+ Result :: ok | {error,error_reason()};
+ %% historic, no longer documented
+ (Client, list() | timeout()) -> Result when
+ Client :: client(),
Result :: ok | {error,error_reason()}.
-create_subscription(Client,Timeout)
+
+create_subscription(Client, #{} = Values) ->
+ create_subscription(Client, Values, ?DEFAULT_TIMEOUT);
+
+%% historic clauses
+create_subscription(Client, Timeout)
when ?is_timeout(Timeout) ->
- create_subscription(Client,?DEFAULT_STREAM,Timeout);
-create_subscription(Client,Stream)
+ create_subscription(Client, #{}, Timeout);
+create_subscription(Client, Stream)
when ?is_string(Stream) ->
- create_subscription(Client,Stream,?DEFAULT_TIMEOUT);
-create_subscription(Client,Filter)
+ create_subscription(Client, #{stream => Stream});
+create_subscription(Client, Filter)
when ?is_filter(Filter) ->
- create_subscription(Client,?DEFAULT_STREAM,Filter,
- ?DEFAULT_TIMEOUT).
+ create_subscription(Client, #{filter => Filter}).
-create_subscription(Client,Stream,Timeout)
- when ?is_string(Stream) andalso
- ?is_timeout(Timeout) ->
- call(Client,{send_rpc_op,{create_subscription,self()},
- [Stream,undefined,undefined,undefined],
- Timeout});
-create_subscription(Client,StartTime,StopTime)
- when ?is_string(StartTime) andalso
- ?is_string(StopTime) ->
- create_subscription(Client,?DEFAULT_STREAM,StartTime,StopTime,
- ?DEFAULT_TIMEOUT);
-create_subscription(Client,Filter,Timeout)
- when ?is_filter(Filter) andalso
- ?is_timeout(Timeout) ->
- create_subscription(Client,?DEFAULT_STREAM,Filter,Timeout);
-create_subscription(Client,Stream,Filter)
- when ?is_string(Stream) andalso
- ?is_filter(Filter) ->
- create_subscription(Client,Stream,Filter,?DEFAULT_TIMEOUT).
-
-create_subscription(Client,StartTime,StopTime,Timeout)
- when ?is_string(StartTime) andalso
- ?is_string(StopTime) andalso
- ?is_timeout(Timeout) ->
- create_subscription(Client,?DEFAULT_STREAM,StartTime,StopTime,Timeout);
-create_subscription(Client,Stream,StartTime,StopTime)
- when ?is_string(Stream) andalso
- ?is_string(StartTime) andalso
- ?is_string(StopTime) ->
- create_subscription(Client,Stream,StartTime,StopTime,?DEFAULT_TIMEOUT);
-create_subscription(Client,Filter,StartTime,StopTime)
- when ?is_filter(Filter) andalso
- ?is_string(StartTime) andalso
- ?is_string(StopTime) ->
- create_subscription(Client,?DEFAULT_STREAM,Filter,
- StartTime,StopTime,?DEFAULT_TIMEOUT);
-create_subscription(Client,Stream,Filter,Timeout)
- when ?is_string(Stream) andalso
- ?is_filter(Filter) andalso
- ?is_timeout(Timeout) ->
- call(Client,{send_rpc_op,{create_subscription,self()},
- [Stream,Filter,undefined,undefined],
- Timeout}).
-
--spec create_subscription(Client, Stream, StartTime, StopTime, Timeout) ->
- Result when
+-spec create_subscription(Client, Values, Timeout) -> Result when
Client :: client(),
+ Values :: #{stream => Stream,
+ filter => Filter,
+ start => StartTime,
+ stop => StopTime},
Stream :: stream_name(),
+ Filter :: simple_xml() | [simple_xml()],
StartTime :: xs_datetime(),
StopTime :: xs_datetime(),
Timeout :: timeout(),
Result :: ok | {error,error_reason()};
- (Client, Stream, Filter,StartTime, StopTime) ->
- Result when
+ %% historic, no longer documented
+ (Client, list(), list() | timeout()) -> Result when
Client :: client(),
- Stream :: stream_name(),
- Filter :: simple_xml() | [simple_xml()],
- StartTime :: xs_datetime(),
- StopTime :: xs_datetime(),
Result :: ok | {error,error_reason()}.
-create_subscription(Client,Stream,StartTime,StopTime,Timeout)
- when ?is_string(Stream) andalso
- ?is_string(StartTime) andalso
- ?is_string(StopTime) andalso
+
+create_subscription(Client, #{} = Values, Timeout) ->
+ Keys = [{stream, ?DEFAULT_STREAM},
+ {filter, undefined},
+ {start, undefined},
+ {stop, undefined}],
+ call(Client, {send_rpc_op, {create_subscription, self()},
+ [maps:get(K, Values, D) || {K,D} <- Keys],
+ Timeout});
+
+%% historic clauses, arity 3
+create_subscription(Client, Stream, Timeout)
+ when ?is_string(Stream), ?is_timeout(Timeout) ->
+ create_subscription(Client, #{stream => Stream}, Timeout);
+create_subscription(Client, StartTime, StopTime)
+ when ?is_string(StartTime), ?is_string(StopTime) ->
+ create_subscription(Client, #{start => StartTime, stop => StopTime});
+create_subscription(Client, Filter, Timeout)
+ when ?is_filter(Filter), ?is_timeout(Timeout) ->
+ create_subscription(Client, #{filter => Filter}, Timeout);
+create_subscription(Client, Stream, Filter)
+ when ?is_string(Stream), ?is_filter(Filter) ->
+ create_subscription(Client, #{stream => Stream, filter => Filter}).
+
+%% historic clauses, arity 1,4-5
+create_subscription(Client) ->
+ create_subscription(Client, #{}).
+create_subscription(Client, StartTime, StopTime, Timeout)
+ when ?is_string(StartTime), ?is_string(StopTime), ?is_timeout(Timeout) ->
+ Values = #{start => StartTime,
+ stop => StopTime},
+ create_subscription(Client, Values, Timeout);
+create_subscription(Client, Stream, StartTime, StopTime)
+ when ?is_string(Stream), ?is_string(StartTime), ?is_string(StopTime) ->
+ create_subscription(Client, #{stream => Stream,
+ start => StartTime,
+ stop => StopTime});
+create_subscription(Client, Filter, StartTime, StopTime)
+ when ?is_filter(Filter), ?is_string(StartTime), ?is_string(StopTime) ->
+ create_subscription(Client, #{filter => Filter,
+ start => StartTime,
+ stop => StopTime});
+create_subscription(Client, Stream, Filter, Timeout)
+ when ?is_string(Stream), ?is_filter(Filter), ?is_timeout(Timeout) ->
+ Values = #{stream => Stream,
+ filter => Filter},
+ create_subscription(Client, Values, Timeout).
+create_subscription(Client, Stream, StartTime, StopTime, Timeout)
+ when ?is_string(Stream), ?is_string(StartTime), ?is_string(StopTime),
?is_timeout(Timeout) ->
- call(Client,{send_rpc_op,{create_subscription,self()},
- [Stream,undefined,StartTime,StopTime],
- Timeout});
-create_subscription(Client,Stream,Filter,StartTime,StopTime)
- when ?is_string(Stream) andalso
- ?is_filter(Filter) andalso
- ?is_string(StartTime) andalso
+ Values = #{stream => Stream,
+ start => StartTime,
+ stop => StopTime},
+ create_subscription(Client, Values, Timeout);
+create_subscription(Client, Stream, Filter, StartTime, StopTime)
+ when ?is_string(Stream), ?is_filter(Filter), ?is_string(StartTime),
?is_string(StopTime) ->
- create_subscription(Client,Stream,Filter,StartTime,StopTime,?DEFAULT_TIMEOUT).
-
--spec create_subscription(Client, Stream, Filter,StartTime, StopTime, Timeout) ->
- Result when
- Client :: client(),
- Stream :: stream_name(),
- Filter :: simple_xml() | [simple_xml()],
- StartTime :: xs_datetime(),
- StopTime :: xs_datetime(),
- Timeout :: timeout(),
- Result :: ok | {error,error_reason()}.
-create_subscription(Client,Stream,Filter,StartTime,StopTime,Timeout) ->
- call(Client,{send_rpc_op,{create_subscription, self()},
- [Stream,Filter,StartTime,StopTime],
- Timeout}).
+ create_subscription(Client, #{stream => Stream,
+ filter => Filter,
+ start => StartTime,
+ stop => StopTime}).
+create_subscription(Client, Stream, Filter, StartTime, StopTime, Timeout) ->
+ Values = #{stream => Stream,
+ filter => Filter,
+ start => StartTime,
+ stop => StopTime},
+ create_subscription(Client, Values, Timeout).
%%----------------------------------------------------------------------
%% Send a request to get the given event streams
@@ -859,6 +920,8 @@ kill_session(Client, SessionId, Timeout) ->
%% Callback functions
%%----------------------------------------------------------------------
+%% init/3
+
init(_KeyOrName,{CM,{Host,Port}},Options) ->
case ssh_channel(#connection{reference=CM,host=Host,port=Port},Options) of
{ok,Connection} ->
@@ -883,29 +946,32 @@ init(_KeyOrName,{_Host,_Port},Options) ->
{error,Reason}
end.
+%% terminate/2
terminate(_, #state{connection=Connection}) ->
ssh_close(Connection),
ok.
-handle_msg({hello, Options, Timeout}, From,
- #state{connection=Connection,hello_status=HelloStatus} = State) ->
+%% handle_msg/3
+
+%% Send hello and return to the caller only after reception of the
+%% server's hello.
+handle_msg({hello, Options, Timeout},
+ From,
+ #state{connection = Connection,
+ hello_status = HelloStatus}
+ = State) ->
case do_send(Connection, client_hello(Options)) of
- ok ->
- case HelloStatus of
- undefined ->
- {Ref,TRef} = set_request_timer(Timeout),
- {noreply, State#state{hello_status=#pending{tref=TRef,
- ref=Ref,
- caller=From}}};
- received ->
- {reply, ok, State#state{hello_status=done}};
- {error,Reason} ->
- {stop, {error,Reason}, State}
- end;
- Error ->
+ ok when HelloStatus == undefined -> %% server hello not yet received
+ TRef = set_request_timer(Timeout, hello),
+ {noreply, State#state{hello_status = #pending{tref = TRef,
+ caller = From}}};
+ ok -> %% or yes: negotiate version
+ handle_capx(State);
+ Error ->
{stop, Error, State}
end;
+
handle_msg(get_ssh_connection, _From, #state{connection=Connection}=State) ->
Reply =
case Connection#connection.reference of
@@ -914,29 +980,40 @@ handle_msg(get_ssh_connection, _From, #state{connection=Connection}=State) ->
Connection#connection.port}}}
end,
{reply, Reply, State};
-handle_msg(_, _From, #state{session_id=undefined} = State) ->
- %% Hello is not yet excanged - this shall never happen
- {reply,{error,waiting_for_hello},State};
+
+%% Request before server hello. Possible with only_open, since a
+%% handle is then returned without waiting for the server.
+handle_msg(_, _From, #state{session_id = undefined} = State) ->
+ {reply, {error, waiting_for_hello}, State};
+
handle_msg(get_capabilities, _From, #state{capabilities = Caps} = State) ->
{reply, Caps, State};
+
handle_msg(get_session_id, _From, #state{session_id = Id} = State) ->
{reply, Id, State};
-handle_msg({send, Timeout, SimpleXml}, From,
- #state{connection=Connection,pending=Pending} = State) ->
+
+handle_msg({send, Timeout, SimpleXml},
+ From,
+ #state{connection = Connection,
+ pending = Pending}
+ = State) ->
case do_send(Connection, SimpleXml) of
- ok ->
- {Ref,TRef} = set_request_timer(Timeout),
- {noreply, State#state{pending=[#pending{tref=TRef,
- ref=Ref,
- caller=From} | Pending]}};
- Error ->
- {reply, Error, State}
+ ok ->
+ TRef = set_request_timer(Timeout, send),
+ {noreply, State#state{pending = [#pending{tref = TRef,
+ caller = From}
+ | Pending]}};
+ Error ->
+ {reply, Error, State}
end;
+
handle_msg({send_rpc, SimpleXml, Timeout}, From, State) ->
do_send_rpc(undefined, SimpleXml, Timeout, From, State);
+
handle_msg({send_rpc_op, Op, Data, Timeout}, From, State) ->
SimpleXml = encode_rpc_operation(Op,Data),
do_send_rpc(Op, SimpleXml, Timeout, From, State);
+
handle_msg({get_event_streams=Op,Streams,Timeout}, From, State) ->
Filter = {netconf,?NETMOD_NOTIF_NAMESPACE_ATTR,
[{streams,[{stream,[{name,[Name]}]} || Name <- Streams]}]},
@@ -945,7 +1022,9 @@ handle_msg({get_event_streams=Op,Streams,Timeout}, From, State) ->
handle_msg({ssh_cm, CM, {data, Ch, _Type, Data}}, State) ->
ssh_connection:adjust_window(CM,Ch,size(Data)),
+ log(State#state.connection, recv, Data),
handle_data(Data, State);
+
handle_msg({ssh_cm, _CM, _SshCloseMsg}, State) ->
%% _SshCloseMsg can probably be one of
%% {eof,Ch}
@@ -962,21 +1041,29 @@ handle_msg({ssh_cm, _CM, _SshCloseMsg}, State) ->
%%! connection - due to terminate/2
{stop, State};
-handle_msg({Ref,timeout},
- #state{hello_status=#pending{ref=Ref,caller=Caller}} = State) ->
- ct_gen_conn:return(Caller,{error,{hello_session_failed,timeout}}),
- {stop,State#state{hello_status={error,timeout}}};
-handle_msg({Ref,timeout},#state{pending=Pending} = State) ->
- {value,#pending{op=Op,caller=Caller},Pending1} =
- lists:keytake(Ref,#pending.ref,Pending),
- ct_gen_conn:return(Caller,{error,timeout}),
- R = case Op of
- close_session -> stop;
- _ -> noreply
- end,
- %% Halfhearted try to get in correct state, this matches
- %% the implementation before this patch
- {R,State#state{pending=Pending1, no_end_tag_buff= <<>>, buff= <<>>}}.
+
+handle_msg({timeout, TRef, hello},
+ #state{hello_status = #pending{tref = TRef,
+ caller = From}}
+ = State) ->
+ ct_gen_conn:return(From, {error, {hello_session_failed, timeout}}),
+ {stop, State#state{hello_status = {error,timeout}}};
+
+handle_msg({timeout, TRef, Op}, #state{pending = Pending} = State) ->
+ case lists:keytake(TRef, #pending.tref, Pending) of
+ {value, #pending{caller = From}, Rest} ->
+ ct_gen_conn:return(From, {error, timeout}),
+ %% Discard received bytes in hope that the server has sent
+ %% an incomplete message. Otherwise this is doomed to
+ %% leave the connection in an unusable state.
+ {if Op == close_session -> stop; true -> noreply end,
+ State#state{pending = Rest,
+ buf = is_binary(State#state.buf)}};
+ false ->
+ {noreply, State}
+ end.
+
+%% close/1
%% Called by ct_util_server to close registered connections before terminate.
close(Client) ->
@@ -1048,63 +1135,163 @@ get_handle(Client) ->
Error
end.
-check_options(OptList,Options) ->
- check_options(OptList,undefined,undefined,Options).
+%% make_options/2
-check_options([], undefined, _Port, _Options) ->
- {error, no_host_address};
-check_options([], _Host, undefined, _Options) ->
- {error, no_port};
-check_options([], Host, Port, Options) ->
- {Host,Port,Options};
-check_options([{ssh, Host}|T], _, Port, Options) ->
- check_options(T, Host, Port, Options#options{host=Host});
-check_options([{port,Port}|T], Host, _, Options) ->
- check_options(T, Host, Port, Options#options{port=Port});
-check_options([{timeout, Timeout}|T], Host, Port, Options)
- when is_integer(Timeout); Timeout==infinity ->
- check_options(T, Host, Port, Options#options{timeout = Timeout});
-check_options([{timeout, _} = Opt|_T], _Host, _Port, _Options) ->
- {error, {invalid_option, Opt}};
-check_options([Opt|T], Host, Port, #options{ssh=SshOpts}=Options) ->
- %% Option verified by ssh
- check_options(T, Host, Port, Options#options{ssh=[Opt|SshOpts]}).
-
-check_session_options([],Options) ->
- {ok,Options};
-check_session_options([{timeout, Timeout}|T], Options)
- when is_integer(Timeout); Timeout==infinity ->
- check_session_options(T, Options#options{timeout = Timeout});
-check_session_options([Opt|_T], _Options) ->
- {error, {invalid_option, Opt}}.
+make_options(Opts, Rec) ->
+ make_options(Opts, Rec#options{port = undefined}, fun opt/2).
+
+opt({T, Host}, Rec)
+ when T == ssh;
+ T == host ->
+ Rec#options{host = Host};
+
+opt({port, Port}, Rec) ->
+ Rec#options{port = Port};
+
+opt({timeout, Tmo}, Rec)
+ when is_integer(Tmo);
+ Tmo == infinity ->
+ Rec#options{timeout = Tmo};
+
+opt({timeout, _} = T, _) ->
+ throw(T);
+
+opt({capability, _}, Rec) ->
+ Rec;
+
+opt(Opt, #options{ssh = Opts} = Rec) -> %% option verified by ssh
+ Rec#options{ssh = [Opt | Opts]}.
+
+%% make_session_options/2
+
+make_session_options(Opts, Rec) ->
+ make_options(Opts, Rec, fun session_opt/2).
+
+session_opt({capability, _}, Rec) ->
+ Rec;
+
+session_opt({timeout, Tmo}, Rec)
+ when is_integer(Tmo);
+ Tmo == infinity ->
+ Rec#options{timeout = Tmo};
+session_opt(T, _Rec) ->
+ throw(T).
+
+%% make_options/3
+
+make_options(Opts, Rec, F) ->
+ try
+ #options{} = lists:foldl(F, Rec, Opts)
+ catch
+ T ->
+ {error, {invalid_option, T}}
+ end.
%%%-----------------------------------------------------------------
-set_request_timer(infinity) ->
- {undefined,undefined};
-set_request_timer(T) ->
- Ref = make_ref(),
- {ok,TRef} = timer:send_after(T,{Ref,timeout}),
- {Ref,TRef}.
+
+set_request_timer(infinity, _) ->
+ false;
+
+set_request_timer(Tmo, Op) ->
+ erlang:start_timer(Tmo, self(), Op).
%%%-----------------------------------------------------------------
-cancel_request_timer(undefined,undefined) ->
+
+cancel_request_timer(false) ->
ok;
-cancel_request_timer(Ref,TRef) ->
- _ = timer:cancel(TRef),
- receive {Ref,timeout} -> ok
- after 0 -> ok
- end.
+
+cancel_request_timer(TRef) ->
+ erlang:cancel_timer(TRef).
%%%-----------------------------------------------------------------
-client_hello(Options) when is_list(Options) ->
- UserCaps = [{capability, UserCap} ||
- {capability, UserCap} <- Options,
- is_list(hd(UserCap))],
- {hello, ?NETCONF_NAMESPACE_ATTR,
- [{capabilities,
- [{capability,[?NETCONF_BASE_CAP++?NETCONF_BASE_CAP_VSN]}|
- UserCaps]}]}.
+
+%% client_hello/1
+%%
+%% Prepend the 1.0 base capability only if none is specified by the
+%% user. Store the versions in the process dictionary until they're
+%% examined upon reception of server capabilities in handle_capx/1.
+
+client_hello(Opts)
+ when is_list(Opts) ->
+ UserCaps = [{T, cap(lists:flatten(Cs))} || {capability = T, Cs} <- Opts],
+ Vsns = versions(UserCaps),
+ put(?KEY(protocol_vsn), Vsns),
+ {hello,
+ ?NETCONF_NAMESPACE_ATTR,
+ [{capabilities, [{capability, [?NETCONF_BASE_CAP, ?NETCONF_BASE_CAP_VSN]}
+ || [] == Vsns]
+ ++ UserCaps}]}.
+
+%% cap/1
+%%
+%% Let NETCONF capabilities be specified in the shorthand documented in
+%% RFC 6241.
+
+%% This shorthand is documented in RFC 6241 10.4 NETCONF Capabilities
+%% URNS, but not in 8 Capabilities.
+cap(":base:" ++ _ = Str) ->
+ ["urn:ietf:params:netconf", Str];
+
+cap([$:|_] = Str) ->
+ ["urn:ietf:params:netconf:capability", Str];
+
+cap(Str) ->
+ [Str].
+
+%% versions/1
+%%
+%% Extract base protocol versions from capability options.
+
+versions(Opts) ->
+ [V || {capability, L} <- Opts,
+ S <- L,
+ ?NETCONF_BASE_CAP ++ X <- [lists:flatten(S)],
+ V <- [lists:takewhile(fun(C) -> C /= $? end, X)]].
+
+%% handle_capx/1
+%%
+%% Ignore parameters as RFC 6241 (NETCONF 1.1) requires in 8.1
+%% Capabilities Exchange. Be overly lenient with whitespace since RFC
+%% 6241 gives examples with significant trailing whitespace.
+
+handle_capx(#state{hello_status = received, capabilities = Caps} = S) ->
+ Remote = [V || ?NETCONF_BASE_CAP ++ X <- Caps,
+ [V|_] <- [string:lexemes(X, "? \t\r\n")]],
+ Local = erase(?KEY(protocol_vsn)),
+ case protocol_vsn(Local, Remote) of
+ false when Remote == [] ->
+ Reason = {incorrect_hello, no_base_capability_found},
+ {stop, {error, Reason}, S};
+ false ->
+ Reason = {incompatible_base_capability_vsn, lists:min(Remote)},
+ {stop, {error, Reason}, S};
+ Vsn ->
+ put(?KEY(chunk), Vsn /= "1.0"),
+ {reply, ok, rebuf(Vsn, S#state{hello_status = Vsn})}
+ end;
+
+handle_capx(#state{hello_status = {error, _} = No} = S) ->
+ {stop, No, S}.
+
+%% rebuf/2
+%%
+%% Turn the message buffer into a list for 1.1 chunking if the
+%% negotiated protocol version is > 1.0.
+
+rebuf("1.0", S) ->
+ S;
+
+rebuf(_, #state{buf = Bin} = S) ->
+ S#state{buf = [Bin, 3]}.
+
+%% protocol_vsn/2
+
+protocol_vsn([], Vsns) ->
+ protocol_vsn(["1.0"], Vsns);
+
+protocol_vsn(Local, Remote) ->
+ lists:max([false | [V || V <- Remote, lists:member(V, Local)]]).
%%%-----------------------------------------------------------------
@@ -1150,111 +1337,130 @@ maybe_element(Tag,Value) ->
%%%-----------------------------------------------------------------
%%% Send XML data to server
-do_send_rpc(PendingOp,SimpleXml,Timeout,Caller,
- #state{connection=Connection,msg_id=MsgId,pending=Pending} = State) ->
- case do_send_rpc(Connection, MsgId, SimpleXml) of
- ok ->
- {Ref,TRef} = set_request_timer(Timeout),
- {noreply, State#state{msg_id=MsgId+1,
- pending=[#pending{tref=TRef,
- ref=Ref,
- msg_id=MsgId,
- op=PendingOp,
- caller=Caller} | Pending]}};
- Error ->
- {reply, Error, State#state{msg_id=MsgId+1}}
+do_send_rpc(Op, SimpleXml, Timeout, Caller, #state{connection = Connection,
+ msg_id = MsgId,
+ pending = Pending}
+ = State) ->
+ Msg = {rpc,
+ [{'message-id', MsgId} | ?NETCONF_NAMESPACE_ATTR],
+ [SimpleXml]},
+ Next = MsgId + 1,
+ case do_send(Connection, Msg) of
+ ok ->
+ TRef = set_request_timer(Timeout, Op),
+ Rec = #pending{tref = TRef,
+ msg_id = MsgId,
+ op = Op,
+ caller = Caller},
+ {noreply, State#state{msg_id = Next,
+ pending = [Rec | Pending]}};
+ Error ->
+ {reply, Error, State#state{msg_id = Next}}
end.
-do_send_rpc(Connection, MsgId, SimpleXml) ->
- do_send(Connection,
- {rpc,
- [{'message-id',MsgId} | ?NETCONF_NAMESPACE_ATTR],
- [SimpleXml]}).
+do_send(Connection, Simple) ->
+ ssh_send(Connection, frame(to_xml(Simple))).
-do_send(Connection, SimpleXml) ->
- Xml=to_xml_doc(SimpleXml),
- ssh_send(Connection, Xml).
-
-to_xml_doc(Simple) ->
+to_xml(Simple) ->
Prolog = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
- Xml = unicode:characters_to_binary(
- xmerl:export_simple([Simple],
- xmerl_xml,
- [#xmlAttribute{name=prolog,
- value=Prolog}])),
- <<Xml/binary,?END_TAG/binary>>.
+ Chars = xmerl:export_simple([Simple],
+ xmerl_xml,
+ [#xmlAttribute{name = prolog,
+ value = Prolog}]),
+ unicode:characters_to_binary(Chars).
+
+%% frame/1
+
+frame(Bin) ->
+ case get(?KEY(chunk)) of
+ true -> %% 1.1 chunking
+ [chunk(Bin) | "\n##\n"];
+ _ -> %% 1.0 framing
+ [Bin | ?END_TAG]
+ end.
+
+%% chunk/1
+%%
+%% Chunk randomly to exercise the server.
+
+chunk(<<>>) ->
+ [];
+
+chunk(Bin) ->
+ Sz = min(rand:uniform(1024), size(Bin)),
+ <<B:Sz/binary, Rest/binary>> = Bin,
+ ["\n#", integer_to_list(Sz), $\n, B | chunk(Rest)].
%%%-----------------------------------------------------------------
%%% Parse and handle received XML data
-%%% Two buffers are used:
-%%% * 'no_end_tag_buff' contains data that is checked and does not
-%%% contain any (part of an) end tag.
-%%% * 'buff' contains all other saved data - it may or may not
-%%% include (a part of) an end tag.
-%%% The reason for this is to avoid running binary:split/3 multiple
-%%% times on the same data when it does not contain an end tag. This
-%%% can be a considerable optimation in the case when a lot of data is
-%%% received (e.g. when fetching all data from a node) and the data is
-%%% sent in multiple ssh packages.
-handle_data(NewData,#state{connection=Connection} = State0) ->
- log(Connection,recv,NewData),
- NoEndTag0 = State0#state.no_end_tag_buff,
- Buff0 = State0#state.buff,
- Data = <<Buff0/binary, NewData/binary>>,
- case binary:split(Data,?END_TAG,[]) of
- [_NoEndTagFound] ->
- NoEndTagSize = case byte_size(Data) of
- Sz when Sz<5 -> 0;
- Sz -> Sz-5
- end,
- <<NoEndTag1:NoEndTagSize/binary,Buff/binary>> = Data,
- NoEndTag = <<NoEndTag0/binary,NoEndTag1/binary>>,
- {noreply, State0#state{no_end_tag_buff=NoEndTag, buff=Buff}};
- [FirstMsg0,Buff1] ->
- FirstMsg = remove_initial_nl(<<NoEndTag0/binary,FirstMsg0/binary>>),
- SaxArgs = [{event_fun,fun sax_event/3}, {event_state,[]}],
- case xmerl_sax_parser:stream(FirstMsg, SaxArgs) of
- {ok, Simple, _Thrash} ->
- case decode(Simple, State0#state{no_end_tag_buff= <<>>,
- buff=Buff1}) of
- {noreply, #state{buff=Buff} = State} when Buff =/= <<>> ->
- %% Recurse if we have more data in buffer
- handle_data(<<>>, State);
- Other ->
- Other
- end;
- {fatal_error,_Loc,Reason,_EndTags,_EventState} ->
- ?error(Connection#connection.name,
- [{parse_error,Reason},
- {buffer, Buff0},
- {new_data,NewData}]),
- handle_error(Reason, State0#state{no_end_tag_buff= <<>>,
- buff= <<>>})
- end
+
+handle_data(Bin, #state{buf = Head} = S) ->
+ case recv(Bin, Head) of
+ {error, Reason} ->
+ Conn = S#state.connection,
+ ?error(Conn#connection.name, [{receive_error, Reason},
+ {buffer, Head},
+ {bytes, Bin}]),
+ {stop, S};
+ {Bytes, Rest} ->
+ handle_more(Rest, handle_xml(Bytes, S));
+ Buf ->
+ {noreply, S#state{buf = Buf}}
end.
+%% handle_more/2
+
+handle_more(_, {stop, _} = No) ->
+ No;
+
+handle_more(Bin, {noreply, State}) ->
+ handle_data(Bin, State#state{buf = true == get(?KEY(chunk))}).
+
+%% handle_xml/2
+
+handle_xml(Bytes, State) ->
+ case parse(Bytes) of
+ {ok, Simple, _Rest} -> %% ignore trailing bytes
+ decode(Simple, State);
+ {fatal_error,_Loc,Reason,_EndTags,_EventState} ->
+ Conn = State#state.connection,
+ ?error(Conn#connection.name, [{parse_error, Reason},
+ {message, Bytes}]),
+ {noreply, handle_error(Reason, State)}
+ end.
+
+%% parse/1
+
+parse(Bytes) ->
+ xmerl_sax_parser:stream(<<>>, [{event_fun, fun sax_event/3},
+ {event_state, []},
+ {continuation_fun, fun cont/1},
+ {continuation_state, Bytes}]).
+
+%% cont/1
-%% xml does not accept a leading nl and some netconf server add a nl after
-%% each ?END_TAG, ignore them
-remove_initial_nl(<<"\n", Data/binary>>) ->
- remove_initial_nl(Data);
-remove_initial_nl(Data) ->
- Data.
-
-handle_error(Reason, State) ->
- Pending1 = case State#state.pending of
- [] -> [];
- Pending ->
- %% Assuming the first request gets the
- %% first answer
- P=#pending{tref=TRef,ref=Ref,caller=Caller} =
- lists:last(Pending),
- cancel_request_timer(Ref,TRef),
- Reason1 = {failed_to_parse_received_data,Reason},
- ct_gen_conn:return(Caller,{error,Reason1}),
- lists:delete(P,Pending)
- end,
- {noreply, State#state{pending=Pending1}}.
+cont([] = No) ->
+ {<<>>, No};
+
+cont([Bin | Rest]) ->
+ {Bin, Rest};
+
+cont(Bin) ->
+ {Bin, <<>>}.
+
+%% handle_error/2
+
+handle_error(_Reason, #state{pending = []} = State) ->
+ State;
+
+handle_error(Reason, #state{pending = Pending} = State) ->
+ %% Assuming the first request gets the first answer.
+ Rec = #pending{tref = TRef,
+ caller = Caller}
+ = lists:last(Pending),
+ cancel_request_timer(TRef),
+ ct_gen_conn:return(Caller,{error, {failed_to_parse_received_data, Reason}}),
+ State#state{pending = lists:delete(Rec, Pending)}.
%% Event function for the sax parser. It builds a simple XML structure.
%% Care is taken to keep namespace attributes and prefixes as in the original XML.
@@ -1305,136 +1511,180 @@ parse_attrs([]) ->
%%%-----------------------------------------------------------------
-%%% Decoding of parsed XML data
-decode({Tag,Attrs,_}=E, #state{connection=Connection,pending=Pending}=State) ->
- ConnName = Connection#connection.name,
- case get_local_name_atom(Tag) of
- 'rpc-reply' ->
- case get_msg_id(Attrs) of
- undefined ->
- case Pending of
- [#pending{msg_id=MsgId}] ->
- ?error(ConnName,[{warning,rpc_reply_missing_msg_id},
- {assuming,MsgId}]),
- decode_rpc_reply(MsgId,E,State);
- _ ->
- ?error(ConnName,[{error,rpc_reply_missing_msg_id}]),
- {noreply,State}
- end;
- MsgId ->
- decode_rpc_reply(MsgId,E,State)
- end;
- hello ->
- case State#state.hello_status of
- undefined ->
- case decode_hello(E) of
- {ok,SessionId,Capabilities} ->
- {noreply,State#state{session_id = SessionId,
- capabilities = Capabilities,
- hello_status = received}};
- {error,Reason} ->
- {noreply,State#state{hello_status = {error,Reason}}}
- end;
- #pending{tref=TRef,ref=Ref,caller=Caller} ->
- cancel_request_timer(Ref,TRef),
- case decode_hello(E) of
- {ok,SessionId,Capabilities} ->
- ct_gen_conn:return(Caller,ok),
- {noreply,State#state{session_id = SessionId,
- capabilities = Capabilities,
- hello_status = done}};
- {error,Reason} ->
- ct_gen_conn:return(Caller,{error,Reason}),
- {stop,State#state{hello_status={error,Reason}}}
- end;
- Other ->
- ?error(ConnName,[{got_unexpected_hello,E},
- {hello_status,Other}]),
- {noreply,State}
- end;
- notification ->
- EventReceiver = State#state.event_receiver,
- EventReceiver ! E,
- {noreply,State};
- Other ->
- %% Result of send/2, when not sending an rpc request - or
- %% if netconf server sends noise. Can handle this only if
- %% there is just one pending that matches (i.e. has
- %% undefined msg_id and op)
- case [P || P = #pending{msg_id=undefined,op=undefined} <- Pending] of
- [#pending{tref=TRef,ref=Ref,caller=Caller}] ->
- cancel_request_timer(Ref,TRef),
- ct_gen_conn:return(Caller,E),
- {noreply,State#state{pending=[]}};
- _ ->
- ?error(ConnName,[{got_unexpected_msg,Other},
- {expecting,Pending}]),
- {noreply,State}
- end
+%% decode/2
+%%
+%% Decode parsed (incoming) XML.
+
+decode({Tag, _, _} = E, #state{} = State) ->
+ case decode(get_local_name_atom(Tag), E, State) of
+ #state{} = S ->
+ {noreply, S};
+ {stop, #state{}} = T ->
+ T
+ end.
+
+%% decode/3
+
+decode('rpc-reply', {_, Attrs, _} = E, State) ->
+ decode_rpc_reply(get_msg_id(Attrs), E, State);
+
+%% Incoming hello, outgoing not yet sent.
+decode(hello, E, #state{hello_status = undefined} = State) ->
+ case decode_hello(E) of
+ {ok, SessionId, Capabilities} ->
+ State#state{session_id = SessionId,
+ capabilities = Capabilities,
+ hello_status = received};
+ {error, _Reason} = No ->
+ State#state{hello_status = No}
+ end;
+
+%% Incoming hello, outgoing already sent: negotiate protocol version.
+decode(hello, E, #state{hello_status = #pending{tref = TRef,
+ caller = From}}
+ = State) ->
+ cancel_request_timer(TRef),
+ case decode_hello(E) of
+ {ok, SessionId, Capabilities} ->
+ reply(From, handle_capx(State#state{session_id = SessionId,
+ capabilities = Capabilities,
+ hello_status = received}));
+ {error, _Reason} = No ->
+ ct_gen_conn:return(From, No),
+ {stop, State#state{hello_status = No}}
+ end;
+
+%% Duplicate hello: ignore.
+decode(hello, E, #state{hello_status = Other} = State) ->
+ ConnName = (State#state.connection)#connection.name,
+ ?error(ConnName, [{got_unexpected_hello, E},
+ {hello_status, Other}]),
+ State;
+
+decode(notification, E, State) ->
+ State#state.event_receiver ! E,
+ State;
+
+decode(Other, E, State) ->
+ decode_send({got_unexpected_msg, Other}, E, State).
+
+%% reply/2
+%%
+%% Explicitly send a reply that can't be returned.
+
+reply(From, {T, Res, State}) ->
+ ct_gen_conn:return(From, Res),
+ case T of
+ reply ->
+ State;
+ stop ->
+ {T, State}
end.
+%% get_msg_id/1
+
get_msg_id(Attrs) ->
- case lists:keyfind('message-id',1,Attrs) of
- {_,Str} ->
- list_to_integer(Str);
- false ->
- undefined
+ case find('message-id', Attrs) of
+ {_,Str} ->
+ list_to_integer(Str);
+ false ->
+ undefined
end.
-decode_rpc_reply(MsgId,{_,Attrs,Content0}=E,#state{pending=Pending} = State) ->
- case lists:keytake(MsgId,#pending.msg_id,Pending) of
- {value, #pending{tref=TRef,ref=Ref,op=Op,caller=Caller}, Pending1} ->
- cancel_request_timer(Ref,TRef),
- Content = forward_xmlns_attr(Attrs,Content0),
- {CallerReply,{ServerReply,State2}} =
- do_decode_rpc_reply(Op,Content,State#state{pending=Pending1}),
- ct_gen_conn:return(Caller,CallerReply),
- {ServerReply,State2};
- false ->
- %% Result of send/2, when receiving a correct
- %% rpc-reply. Can handle this only if there is just one
- %% pending that matches (i.e. has undefined msg_id and op)
- case [P || P = #pending{msg_id=undefined,op=undefined} <- Pending] of
- [#pending{tref=TRef,
- ref=Ref,
- msg_id=undefined,
- op=undefined,
- caller=Caller}] ->
- cancel_request_timer(Ref,TRef),
- ct_gen_conn:return(Caller,E),
- {noreply,State#state{pending=[]}};
- _ ->
- ConnName = (State#state.connection)#connection.name,
- ?error(ConnName,[{got_unexpected_msg_id,MsgId},
- {expecting,Pending}]),
- {noreply,State}
- end
+%% recode_rpc_reply/3
+
+decode_rpc_reply(undefined, E, #state{pending = [#pending{msg_id = MsgId}]}
+ = State)
+ when MsgId /= undefined ->
+ ConnName = (State#state.connection)#connection.name,
+ ?error(ConnName, [{warning, rpc_reply_missing_msg_id},
+ {assuming, MsgId}]),
+ decode_rpc_reply(MsgId, E, State);
+
+decode_rpc_reply(undefined, _, State) ->
+ ConnName = (State#state.connection)#connection.name,
+ ?error(ConnName, [{error, rpc_reply_missing_msg_id}]),
+ State;
+
+decode_rpc_reply(MsgId,
+ {_, Attrs, Content0}
+ = E,
+ #state{pending = Pending}
+ = State) ->
+ case lists:keytake(MsgId, #pending.msg_id, Pending) of
+ {value, Rec, Rest} ->
+ #pending{tref = TRef, op = Op, caller = From}
+ = Rec,
+ cancel_request_timer(TRef),
+ Content = forward_xmlns_attr(Attrs, Content0),
+ {Reply, T} = do_decode_rpc_reply(Op,
+ Content,
+ State#state{pending = Rest}),
+ ct_gen_conn:return(From, Reply),
+ T;
+ false -> %% not a send_rcp or server has sent wrong id
+ decode_send({got_unexpected_msg_id, MsgId}, E, State)
+ end.
+
+%% decode_send/2
+%%
+%% Result of send/2,3. Only handle one at a time there since all
+%% pendings have msg_id = undefined.
+
+decode_send(ErrorT, Elem, #state{pending = Pending} = State) ->
+ case [P || #pending{msg_id = undefined} = P <- Pending] of
+ [Rec] ->
+ #pending{tref = TRef,
+ caller = From}
+ = Rec,
+ cancel_request_timer(TRef),
+ ct_gen_conn:return(From, Elem),
+ State#state{pending = lists:delete(Rec, Pending)};
+ _ ->
+ Conn = State#state.connection,
+ ?error(Conn#connection.name, [ErrorT, {expecting, Pending}]),
+ State
end.
-do_decode_rpc_reply(Op,Result,State)
- when Op==lock; Op==unlock; Op==edit_config; Op==delete_config;
- Op==copy_config; Op==kill_session ->
- {decode_ok(Result),{noreply,State}};
-do_decode_rpc_reply(Op,Result,State)
- when Op==get; Op==get_config; Op==action ->
- {decode_data(Result),{noreply,State}};
-do_decode_rpc_reply(close_session,Result,State) ->
+%% do_decode_rpc_reply/3
+
+do_decode_rpc_reply(Op, Result, State)
+ when Op == lock;
+ Op == unlock;
+ Op == edit_config;
+ Op == delete_config;
+ Op == copy_config;
+ Op == kill_session ->
+ {decode_ok(Result), State};
+
+do_decode_rpc_reply(Op, Result, State)
+ when Op == get;
+ Op == get_config;
+ Op == action ->
+ {decode_data(Result), State};
+
+do_decode_rpc_reply(close_session, Result, State) ->
case decode_ok(Result) of
- ok -> {ok,{stop,State}};
- Other -> {Other,{noreply,State}}
+ ok ->
+ {ok, {stop, State}};
+ Other ->
+ {Other, State}
end;
-do_decode_rpc_reply({create_subscription,Caller},Result,State) ->
+
+do_decode_rpc_reply({create_subscription, From}, Result, State) ->
case decode_ok(Result) of
- ok ->
- {ok,{noreply,State#state{event_receiver=Caller}}};
- Other ->
- {Other,{noreply,State}}
+ ok ->
+ {ok, State#state{event_receiver = From}};
+ Other ->
+ {Other, State}
end;
-do_decode_rpc_reply(get_event_streams,Result,State) ->
- {decode_streams(decode_data(Result)),{noreply,State}};
-do_decode_rpc_reply(undefined,Result,State) ->
- {Result,{noreply,State}}.
+
+do_decode_rpc_reply(get_event_streams, Result, State) ->
+ {decode_streams(decode_data(Result)), State};
+
+do_decode_rpc_reply(undefined, Result, State) ->
+ {Result, State}.
@@ -1454,7 +1704,7 @@ decode_data([{Tag,Attrs,Content}]) ->
case get_local_name_atom(Tag) of
ok ->
%% when action has return type void
- ok;
+ ok;
data ->
%% Since content of data has nothing from the netconf
%% namespace, we remove the parent's xmlns attribute here
@@ -1525,41 +1775,43 @@ get_all_xmlns_attrs([{Key,_}=Attr|Attrs],XmlnsAttrs) ->
get_all_xmlns_attrs([],XmlnsAttrs) ->
XmlnsAttrs.
-
%% Decode server hello to pick out session id and capabilities
-decode_hello({hello,_Attrs,Hello}) ->
- case lists:keyfind('session-id',1,Hello) of
- {'session-id',_,[SessionId]} ->
- case lists:keyfind(capabilities,1,Hello) of
- {capabilities,_,Capabilities} ->
- case decode_caps(Capabilities,[],false) of
- {ok,Caps} ->
- {ok,list_to_integer(SessionId),Caps};
- Error ->
- Error
- end;
- false ->
- {error,{incorrect_hello,capabilities_not_found}}
- end;
- false ->
- {error,{incorrect_hello,no_session_id_found}}
+decode_hello({hello, _Attrs, Hello}) ->
+ U = make_ref(),
+ try
+ [{'session-id', _, [SessionId]}, _ | _]
+ = [find('session-id', Hello), no_session_id_found | U],
+ [{ok, Id}, _ | _]
+ = [catch {ok, list_to_integer(SessionId)}, invalid_session_id | U],
+ [true, _ | _]
+ = [0 < Id, invalid_session_id | U],
+ [{capabilities, _, Capabilities}, _ | _]
+ = [find(capabilities, Hello), capabilities_not_found | U],
+ [{ok, Caps}, _ | _]
+ = [decode_caps(Capabilities, [], false), false | U],
+ {ok, Id, Caps}
+ catch
+ error: {badmatch, [Error, false | U]} ->
+ Error;
+ error: {badmatch, [_, Reason | U]} ->
+ {error, {incorrect_hello, Reason}}
end.
-decode_caps([{capability,[],[?NETCONF_BASE_CAP++Vsn=Cap]} |Caps], Acc, _) ->
- case Vsn of
- ?NETCONF_BASE_CAP_VSN ->
- decode_caps(Caps, [Cap|Acc], true);
- _ ->
- {error,{incompatible_base_capability_vsn,Vsn}}
- end;
-decode_caps([{capability,[],[Cap]}|Caps],Acc,Base) ->
- decode_caps(Caps,[Cap|Acc],Base);
-decode_caps([H|_T],_,_) ->
- {error,{unexpected_capability_element,H}};
-decode_caps([],_,false) ->
- {error,{incorrect_hello,no_base_capability_found}};
-decode_caps([],Acc,true) ->
- {ok,lists:reverse(Acc)}.
+find(Key, List) ->
+ lists:keyfind(Key, 1, List).
+
+decode_caps([{capability, [], [?NETCONF_BASE_CAP ++ _ = Cap]} | Caps],
+ Acc,
+ _) ->
+ decode_caps(Caps, [Cap|Acc], true);
+decode_caps([{capability, [], [Cap]} | Caps], Acc, Base) ->
+ decode_caps(Caps, [Cap|Acc], Base);
+decode_caps([H|_], _, _) ->
+ {error, {unexpected_capability_element, H}};
+decode_caps([], _, false) ->
+ {error, {incorrect_hello, no_base_capability_found}};
+decode_caps([], Acc, true) ->
+ {ok, lists:reverse(Acc)}.
%% Return a list of {Name,Data}, where data is a {Tag,Value} list for each stream
@@ -1570,7 +1822,7 @@ decode_streams({ok,[{netconf,_,Streams}]}) ->
decode_streams([{streams,_,Streams}]) ->
decode_streams(Streams);
decode_streams([{stream,_,Stream} | Streams]) ->
- {name,_,[Name]} = lists:keyfind(name,1,Stream),
+ {name,_,[Name]} = find(name, Stream),
[{Name,[{Tag,Value} || {Tag,_,[Value]} <- Stream, Tag /= name]}
| decode_streams(Streams)];
decode_streams([]) ->
@@ -1814,6 +2066,190 @@ ssh_close(Connection=#connection{reference = CM}) ->
log(Connection,disconnect),
ok.
+%% ===========================================================================
+
+%% recv/1
+%%
+%% Extract incoming messages using either NETCONF 1.0 framing or
+%% NETCONF 1.1 chunking.
+
+recv(Bin, true) ->
+ recv(Bin, [<<>>, 3]);
+recv(Bin, false) ->
+ recv(Bin, <<>>);
+
+recv(Bin, [Head, Len | Chunks]) -> %% 1.1 chunking
+ chunk(<<Head/binary, Bin/binary>>, Chunks, Len);
+
+%% Start looking for the terminating end-of-message sequence ]]>]]>
+%% 5 characters from the end of the buffered head, since this binary
+%% has already been scanned.
+recv(Bin, Head) when is_binary(Head) -> %% 1.0 framing
+ frame(<<Head/binary, Bin/binary>>, max(0, size(Head) - 5)).
+
+%% frame/2
+%%
+%% Extract a message terminated by the ]]>]]> end-of-message sequence.
+%% Don't need to extract characters as UTF-8 since matching byte-wise
+%% is unambiguous: the high-order bit of every byte of a multi-byte
+%% UTF character is 1, while the end-of-message sequence is ASCII.
+
+frame(Bin, Start) ->
+ Sz = size(Bin),
+ Scope = {Start, Sz - Start},
+ case binary:match(Bin, pattern(), [{scope, Scope}]) of
+ {Len, 6} ->
+ <<Msg:Len/binary, _:6/binary, Rest/binary>> = Bin,
+ {trim(Msg), Rest};
+ nomatch ->
+ Bin
+ end.
+
+%% pattern/0
+
+pattern() ->
+ Key = ?KEY(pattern),
+ case get(Key) of
+ undefined ->
+ CP = binary:compile_pattern(<<"]]>]]>">>),
+ put(Key, CP),
+ CP;
+ CP ->
+ CP
+ end.
+
+%% trim/1
+%%
+%% Whitespace before an XML declaration is an error, but be somewhat
+%% lenient and strip line breaks since the RFC's are unclear on what's
+%% allowed following a ]]>]]> delimiter. Typical seems to be a single
+%% $\n, but strip any of " \t\r\n", and regardless of NETCONF version.
+
+trim(<<C, Bin/binary>>)
+ when C == $\n;
+ C == $\r;
+ C == $\t;
+ C == $ ->
+ trim(Bin);
+
+trim(Bin) ->
+ Bin.
+
+%% chunk/3
+%%
+%% The final argument is either 0 to indicate that a specified number
+%% of bytes of chunk data should be consumed, or at least 3 to
+%% indicate an offset at which to look for a newline following a chunk
+%% size.
+
+%% Accumulating chunk-data ...
+chunk(Bin, [Sz | Chunks] = L, 0) ->
+ case Bin of
+ <<Chunk:Sz/binary, Rest/binary>> ->
+ chunk(Rest, acc(Chunk, Chunks), 3); %% complete chunk ...
+ _ ->
+ [Bin, 0 | L] %% ... or not
+ end;
+
+%% ... or a header.
+
+chunk(Bin, Chunks, Len)
+ when size(Bin) < 4 ->
+ [Bin, 3 = Len | Chunks];
+
+%% End of chunks.
+chunk(<<"\n##\n", Rest/binary>>, Chunks, _) ->
+ case Chunks of
+ [] ->
+ {error, "end-of-chunks unexpected"}; %% must be at least one
+ Bins ->
+ {lists:reverse(Bins), Rest}
+ end;
+
+%% Matching each of the 10 newline possibilities is faster than
+%% searching.
+chunk(<<"\n#", Head:1/binary, $\n, Rest/binary>>, Chunks, _) ->
+ acc(Head, Rest, Chunks);
+chunk(<<"\n#", Head:2/binary, $\n, Rest/binary>>, Chunks, _) ->
+ acc(Head, Rest, Chunks);
+chunk(<<"\n#", Head:3/binary, $\n, Rest/binary>>, Chunks, _) ->
+ acc(Head, Rest, Chunks);
+chunk(<<"\n#", Head:4/binary, $\n, Rest/binary>>, Chunks, _) ->
+ acc(Head, Rest, Chunks);
+chunk(<<"\n#", Head:5/binary, $\n, Rest/binary>>, Chunks, _) ->
+ acc(Head, Rest, Chunks);
+chunk(<<"\n#", Head:6/binary, $\n, Rest/binary>>, Chunks, _) ->
+ acc(Head, Rest, Chunks);
+chunk(<<"\n#", Head:7/binary, $\n, Rest/binary>>, Chunks, _) ->
+ acc(Head, Rest, Chunks);
+chunk(<<"\n#", Head:8/binary, $\n, Rest/binary>>, Chunks, _) ->
+ acc(Head, Rest, Chunks);
+chunk(<<"\n#", Head:9/binary, $\n, Rest/binary>>, Chunks, _) ->
+ acc(Head, Rest, Chunks);
+chunk(<<"\n#", Head:10/binary, $\n, Rest/binary>>, Chunks, _) ->
+ acc(Head, Rest, Chunks);
+
+chunk(<<"\n#", Bin:11/binary, _/binary>>, _, _) ->
+ {error, {"chunk-size too long", Bin}}; %% 32-bits = max 10 digits
+
+chunk(<<"\n#", _/binary>> = Bin, Chunks, _) ->
+ [Bin, size(Bin) | Chunks];
+
+chunk(Bin, Chunks, 3 = Len) ->
+ case drop(Bin) of
+ <<>> ->
+ [Bin, Len | Chunks];
+ <<"\n#", _/binary>> = B ->
+ chunk(B, Chunks, Len);
+ _ ->
+ {error, {"not a chunk", Bin}}
+ end.
+
+%% drop/1
+
+drop(<<"\n#", _/binary>> = Bin) ->
+ Bin;
+
+drop(<<C, Bin/binary>>)
+ when C == $\n;
+ C == $\r;
+ C == $\t;
+ C == $ ->
+ drop(Bin);
+
+drop(Bin) ->
+ Bin.
+
+%% acc/2
+
+acc(Chunk, []) ->
+ [B || B <- [trim(Chunk)], <<>> /= B];
+
+acc(Chunk, Chunks) ->
+ [Chunk | Chunks].
+
+%% acc/3
+
+acc(Head, Rest, Chunks) ->
+ case chunk_size(Head) of
+ {error, _Reason} = No ->
+ No;
+ Sz ->
+ chunk(Rest, [Sz | Chunks], 0)
+ end.
+
+%% chunk_size/1
+
+chunk_size(<<C, _/binary>> = Bin) ->
+ try true = $0 < C, binary_to_integer(Bin) of
+ Sz when 0 < Sz bsr 32 ->
+ {error, {"chunk-size too large", Sz}};
+ Sz ->
+ Sz
+ catch
+ error: _ ->
+ {error, {"chunk-size invalid", Bin}}
+ end.
%%----------------------------------------------------------------------
%% END OF MODULE
diff --git a/lib/common_test/src/ct_property_test.erl b/lib/common_test/src/ct_property_test.erl
index f51f454d08..d884a6d494 100644
--- a/lib/common_test/src/ct_property_test.erl
+++ b/lib/common_test/src/ct_property_test.erl
@@ -32,27 +32,34 @@
%% API
-export([init_per_suite/1,
- quickcheck/2]).
+ init_tool/1,
+ quickcheck/2]).
-include_lib("common_test/include/ct.hrl").
init_per_suite(Config) ->
+ case init_tool(Config) of
+ {skip, _}=Skip ->
+ Skip;
+ Config1 ->
+ Path = property_tests_path("property_test", Config1),
+ case compile_tests(Path, Config1) of
+ error ->
+ {fail, "Property test compilation failed in "++Path};
+ up_to_date ->
+ add_code_pathz(Path),
+ [{property_dir, Path} | Config1]
+ end
+ end.
+
+init_tool(Config) ->
case which_module_exists([eqc,proper,triq]) of
- {ok,ToolModule} ->
- ct:pal("Found property tester ~p",[ToolModule]),
- Path = property_tests_path("property_test", Config),
- case compile_tests(Path,ToolModule) of
- error ->
- {fail, "Property test compilation failed in "++Path};
- up_to_date ->
- add_code_pathz(Path),
- [{property_dir,Path},
- {property_test_tool,ToolModule} | Config]
- end;
-
- not_found ->
- ct:pal("No property tester found",[]),
- {skip, "No property testing tool found"}
+ {ok, ToolModule} ->
+ ct:pal("Found property tester ~p",[ToolModule]),
+ [{property_test_tool, ToolModule} | Config];
+ not_found ->
+ ct:pal("No property tester found",[]),
+ {skip, "No property testing tool found"}
end.
quickcheck(Property, Config) ->
@@ -105,7 +112,8 @@ add_code_pathz(Dir) ->
ok
end.
-compile_tests(Path, ToolModule) ->
+compile_tests(Path, Config) ->
+ ToolModule = proplists:get_value(property_test_tool, Config),
MacroDefs = macro_def(ToolModule),
{ok,Cwd} = file:get_cwd(),
ok = file:set_cwd(Path),
diff --git a/lib/common_test/src/ct_run.erl b/lib/common_test/src/ct_run.erl
index 960252a6fe..b028078332 100644
--- a/lib/common_test/src/ct_run.erl
+++ b/lib/common_test/src/ct_run.erl
@@ -27,13 +27,8 @@
-export([install/1,install/2,run/1,run/2,run/3,run_test/1,
run_testspec/1,step/3,step/4,refresh_logs/1]).
-
-%% Exported for VTS
--export([run_make/3,do_run/4,tests/1,tests/2,tests/3]).
-
-
-%% Misc internal functions
--export([variables_file_name/1,script_start1/2,run_test2/1]).
+%% Misc internal API functions
+-export([variables_file_name/1,script_start1/2,run_test2/1, run_make/3]).
-include("ct.hrl").
-include("ct_event.hrl").
@@ -51,7 +46,6 @@
-record(opts, {label,
profile,
- vts,
shell,
cover,
cover_stop,
@@ -212,25 +206,19 @@ finish(Tracing, ExitStatus, Args) ->
if ExitStatus == interactive_mode ->
interactive_mode;
true ->
- case get_start_opt(vts, true, Args) of
- true ->
- %% VTS mode, don't halt the node
- ok;
- _ ->
- %% it's possible to tell CT to finish execution with a call
- %% to a different function than the normal halt/1 BIF
- %% (meant to be used mainly for reading the CT exit status)
- case get_start_opt(halt_with,
- fun([HaltMod,HaltFunc]) ->
- {list_to_atom(HaltMod),
- list_to_atom(HaltFunc)} end,
- Args) of
- undefined ->
- halt(ExitStatus);
- {M,F} ->
- apply(M, F, [ExitStatus])
- end
- end
+ %% it's possible to tell CT to finish execution with a call
+ %% to a different function than the normal halt/1 BIF
+ %% (meant to be used mainly for reading the CT exit status)
+ case get_start_opt(halt_with,
+ fun([HaltMod,HaltFunc]) ->
+ {list_to_atom(HaltMod),
+ list_to_atom(HaltFunc)} end,
+ Args) of
+ undefined ->
+ halt(ExitStatus);
+ {M,F} ->
+ apply(M, F, [ExitStatus])
+ end
end.
script_start1(Parent, Args) ->
@@ -239,7 +227,6 @@ script_start1(Parent, Args) ->
%% read general start flags
Label = get_start_opt(label, fun([Lbl]) -> Lbl end, Args),
Profile = get_start_opt(profile, fun([Prof]) -> Prof end, Args),
- Vts = get_start_opt(vts, true, undefined, Args),
Shell = get_start_opt(shell, true, Args),
Cover = get_start_opt(cover, fun([CoverFile]) -> ?abs(CoverFile) end, Args),
CoverStop = get_start_opt(cover_stop,
@@ -325,8 +312,8 @@ script_start1(Parent, Args) ->
Stylesheet = get_start_opt(stylesheet,
fun([SS]) -> ?abs(SS) end, Args),
%% basic_html - used by ct_logs
- BasicHtml = case {Vts,proplists:get_value(basic_html, Args)} of
- {undefined,undefined} ->
+ BasicHtml = case proplists:get_value(basic_html, Args) of
+ undefined ->
application:set_env(common_test, basic_html, false),
undefined;
_ ->
@@ -357,7 +344,7 @@ script_start1(Parent, Args) ->
application:set_env(common_test, keep_logs, KeepLogs),
Opts = #opts{label = Label, profile = Profile,
- vts = Vts, shell = Shell,
+ shell = Shell,
cover = Cover, cover_stop = CoverStop,
logdir = LogDir, logopts = LogOpts,
basic_html = BasicHtml,
@@ -415,8 +402,7 @@ run_or_refresh(Opts = #opts{logdir = LogDir}, Args) ->
end
end.
-script_start2(Opts = #opts{vts = undefined,
- shell = undefined}, Args) ->
+script_start2(Opts = #opts{shell = undefined}, Args) ->
case proplists:get_value(spec, Args) of
Specs when Specs =/= [], Specs =/= undefined ->
Specs1 = get_start_opt(join_specs, [Specs], Specs, Args),
@@ -702,7 +688,7 @@ script_start3(Opts, Args) ->
{error,incorrect_start_options};
{undefined,undefined,_} ->
- if Opts#opts.vts ; Opts#opts.shell ->
+ if Opts#opts.shell ->
script_start4(Opts#opts{tests = []}, Args);
true ->
%% no start options, use default "-dir ./"
@@ -712,20 +698,6 @@ script_start3(Opts, Args) ->
end
end.
-script_start4(#opts{vts = true, config = Config, event_handlers = EvHandlers,
- tests = Tests, logdir = LogDir, logopts = LogOpts}, _Args) ->
- ConfigFiles =
- lists:foldl(fun({ct_config_plain,CfgFiles}, AllFiles) when
- is_list(hd(CfgFiles)) ->
- AllFiles ++ CfgFiles;
- ({ct_config_plain,CfgFile}, AllFiles) when
- is_integer(hd(CfgFile)) ->
- AllFiles ++ [CfgFile];
- (_, AllFiles) ->
- AllFiles
- end, [], Config),
- vts:init_data(ConfigFiles, EvHandlers, ?abs(LogDir), LogOpts, Tests);
-
script_start4(#opts{label = Label, profile = Profile,
shell = true, config = Config,
event_handlers = EvHandlers,
@@ -759,27 +731,6 @@ script_start4(#opts{label = Label, profile = Profile,
Error ->
Error
end;
-
-script_start4(#opts{vts = true, cover = Cover}, _) ->
- case Cover of
- undefined ->
- script_usage();
- _ ->
- %% Add support later (maybe).
- io:format("\nCan't run cover in vts mode.\n\n", [])
- end,
- {error,no_cover_in_vts_mode};
-
-script_start4(#opts{shell = true, cover = Cover}, _) ->
- case Cover of
- undefined ->
- script_usage();
- _ ->
- %% Add support later (maybe).
- io:format("\nCan't run cover in interactive mode.\n\n", [])
- end,
- {error,no_cover_in_interactive_mode};
-
script_start4(Opts = #opts{tests = Tests}, Args) ->
do_run(Tests, [], Opts, Args).
@@ -850,7 +801,6 @@ script_usage() ->
"\n\t [-config ConfigFile1 ConfigFile2 .. ConfigFileN]"
"\n\t [-decrypt_key Key] | [-decrypt_file KeyFile]\n\n"),
io:format("Run tests in web based GUI:\n\n"
- "\tct_run -vts [-browser Browser]"
"\n\t [-config ConfigFile1 ConfigFile2 .. ConfigFileN]"
"\n\t [-decrypt_key Key] | [-decrypt_file KeyFile]"
"\n\t [-dir TestDir1 TestDir2 .. TestDirN] |"
@@ -2674,7 +2624,6 @@ get_name(Dir) ->
TopDir ++ "." ++ Base
end.
-
run_make(TestDir, Mod, UserInclude) ->
run_make(suites, TestDir, Mod, UserInclude, [nowarn_export_all]).
diff --git a/lib/common_test/src/ct_util.erl b/lib/common_test/src/ct_util.erl
index 9f489e9bfb..470a938c53 100644
--- a/lib/common_test/src/ct_util.erl
+++ b/lib/common_test/src/ct_util.erl
@@ -156,12 +156,7 @@ do_start(Parent, Mode, LogDir, Verbosity) ->
{error,{already_started,_}} ->
ok;
_ ->
- case whereis(vts) of
- undefined ->
- ct_event:add_handler();
- VtsPid ->
- ct_event:add_handler([{vts,VtsPid}])
- end
+ ct_event:add_handler()
end,
%% start ct_config server
diff --git a/lib/common_test/src/ct_webtool.erl b/lib/common_test/src/ct_webtool.erl
deleted file mode 100644
index 32d4255217..0000000000
--- a/lib/common_test/src/ct_webtool.erl
+++ /dev/null
@@ -1,1214 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2001-2018. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
--module(ct_webtool).
--behaviour(gen_server).
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% %%
-%% The general idea is: %%
-%% %%
-%% %%
-%% 1. Scan through the path for *.tool files and find all the web %%
-%% based tools. Query each tool for configuration data. %%
-%% 2. Add Alias for Erlscript and html for each tool to %%
-%% the webserver configuration data. %%
-%% 3. Start the webserver. %%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-%% API functions
--export([start/0, start/2, stop/0]).
-
-%% Starting Webtool from a shell script
--export([script_start/0, script_start/1]).
-
-%% Web api
--export([started_tools/2, toolbar/2, start_tools/2, stop_tools/2]).
-
-%% API against other tools
--export([is_localhost/0]).
-
-%% Debug export s
--export([get_tools1/1]).
--export([debug/1, stop_debug/0, debug_app/1]).
-
-%% gen_server callbacks
--export([init/1, handle_call/3, handle_cast/2, handle_info/2,
- terminate/2, code_change/3]).
-
--include_lib("kernel/include/file.hrl").
--include_lib("stdlib/include/ms_transform.hrl").
-
--record(state,{priv_dir,app_data,supvis,web_data,started=[]}).
-
--define(MAX_NUMBER_OF_WEBTOOLS,256).
--define(DEFAULT_PORT,8888).% must be >1024 or the user must be root on unix
--define(DEFAULT_ADDR,{127,0,0,1}).
-
--define(WEBTOOL_ALIAS,{ct_webtool,[{alias,{erl_alias,"/ct_webtool",[ct_webtool]}}]}).
--define(HEADER,"Pragma:no-cache\r\n Content-type: text/html\r\n\r\n").
--define(HTML_HEADER,"<HTML>\r\n<HEAD>\r\n<TITLE>WebTool</TITLE>\r\n</HEAD>\r\n<BODY BGCOLOR=\"#FFFFFF\">\r\n").
--define(HTML_HEADER_RELOAD,"<HTML>\r\n<HEAD>\r\n<TITLE>WebTool
- </TITLE>\r\n</HEAD>\r\n
- <BODY BGCOLOR=\"#FFFFFF\" onLoad=reloadCompiledList()>\r\n").
-
--define(HTML_END,"</BODY></HTML>").
-
--define(SEND_URL_TIMEOUT,5000).
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% %%
-%% For debugging only. %%
-%% %%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% Start tracing with
-%% debug(Functions).
-%% Functions = local | global | FunctionList
-%% FunctionList = [Function]
-%% Function = {FunctionName,Arity} | FunctionName |
-%% {Module, FunctionName, Arity} | {Module,FunctionName}
-debug(F) ->
- {ok, _} = ttb:tracer(all,[{file,"webtool.trc"}]), % tracing all nodes
- {ok, _} = ttb:p(all,[call,timestamp]),
- MS = [{'_',[],[{return_trace},{message,{caller}}]}],
- _ = tp(F,MS),
- {ok, _} = ttb:ctp(?MODULE,stop_debug), % don't want tracing of the stop_debug func
- ok.
-tp(local,MS) -> % all functions
- ttb:tpl(?MODULE,MS);
-tp(global,MS) -> % all exported functions
- ttb:tp(?MODULE,MS);
-tp([{M,F,A}|T],MS) -> % Other module
- {ok, _} = ttb:tpl(M,F,A,MS),
- tp(T,MS);
-tp([{M,F}|T],MS) when is_atom(F) -> % Other module
- {ok, _} = ttb:tpl(M,F,MS),
- tp(T,MS);
-tp([{F,A}|T],MS) -> % function/arity
- {ok, _} = ttb:tpl(?MODULE,F,A,MS),
- tp(T,MS);
-tp([F|T],MS) -> % function
- {ok, _} = ttb:tpl(?MODULE,F,MS),
- tp(T,MS);
-tp([],_MS) ->
- ok.
-stop_debug() ->
- ttb:stop([format]).
-
-debug_app(Mod) ->
- {ok, _} = ttb:tracer(all,[{file,"webtool_app.trc"},{handler,{fun out/4,true}}]),
- {ok, _} = ttb:p(all,[call,timestamp]),
- MS = [{'_',[],[{return_trace},{message,{caller}}]}],
- {ok, _} = ttb:tp(Mod,MS),
- ok.
-
-out(_,{trace_ts,Pid,call,MFA={M,F,A},{W,_,_},TS},_,S)
- when W==webtool;W==mod_esi->
- io:format("~w: (~p)~ncall ~ts~n", [TS,Pid,ffunc(MFA)]),
- [{M,F,length(A)}|S];
-out(_,{trace_ts,Pid,return_from,MFA,R,TS},_,[MFA|S]) ->
- io:format("~w: (~p)~nreturned from ~ts -> ~tp~n", [TS,Pid,ffunc(MFA),R]),
- S;
-out(_,_,_,_) ->
- ok.
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% %%
-%% Functions called via script. %%
-%% %%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-script_start() ->
- usage(),
- halt().
-script_start([App]) ->
- DefaultBrowser =
- case os:type() of
- {win32,_} -> iexplore;
- _ -> firefox
- end,
- script_start([App,DefaultBrowser]);
-script_start([App,Browser]) ->
- io:format("Starting webtool...\n"),
- {ok, _} = start(),
- AvailableApps = get_applications(),
- {OSType,_} = os:type(),
- case lists:keysearch(App,1,AvailableApps) of
- {value,{App,StartPage}} ->
- io:format("Starting ~w...\n",[App]),
- start_tools([],"app=" ++ atom_to_list(App)),
- PortStr = integer_to_list(get_port()),
- Url = case StartPage of
- "/" ++ Page ->
- "http://localhost:" ++ PortStr ++ "/" ++ Page;
- _ ->
- "http://localhost:" ++ PortStr ++ "/" ++ StartPage
- end,
- _ = case Browser of
- none ->
- ok;
- iexplore when OSType == win32->
- io:format("Starting internet explorer...\n"),
- {ok,R} = win32reg:open(""),
- Key="\\local_machine\\SOFTWARE\\Microsoft\\IE Setup\\Setup",
- ok = win32reg:change_key(R,Key),
- {ok,Val} = win32reg:value(R,"Path"),
- IExplore=filename:join(win32reg:expand(Val),"iexplore.exe"),
- os:cmd("\"" ++ IExplore ++ "\" " ++ Url);
- _ when OSType == win32 ->
- io:format("Starting ~tw...\n",[Browser]),
- os:cmd("\"" ++ atom_to_list(Browser) ++ "\" " ++ Url);
- B when B==firefox; B==mozilla ->
- io:format("Sending URL to ~w...",[Browser]),
- BStr = atom_to_list(Browser),
- SendCmd = BStr ++ " -raise -remote \'openUrl(" ++
- Url ++ ")\'",
- Port = open_port({spawn,SendCmd},[exit_status]),
- receive
- {Port,{exit_status,0}} ->
- io:format("done\n"),
- ok;
- {Port,{exit_status,_Error}} ->
- io:format(" not running, starting ~w...\n",
- [Browser]),
- _ = os:cmd(BStr ++ " " ++ Url),
- ok
- after ?SEND_URL_TIMEOUT ->
- io:format(" failed, starting ~w...\n",[Browser]),
- erlang:port_close(Port),
- os:cmd(BStr ++ " " ++ Url)
- end;
- _ ->
- io:format("Starting ~tw...\n",[Browser]),
- os:cmd(atom_to_list(Browser) ++ " " ++ Url)
- end,
- ok;
- false ->
- stop(),
- io:format("\n{error,{unknown_app,~p}}\n",[App]),
- halt()
- end.
-
-usage() ->
- io:format("Starting webtool...\n"),
- {ok, _} = start(),
- Apps = lists:map(fun({A,_}) -> A end,get_applications()),
- io:format(
- "\nUsage: start_webtool application [ browser ]\n"
- "\nAvailable applications are: ~p\n"
- "Default browser is \'iexplore\' (Internet Explorer) on Windows "
- "or else \'firefox\'\n",
- [Apps]),
- stop().
-
-
-get_applications() ->
- gen_server:call(ct_web_tool,get_applications).
-
-get_port() ->
- gen_server:call(ct_web_tool,get_port).
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% %%
-%% Api functions to the genserver. %%
-%% %%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%----------------------------------------------------------------------
-%
-%----------------------------------------------------------------------
-
-start()->
- start(standard_path,standard_data).
-
-start(Path,standard_data)->
- case get_standard_data() of
- {error,Reason} ->
- {error,Reason};
- Data ->
- start(Path,Data)
- end;
-
-start(standard_path,Data)->
- Path=get_path(),
- start(Path,Data);
-
-start(Path,Port) when is_integer(Port)->
- Data = get_standard_data(Port),
- start(Path,Data);
-
-start(Path,Data0)->
- Data = Data0 ++ rest_of_standard_data(),
- case gen_server:start({local,ct_web_tool},ct_webtool,{Path,Data},[]) of
- {error, {already_started, Pid}} ->
- {ok, Pid};
- Else ->
- Else
- end.
-
-stop()->
- gen_server:call(ct_web_tool,stoppit).
-
-%----------------------------------------------------------------------
-%Web Api functions called by the web
-%----------------------------------------------------------------------
-started_tools(Env,Input)->
- gen_server:call(ct_web_tool,{started_tools,Env,Input}).
-
-toolbar(Env,Input)->
- gen_server:call(ct_web_tool,{toolbar,Env,Input}).
-
-start_tools(Env,Input)->
- gen_server:call(ct_web_tool,{start_tools,Env,Input}).
-
-stop_tools(Env,Input)->
- gen_server:call(ct_web_tool,{stop_tools,Env,Input}).
-%----------------------------------------------------------------------
-%Support API for other tools
-%----------------------------------------------------------------------
-
-is_localhost()->
- gen_server:call(ct_web_tool,is_localhost).
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% %%
-%%The gen_server callback functions that builds the webbpages %%
-%% %%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-handle_call(get_applications,_,State)->
- MS = ets:fun2ms(fun({Tool,{web_data,{_,Start}}}) -> {Tool,Start} end),
- Tools = ets:select(State#state.app_data,MS),
- {reply,Tools,State};
-
-handle_call(get_port,_,State)->
- {value,{port,Port}}=lists:keysearch(port,1,State#state.web_data),
- {reply,Port,State};
-
-handle_call({started_tools,_Env,_Input},_,State)->
- {reply,started_tools_page(State),State};
-
-handle_call({toolbar,_Env,_Input},_,State)->
- {reply,toolbar(),State};
-
-handle_call({start_tools,Env,Input},_,State)->
- {NewState,Page}=start_tools_page(Env,Input,State),
- {reply,Page,NewState};
-
-handle_call({stop_tools,Env,Input},_,State)->
- {NewState,Page}=stop_tools_page(Env,Input,State),
- {reply,Page,NewState};
-
-handle_call(stoppit,_From,Data)->
- {stop,normal,ok,Data};
-
-handle_call(is_localhost,_From,Data)->
- Result=case proplists:get_value(bind_address, Data#state.web_data) of
- ?DEFAULT_ADDR ->
- true;
- _IpNumber ->
- false
- end,
- {reply,Result,Data}.
-
-
-handle_info(_Message,State)->
- {noreply,State}.
-
-handle_cast(_Request,State)->
- {noreply,State}.
-
-code_change(_,State,_)->
- {ok,State}.
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%
-% The other functions needed by the gen_server behaviour
-%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%----------------------------------------------------------------------
-% Start the gen_server
-%----------------------------------------------------------------------
-init({Path,Config})->
- ct_util:mark_process(),
- case filelib:is_dir(Path) of
- true ->
- {ok, Table} = get_tool_files_data(),
- insert_app(?WEBTOOL_ALIAS, Table),
- case ct_webtool_sup:start_link() of
- {ok, Pid} ->
- case start_webserver(Table, Path, Config) of
- {ok, _} ->
- print_url(Config),
- {ok,#state{priv_dir=Path,
- app_data=Table,
- supvis=Pid,
- web_data=Config}};
- {error, Error} ->
- {stop, {error, Error}}
- end;
- Error ->
- {stop,Error}
- end;
- false ->
- {stop, {error, error_dir}}
- end.
-
-terminate(_Reason,Data)->
- %%shut down the webbserver
- shutdown_server(Data),
- %%Shutdown the different tools that are started with application:start
- shutdown_apps(Data),
- %%Shutdown the supervisor and its children will die
- shutdown_supervisor(Data),
- ok.
-
-print_url(ConfigData)->
- Server=proplists:get_value(server_name,ConfigData,"undefined"),
- Port=proplists:get_value(port,ConfigData,"undefined"),
- {A,B,C,D}=proplists:get_value(bind_address,ConfigData,"undefined"),
- io:format("WebTool is available at http://~ts:~w/~n",[Server,Port]),
- io:format("Or http://~w.~w.~w.~w:~w/~n",[A,B,C,D,Port]).
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%
-% begin build the pages
-%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-%----------------------------------------------------------------------
-%The page that shows the started tools
-%----------------------------------------------------------------------
-started_tools_page(State)->
- [?HEADER,?HTML_HEADER,started_tools(State),?HTML_END].
-
-toolbar()->
- [?HEADER,?HTML_HEADER,toolbar_page(),?HTML_END].
-
-
-start_tools_page(_Env,Input,State)->
- %%io:format("~n======= ~n ~p ~n============~n",[Input]),
- case get_tools(Input) of
- {tools,Tools}->
- %%io:format("~n======= ~n ~p ~n============~n",[Tools]),
- {ok,NewState}=handle_apps(Tools,State,start),
- {NewState,[?HEADER,?HTML_HEADER_RELOAD,reload_started_apps(),
- show_unstarted_apps(NewState),?HTML_END]};
- _ ->
- {State,[?HEADER,?HTML_HEADER,show_unstarted_apps(State),?HTML_END]}
- end.
-
-stop_tools_page(_Env,Input,State)->
- case get_tools(Input) of
- {tools,Tools}->
- {ok,NewState}=handle_apps(Tools,State,stop),
- {NewState,[?HEADER,?HTML_HEADER_RELOAD,reload_started_apps(),
- show_started_apps(NewState),?HTML_END]};
- _ ->
- {State,[?HEADER,?HTML_HEADER,show_started_apps(State),?HTML_END]}
- end.
-
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%%
-%% Functions that start and config the webserver
-%% 1. Collect the config data
-%% 2. Start webserver
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-%----------------------------------------------------------------------
-% Start the webserver
-%----------------------------------------------------------------------
-start_webserver(Data,Path,Config)->
- case get_conf_data(Data,Path,Config) of
- {ok,Conf_data}->
- %%io:format("Conf_data: ~p~n",[Conf_data]),
- start_server(Conf_data);
- {error,Error} ->
- {error,{error_server_conf_file,Error}}
- end.
-
-start_server(Conf_data)->
- case inets:start(httpd, Conf_data, stand_alone) of
- {ok,Pid}->
- {ok,Pid};
- Error->
- {error,{server_error,Error}}
- end.
-
-%----------------------------------------------------------------------
-% Create config data for the webserver
-%----------------------------------------------------------------------
-get_conf_data(Data,Path,Config)->
- Aliases=get_aliases(Data),
- ServerRoot = filename:join([Path,"root"]),
- MimeTypesFile = filename:join([ServerRoot,"conf","mime.types"]),
- case httpd_conf:load_mime_types(MimeTypesFile) of
- {ok,MimeTypes} ->
- Config1 = Config ++ Aliases,
- Config2 = [{server_root,ServerRoot},
- {document_root,filename:join([Path,"root/doc"])},
- {mime_types,MimeTypes} |
- Config1],
- {ok,Config2};
- Error ->
- Error
- end.
-
-%----------------------------------------------------------------------
-% Control the path for *.tools files
-%----------------------------------------------------------------------
-get_tool_files_data()->
- Tools=get_tools1(code:get_path()),
- %%io:format("Data : ~p ~n",[Tools]),
- get_file_content(Tools).
-
-%----------------------------------------------------------------------
-%Control that the data in the file really is erlang terms
-%----------------------------------------------------------------------
-get_file_content(Tools)->
- Get_data=fun({tool,ToolData}) ->
- %%io:format("Data : ~p ~n",[ToolData]),
- case proplists:get_value(config_func,ToolData) of
- {M,F,A}->
- case catch apply(M,F,A) of
- {'EXIT',_} ->
- bad_data;
- Data when is_tuple(Data) ->
- Data;
- _->
- bad_data
- end;
- _ ->
- bad_data
- end
- end,
- insert_file_content([X ||X<-lists:map(Get_data,Tools),X/=bad_data]).
-
-%----------------------------------------------------------------------
-%Insert the data from the file in to the ets:table
-%----------------------------------------------------------------------
-insert_file_content(Content)->
- Table=ets:new(app_data,[bag]),
- lists:foreach(fun(X)->
- insert_app(X,Table)
- end,Content),
- {ok,Table}.
-
-%----------------------------------------------------------------------
-%Control that we got a a tuple of a atom and a list if so add the
-%elements in the list to the ets:table
-%----------------------------------------------------------------------
-insert_app({Name,Key_val_list},Table) when is_list(Key_val_list),is_atom(Name)->
- %%io:format("ToolData: ~p: ~p~n",[Name,Key_val_list]),
- lists:foreach(
- fun({alias,{erl_alias,Alias,Mods}}) ->
- Key_val = {erl_script_alias,{Alias,Mods}},
- %%io:format("Insert: ~p~n",[Key_val]),
- ets:insert(Table,{Name,Key_val});
- (Key_val_pair)->
- %%io:format("Insert: ~p~n",[Key_val_pair]),
- ets:insert(Table,{Name,Key_val_pair})
- end,
- Key_val_list);
-
-insert_app(_,_)->
- ok.
-
-%----------------------------------------------------------------------
-% Select all the alias in the database
-%----------------------------------------------------------------------
-get_aliases(Data)->
- MS = ets:fun2ms(fun({_,{erl_script_alias,Alias}}) ->
- {erl_script_alias,Alias};
- ({_,{alias,Alias}}) ->
- {alias,Alias}
- end),
- ets:select(Data,MS).
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% %%
-%% Helper functions %%
-%% %%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-get_standard_data(Port)->
- [
- {port,Port},
- {bind_address,?DEFAULT_ADDR},
- {server_name,"localhost"}
- ].
-
-get_standard_data()->
- case get_free_port(?DEFAULT_PORT,?MAX_NUMBER_OF_WEBTOOLS) of
- {error,Reason} -> {error,Reason};
- Port ->
- [
- {port,Port},
- {bind_address,?DEFAULT_ADDR},
- {server_name,"localhost"}
- ]
- end.
-
-get_free_port(_Port,0) ->
- {error,no_free_port_found};
-get_free_port(Port,N) ->
- case gen_tcp:connect("localhost",Port,[]) of
- {error, _Reason} ->
- Port;
- {ok,Sock} ->
- gen_tcp:close(Sock),
- get_free_port(Port+1,N-1)
- end.
-
-rest_of_standard_data() ->
- [
- %% Do not allow the server to be crashed by malformed http-request
- {max_header_siz,1024},
- {max_header_action,reply414},
- %% Go on a straight ip-socket
- {com_type,ip_comm},
- %% Do not change the order of these module names!!
- {modules,[mod_alias,
- mod_auth,
- mod_esi,
- mod_actions,
- mod_cgi,
- mod_include,
- mod_dir,
- mod_get,
- mod_head,
- mod_log,
- mod_disk_log]},
- {directory_index,["index.html"]},
- {default_type,"text/plain"}
- ].
-
-
-get_path()->
- code:priv_dir(webtool).
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-% These functions is used to shutdown the webserver
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-%----------------------------------------------------------------------
-% Shut down the webbserver
-%----------------------------------------------------------------------
-shutdown_server(State)->
- {Addr,Port} = get_addr_and_port(State#state.web_data),
- inets:stop(httpd,{Addr,Port}).
-
-get_addr_and_port(Config) ->
- Addr = proplists:get_value(bind_address,Config,?DEFAULT_ADDR),
- Port = proplists:get_value(port,Config,?DEFAULT_PORT),
- {Addr,Port}.
-
-%----------------------------------------------------------------------
-% Select all apps in the table and close them
-%----------------------------------------------------------------------
-shutdown_apps(State)->
- Data=State#state.app_data,
- MS = ets:fun2ms(fun({_,{start,HowToStart}}) -> HowToStart end),
- lists:foreach(fun(Start_app)->
- stop_app(Start_app)
- end,
- ets:select(Data,MS)).
-
-%----------------------------------------------------------------------
-%Shuts down the supervisor that supervises tools that is not
-%Designed as applications
-%----------------------------------------------------------------------
-shutdown_supervisor(State)->
- %io:format("~n==================~n"),
- ct_webtool_sup:stop(State#state.supvis).
- %io:format("~n==================~n").
-
-%----------------------------------------------------------------------
-%close the individual apps.
-%----------------------------------------------------------------------
-stop_app({child,_Real_name})->
- ok;
-
-stop_app({app,Real_name})->
- application:stop(Real_name);
-
-stop_app({func,_Start,Stop})->
- case Stop of
- {M,F,A} ->
- catch apply(M,F,A);
- _NoStop ->
- ok
- end.
-
-
-
-
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%%
-%% These functions creates the webpage where the user can select if
-%% to start apps or to stop apps
-%%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-toolbar_page()->
- "<TABLE>
- <TR>
- <TD>
- <B>Select Action</B>
- </TD>
- </TR>
- <TR>
- <TD>
- <A HREF=\"./start_tools\" TARGET=right> Start Tools</A>
- </TD>
- </TR>
- <TR>
- <TD>
- <A HREF=\"./stop_tools\" TARGET=right> Stop Tools</A>
- </TD>
- </TR>
- </TABLE>".
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%%
-%% These functions creates the webbpage that shows the started apps
-%%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-%----------------------------------------------------------------------
-% started_tools(State)->String (html table)
-% State is a record of type state
-%----------------------------------------------------------------------
-started_tools(State)->
- Names=get_started_apps(State#state.app_data,State#state.started),
- "<TABLE BORDER=1 WIDTH=100%>
- "++ make_rows(Names,[],0) ++"
- </TABLE>".
-%----------------------------------------------------------------------
-%get_started_apps(Data,Started)-> [{web_name,link}]
-%selects the started apps from the ets table of apps.
-%----------------------------------------------------------------------
-
-get_started_apps(Data,Started)->
- SelectData=fun({Name,Link}) ->
- {Name,Link}
- end,
- MS = lists:map(fun(A) -> {{A,{web_data,'$1'}},[],['$1']} end,Started),
-
- [{"WebTool","/tool_management.html"} |
- [SelectData(X) || X <- ets:select(Data,MS)]].
-
-%----------------------------------------------------------------------
-% make_rows(List,Result,Fields)-> String (The rows of a htmltable
-% List a list of tupler discibed above
-% Result an accumulator for the result
-% Field, counter that counts the number of cols in each row.
-%----------------------------------------------------------------------
-make_rows([],Result,Fields)->
- Result ++ fill_out(Fields);
-make_rows([Data|Paths],Result,Field)when Field==0->
- make_rows(Paths,Result ++ "<TR>" ++ make_field(Data),Field+1);
-
-make_rows([Path|Paths],Result,Field)when Field==4->
- make_rows(Paths,Result ++ make_field(Path) ++ "</TR>",0);
-
-make_rows([Path|Paths],Result,Field)->
- make_rows(Paths,Result ++ make_field(Path),Field+1).
-
-%----------------------------------------------------------------------
-% make_fields(Path)-> String that is a field i a html table
-% Path is a name url tuple {Name,url}
-%----------------------------------------------------------------------
-make_field(Path)->
- "<TD WIDTH=20%>" ++ get_name(Path) ++ "</TD>".
-
-
-%----------------------------------------------------------------------
-%get_name({Nae,Url})->String that represents a <A> tag in html.
-%----------------------------------------------------------------------
-get_name({Name,Url})->
- "<A HREF=\"" ++ Url ++ "\" TARGET=app_frame>" ++ Name ++ "</A>".
-
-
-%----------------------------------------------------------------------
-% fill_out(Nr)-> String, that represent Nr fields in a html-table.
-%----------------------------------------------------------------------
-fill_out(Nr)when Nr==0->
- [];
-fill_out(Nr)when Nr==4->
- "<TD WIDTH=\"20%\" >&nbsp</TD></TR>";
-
-fill_out(Nr)->
- "<TD WIDTH=\"20%\">&nbsp</TD>" ++ fill_out(Nr+1).
-
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%%
-%%These functions starts applicatons and builds the page showing tools
-%%to start
-%%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%----------------------------------------------------------------------
-%Controls whether the user selected a tool to start
-%----------------------------------------------------------------------
-get_tools(Input)->
- case uri_string:dissect_query(Input) of
- []->
- no_tools;
- Tools->
- FormatData=fun({_Name,Data}) -> list_to_atom(Data) end,
- SelectData=
- fun({Name,_Data}) -> string:equal(Name,"app") end,
- {tools,[FormatData(X)||X<-Tools,SelectData(X)]}
- end.
-
-%----------------------------------------------------------------------
-% Selects the data to start the applications the user has ordered
-% starting of
-%----------------------------------------------------------------------
-handle_apps([],State,_Cmd)->
- {ok,State};
-
-handle_apps([Tool|Tools],State,Cmd)->
- case ets:match_object(State#state.app_data,{Tool,{start,'_'}}) of
- []->
- Started = case Cmd of
- start ->
- [Tool|State#state.started];
- stop ->
- lists:delete(Tool,State#state.started)
- end,
- {ok,#state{priv_dir=State#state.priv_dir,
- app_data=State#state.app_data,
- supvis=State#state.supvis,
- web_data=State#state.web_data,
- started=Started}};
- ToStart ->
- case handle_apps2(ToStart,State,Cmd) of
- {ok,NewState}->
- handle_apps(Tools,NewState,Cmd);
- _->
- handle_apps(Tools,State,Cmd)
- end
- end.
-
-%----------------------------------------------------------------------
-%execute every start or stop data about a tool.
-%----------------------------------------------------------------------
-handle_apps2([{Name,Start_data}],State,Cmd)->
- case handle_app({Name,Start_data},State#state.app_data,State#state.supvis,Cmd) of
- ok->
- Started = case Cmd of
- start ->
- [Name|State#state.started];
- stop ->
-
- lists:delete(Name,State#state.started)
- end,
- {ok,#state{priv_dir=State#state.priv_dir,
- app_data=State#state.app_data,
- supvis=State#state.supvis,
- web_data=State#state.web_data,
- started=Started}};
- _->
- error
- end;
-
-handle_apps2([{Name,Start_data}|Rest],State,Cmd)->
- case handle_app({Name,Start_data},State#state.app_data,State#state.supvis,Cmd)of
- ok->
- handle_apps2(Rest,State,Cmd);
- _->
- error
- end.
-
-
-%----------------------------------------------------------------------
-% Handle start and stop of applications
-%----------------------------------------------------------------------
-
-handle_app({Name,{start,{func,Start,Stop}}},Data,_Pid,Cmd)->
- Action = case Cmd of
- start ->
- Start;
- _ ->
- Stop
- end,
- case Action of
- {M,F,A} ->
- case catch apply(M,F,A) of
- {'EXIT',_} = Exit->
- %%! Here the tool disappears from the webtool interface!!
- io:format("\n=======ERROR (webtool, line ~w) =======\n"
- "Could not start application \'~p\'\n\n"
- "~w:~tw(~ts) ->\n"
- "~tp\n\n",
- [?LINE,Name,M,F,format_args(A),Exit]),
- ets:delete(Data,Name);
- _OK->
- ok
- end;
- _NoStart ->
- ok
- end;
-
-
-handle_app({Name,{start,{child,ChildSpec}}},Data,Pid,Cmd)->
- case Cmd of
- start ->
- case catch supervisor:start_child(Pid,ChildSpec) of
- {ok,_}->
- ok;
- {ok,_,_}->
- ok;
- {error,Reason}->
- %%! Here the tool disappears from the webtool interface!!
- io:format("\n=======ERROR (webtool, line ~w) =======\n"
- "Could not start application \'~p\'\n\n"
- "supervisor:start_child(~p,~tp) ->\n"
- "~tp\n\n",
- [?LINE,Name,Pid,ChildSpec,{error,Reason}]),
- ets:delete(Data,Name);
- Error ->
- %%! Here the tool disappears from the webtool interface!!
- io:format("\n=======ERROR (webtool, line ~w) =======\n"
- "Could not start application \'~p\'\n\n"
- "supervisor:start_child(~p,~tp) ->\n"
- "~tp\n\n",
- [?LINE,Name,Pid,ChildSpec,Error]),
- ets:delete(Data,Name)
- end;
- stop ->
- case catch supervisor:terminate_child(websup,element(1,ChildSpec)) of
- ok ->
- supervisor:delete_child(websup,element(1,ChildSpec));
- _ ->
- error
- end
- end;
-
-
-
-handle_app({Name,{start,{app,Real_name}}},Data,_Pid,Cmd)->
- case Cmd of
- start ->
- case application:start(Real_name,temporary) of
- ok->
- io:write(Name),
- ok;
- {error,{already_started,_}}->
- %% Remove it from the database so we dont start
- %% anything already started
- ets:match_delete(Data,{Name,{start,{app,Real_name}}}),
- ok;
- {error,_Reason}=Error->
- %%! Here the tool disappears from the webtool interface!!
- io:format("\n=======ERROR (webtool, line ~w) =======\n"
- "Could not start application \'~p\'\n\n"
- "application:start(~p,~p) ->\n"
- "~tp\n\n",
- [?LINE,Name,Real_name,temporary,Error]),
- ets:delete(Data,Name)
- end;
-
- stop ->
- application:stop(Real_name)
- end;
-
-%----------------------------------------------------------------------
-% If the data is incorrect delete the app
-%----------------------------------------------------------------------
-handle_app({Name,Incorrect},Data,_Pid,Cmd)->
- %%! Here the tool disappears from the webtool interface!!
- io:format("\n=======ERROR (webtool, line ~w) =======\n"
- "Could not ~w application \'~p\'\n\n"
- "Incorrect data: ~tp\n\n",
- [?LINE,Cmd,Name,Incorrect]),
- ets:delete(Data,Name).
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% %%
-%% this functions creates the page that shows the unstarted tools %%
-%% %%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-reload_started_apps()->
- "<script>
- function reloadCompiledList()
- {
- parent.parent.top1.document.location.href=\"/webtool/webtool/started_tools\";
- }
- </script>".
-
-show_unstarted_apps(State)->
- "<TABLE HEIGHT=100% WIDTH=100% BORDER=0>
- <TR HEIGHT=80%><TD ALIGN=\"center\" VALIGN=\"middle\">
- <FORM NAME=\"stop_apps\" ACTION=\"/webtool/webtool/start_tools\" >
- <TABLE BORDER=1 WIDTH=60%>
- <TR BGCOLOR=\"#8899AA\">
- <TD ALIGN=CENTER COLSPAN=2><FONT SIZE=4>Available Tools<FONT></TD>
- </TR>
- <TR>
- <TD WIDTH=50%>
- <TABLE BORDER=0>
- "++ list_available_apps(State)++"
- <TR><TD COLSPAN=2>&nbsp;</TD></TR>
- <TR>
- <TD COLSPAN=2 ALIGN=\"center\">
- <INPUT TYPE=submit VALUE=\"Start\">
- </TD>
- </TR>
- </TABLE>
- </TD>
- <TD>
- To Start a Tool:
- <UL>
- <LI>Select the
- checkbox for each tool to
- start.</LI>
- <LI>Click on the
- button marked <EM>Start</EM>.</LI></UL>
- </TD>
- </TR>
- </TABLE>
- </FORM>
- </TD></TR>
- <TR><TD>&nbsp;</TD></TR>
- </TABLE>".
-
-
-
-list_available_apps(State)->
- MS = ets:fun2ms(fun({Tool,{web_data,{Name,_}}}) -> {Tool,Name} end),
- Unstarted_apps=
- lists:filter(
- fun({Tool,_})->
- false==lists:member(Tool,State#state.started)
- end,
- ets:select(State#state.app_data,MS)),
- case Unstarted_apps of
- []->
- "<TR><TD>All tools are started</TD></TR>";
- _->
- list_apps(Unstarted_apps)
- end.
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% %%
-%% these functions creates the page that shows the started apps %%
-%% the user can select to shutdown %%
-%% %%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-show_started_apps(State)->
- "<TABLE HEIGHT=100% WIDTH=100% BORDER=0>
- <TR HEIGHT=80%><TD ALIGN=\"center\" VALIGN=\"middle\">
- <FORM NAME=\"stop_apps\" ACTION=\"/webtool/webtool/stop_tools\" >
- <TABLE BORDER=1 WIDTH=60%>
- <TR BGCOLOR=\"#8899AA\">
- <TD ALIGN=CENTER COLSPAN=2><FONT SIZE=4>Started Tools<FONT></TD>
- </TR>
- <TR>
- <TD WIDTH=50%>
- <TABLE BORDER=0>
- "++ list_started_apps(State)++"
- <TR><TD COLSPAN=2>&nbsp;</TD></TR>
- <TR>
- <TD COLSPAN=2 ALIGN=\"center\">
- <INPUT TYPE=submit VALUE=\"Stop\">
- </TD>
- </TR>
- </TABLE>
- </TD>
- <TD>
- Stop a Tool:
- <UL>
- <LI>Select the
- checkbox for each tool to
- stop.</LI>
- <LI>Click on the
- button marked <EM>Stop</EM>.</LI></UL>
- </TD>
- </TR>
- </TABLE>
- </FORM>
- </TD></TR>
- <TR><TD>&nbsp;</TD></TR>
- </TABLE>".
-
-list_started_apps(State)->
- MS = lists:map(fun(A) -> {{A,{web_data,{'$1','_'}}},[],[{{A,'$1'}}]} end,
- State#state.started),
- Started_apps= ets:select(State#state.app_data,MS),
- case Started_apps of
- []->
- "<TR><TD>No tool is started yet.</TD></TR>";
- _->
- list_apps(Started_apps)
- end.
-
-
-list_apps(Apps) ->
- lists:map(fun({Tool,Name})->
- "<TR><TD>
- <INPUT TYPE=\"checkbox\" NAME=\"app\" VALUE=\""
- ++ atom_to_list(Tool) ++ "\">
- " ++ Name ++ "
- </TD></TR>"
- end,
- Apps).
-
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% %%
-%% Collecting the data from the *.tool files %%
-%% %%
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%----------------------------------------
-% get_tools(Dirs) => [{M,F,A},{M,F,A}...{M,F,A}]
-% Dirs - [string()] Directory names
-% Calls get_tools2/2 recursively for a number of directories
-% to retireve the configuration data for the web based tools.
-%----------------------------------------
-get_tools1(Dirs)->
- get_tools1(Dirs,[]).
-
-get_tools1([Dir|Rest],Data) when is_list(Dir) ->
- Tools=case filename:basename(Dir) of
- %% Dir is an 'ebin' directory, check in '../priv' as well
- "ebin" ->
- [get_tools2(filename:join(filename:dirname(Dir),"priv")) |
- get_tools2(Dir)];
- _ ->
- get_tools2(Dir)
- end,
- get_tools1(Rest,[Tools|Data]);
-
-get_tools1([],Data) ->
- lists:flatten(Data).
-
-%----------------------------------------
-% get_tools2(Directory) => DataList
-% DataList : [WebTuple]|[]
-% WebTuple: {tool,[{web,M,F,A}]}
-%
-%----------------------------------------
-get_tools2(Dir)->
- get_tools2(tool_files(Dir),[]).
-
-get_tools2([ToolFile|Rest],Data) ->
- case get_tools3(ToolFile) of
- {tool,WebData} ->
- get_tools2(Rest,[{tool,WebData}|Data]);
- {error,_Reason} ->
- get_tools2(Rest,Data);
- nodata ->
- get_tools2(Rest,Data)
- end;
-
-get_tools2([],Data) ->
- Data.
-
-%----------------------------------------
-% get_tools3(ToolFile) => {ok,Tool}|{error,Reason}|nodata
-% Tool: {tool,[KeyValTuple]}
-% ToolFile - string() A .tool file
-% Now we have the file get the data and sort it out
-%----------------------------------------
-get_tools3(ToolFile) ->
- case file:consult(ToolFile) of
- {error,open} ->
- {error,nofile};
- {error,read} ->
- {error,format};
- {ok,[{version,"1.2"},ToolInfo]} when is_list(ToolInfo)->
- webdata(ToolInfo);
- {ok,[{version,_Vsn},_Info]} ->
- {error,old_version};
- {ok,_Other} ->
- {error,format}
- end.
-
-
-%----------------------------------------------------------------------
-% webdata(TupleList)-> ToolTuple| nodata
-% ToolTuple: {tool,[{config_func,{M,F,A}}]}
-%
-% There are a little unneccesary work in this format but it is extendable
-%----------------------------------------------------------------------
-webdata(TupleList)->
- case proplists:get_value(config_func,TupleList,nodata) of
- {M,F,A} ->
- {tool,[{config_func,{M,F,A}}]};
- _ ->
- nodata
- end.
-
-
-%=============================================================================
-% Functions for getting *.tool configuration files
-%=============================================================================
-
-%----------------------------------------
-% tool_files(Dir) => ToolFiles
-% Dir - string() Directory name
-% ToolFiles - [string()]
-% Return the list of all files in Dir ending with .tool (appended to Dir)
-%----------------------------------------
-tool_files(Dir) ->
- case file:list_dir(Dir) of
- {ok,Files} ->
- filter_tool_files(Dir,Files);
- {error,_Reason} ->
- []
- end.
-
-%----------------------------------------
-% filter_tool_files(Dir,Files) => ToolFiles
-% Dir - string() Directory name
-% Files, ToolFiles - [string()] File names
-% Filters out the files in Files ending with .tool and append them to Dir
-%----------------------------------------
-filter_tool_files(_Dir,[]) ->
- [];
-filter_tool_files(Dir,[File|Rest]) ->
- case filename:extension(File) of
- ".tool" ->
- [filename:join(Dir,File)|filter_tool_files(Dir,Rest)];
- _ ->
- filter_tool_files(Dir,Rest)
- end.
-
-
-%%%-----------------------------------------------------------------
-%%% format functions
-ffunc({M,F,A}) when is_list(A) ->
- io_lib:format("~w:~tw(~ts)\n",[M,F,format_args(A)]);
-ffunc({M,F,A}) when is_integer(A) ->
- io_lib:format("~w:~tw/~w\n",[M,F,A]).
-
-format_args([]) ->
- "";
-format_args(Args) ->
- Str = lists:append(["~tp"|lists:duplicate(length(Args)-1,",~tp")]),
- io_lib:format(Str,Args).
diff --git a/lib/common_test/src/ct_webtool_sup.erl b/lib/common_test/src/ct_webtool_sup.erl
deleted file mode 100644
index 04fbbf8745..0000000000
--- a/lib/common_test/src/ct_webtool_sup.erl
+++ /dev/null
@@ -1,76 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2001-2018. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
--module(ct_webtool_sup).
-
--behaviour(supervisor).
-
-%% External exports
--export([start_link/0,stop/1]).
-
-%% supervisor callbacks
--export([init/1]).
-
-%%%----------------------------------------------------------------------
-%%% API
-%%%----------------------------------------------------------------------
-start_link() ->
- supervisor:start_link({local,ct_websup},ct_webtool_sup, []).
-
-stop(Pid)->
- exit(Pid,normal).
-%%%----------------------------------------------------------------------
-%%% Callback functions from supervisor
-%%%----------------------------------------------------------------------
-
-%%----------------------------------------------------------------------
-%% Func: init/1
-%% Returns: {ok, {SupFlags, [ChildSpec]}} |
-%% ignore |
-%% {error, Reason}
-%%----------------------------------------------------------------------
-init(_StartArgs) ->
- ct_util:mark_process(),
- %%Child1 =
- %%Child2 ={webcover_backend,{webcover_backend,start_link,[]},permanent,2000,worker,[webcover_backend]},
- %%{ok,{{simple_one_for_one,5,10},[Child1]}}.
- {ok,{{one_for_one,100,10},[]}}.
-
-%%%----------------------------------------------------------------------
-%%% Internal functions
-%%%----------------------------------------------------------------------
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/lib/common_test/src/vts.erl b/lib/common_test/src/vts.erl
deleted file mode 100644
index 38e549d2d6..0000000000
--- a/lib/common_test/src/vts.erl
+++ /dev/null
@@ -1,927 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2003-2018. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
-
--module(vts).
-
--export([start/0,
- init_data/5,
- stop/0,
- report/2]).
-
--export([config_data/0,
- start_link/0]).
-
--export([start_page/2,
- title_frame/2,
- menu_frame/2,
- welcome_frame/2,
- config_frame/2,
- browse_config_file/2,
- add_config_file/2,
- remove_config_file/2,
- run_frame/2,
- add_test_dir/2,
- remove_test_dir/2,
- select_case/2,
- select_suite/2,
- run_test/2,
- result_frameset/2,
- result_summary_frame/2,
- no_result_log_frame/2,
- redirect_to_result_log_frame/2]).
-
--export([test_info/3]).
-
--define(START_PAGE,"/vts_erl/vts/start_page").
-
--define(tests,vts_tests).
-
-%% Colors
--define(INFO_BG_COLOR,"#C0C0EA").
-
--record(state,{tests=[],config=[],event_handler=[],test_runner,
- running=0,reload_results=false,start_dir,current_log_dir,
- logopts=[],total=0,ok=0,fail=0,skip=0,testruns=[]}).
-
-
-%%%-----------------------------------------------------------------
-%%% User API
-start() ->
- {ok, _} = ct_webtool:start(),
- ct_webtool:start_tools([],"app=vts").
-
-init_data(ConfigFiles,EvHandlers,LogDir,LogOpts,Tests) ->
- call({init_data,ConfigFiles,EvHandlers,LogDir,LogOpts,Tests}).
-
-stop() ->
- ct_webtool:stop_tools([],"app=vts"),
- ct_webtool:stop().
-
-report(What,Data) ->
- call({report,What,Data}).
-
-%%%-----------------------------------------------------------------
-%%% Return config data used by ct_webtool
-config_data() ->
- {ok,LogDir} =
- case lists:keysearch(logdir,1,init:get_arguments()) of
- {value,{logdir,[LogD]}} -> {ok,filename:absname(LogD)};
- false -> file:get_cwd()
- end,
- {vts,
- [{web_data,{"VisualTestServer",?START_PAGE}},
- {alias,{erl_alias,"/vts_erl",[?MODULE]}},
- {alias,{"/log_dir",LogDir}},
- {start,{child,{{local,?MODULE},
- {?MODULE,start_link,[]},
- permanent,100,worker,[?MODULE]}}}
- ]}.
-
-start_link() ->
- case whereis(?MODULE) of
- undefined ->
- Self = self(),
- Pid = spawn_link(fun() -> init(Self) end),
- MRef = erlang:monitor(process,Pid),
- receive
- {Pid,started} ->
- erlang:demonitor(MRef, [flush]),
- {ok,Pid};
- {'DOWN',MRef,process,_,Reason} ->
- {error,{vts,died,Reason}}
- end;
- Pid ->
- {ok,Pid}
- end.
-
-start_page(_Env,_Input) ->
- call(start_page).
-title_frame(_Env,_Input) ->
- call(title_frame).
-welcome_frame(_Env,_Input) ->
- call(welcome_frame).
-menu_frame(_Env,_Input) ->
- call(menu_frame).
-config_frame(_Env,_Input) ->
- call(config_frame).
-browse_config_file(_Env,Input) ->
- call({browse_config_file,Input}).
-add_config_file(_Env,Input) ->
- call({add_config_file,Input}).
-remove_config_file(_Env,Input) ->
- call({remove_config_file,Input}).
-run_frame(_Env,_Input) ->
- call(run_frame).
-add_test_dir(_Env,Input) ->
- call({add_test_dir,Input}).
-remove_test_dir(_Env,Input) ->
- call({remove_test_dir,Input}).
-select_suite(_Env,Input) ->
- call({select_suite,Input}).
-select_case(_Env,Input) ->
- call({select_case,Input}).
-run_test(_Env,_Input) ->
- call(run_test).
-result_frameset(_Env,_Input) ->
- call(result_frameset).
-redirect_to_result_log_frame(_Env,_Input) ->
- call(redirect_to_result_log_frame).
-result_summary_frame(_Env,_Input) ->
- call(result_summary_frame).
-no_result_log_frame(_Env,_Input) ->
- call(no_result_log_frame).
-
-aborted() ->
- call(aborted).
-
-test_info(_VtsPid,Type,Data) ->
- call({test_info,Type,Data}).
-
-init(Parent) ->
- register(?MODULE,self()),
- process_flag(trap_exit,true),
- ct_util:mark_process(),
- Parent ! {self(),started},
- {ok,Cwd} = file:get_cwd(),
- InitState = #state{start_dir=Cwd},
- loop(InitState).
-
-loop(State) ->
- receive
- {{init_data,Config,EvHandlers,LogDir,LogOpts,Tests},From} ->
- %% ct:pal("State#state.current_log_dir=~p", [State#state.current_log_dir]),
- NewState = State#state{config=Config,event_handler=EvHandlers,
- current_log_dir=LogDir,
- logopts=LogOpts,tests=Tests},
- _ = ct_install(NewState),
- return(From,ok),
- loop(NewState);
- {start_page,From} ->
- return(From,start_page1()),
- loop(State);
- {title_frame,From} ->
- return(From,title_frame1()),
- loop(State);
- {welcome_frame,From} ->
- return(From,welcome_frame1()),
- loop(State);
- {menu_frame,From} ->
- return(From,menu_frame1()),
- loop(State);
- {config_frame,From} ->
- return(From,config_frame1(State)),
- loop(State);
- {{browse_config_file,_Input},From} ->
- return(From,ok),
- loop(State);
- {{add_config_file,Input},From} ->
- {Return,State1} = add_config_file1(Input,State),
- _ = ct_install(State1),
- return(From,Return),
- loop(State1);
- {{remove_config_file,Input},From} ->
- {Return,State1} = remove_config_file1(Input,State),
- _ = ct_install(State1),
- return(From,Return),
- loop(State1);
- {run_frame,From} ->
- return(From,run_frame1(State)),
- loop(State);
- {{add_test_dir,Input},From} ->
- {Return,State1} = add_test_dir1(Input,State),
- return(From,Return),
- loop(State1);
- {{remove_test_dir,Input},From} ->
- {Return,State1} = remove_test_dir1(Input,State),
- return(From,Return),
- loop(State1);
- {{select_suite,Input},From} ->
- {Return,State1} = select_suite1(Input,State),
- return(From,Return),
- loop(State1);
- {{select_case,Input},From} ->
- {Return,State1} = select_case1(Input,State),
- return(From,Return),
- loop(State1);
- {run_test,From} ->
- State1 = run_test1(State),
- return(From,redirect_to_result_frameset1()),
- loop(State1);
- {result_frameset,From} ->
- return(From,result_frameset1(State)),
- loop(State);
- {redirect_to_result_log_frame,From} ->
- return(From,redirect_to_result_log_frame1(State)),
- loop(State);
- {result_summary_frame,From} ->
- return(From,result_summary_frame1(State)),
- loop(State);
- stop_reload_results ->
- ok = file:set_cwd(State#state.start_dir),
- loop(State#state{reload_results=false});
- {no_result_log_frame,From} ->
- return(From,no_result_log_frame1()),
- loop(State);
- {aborted,From} ->
- return(From,ok),
- loop(State#state{test_runner=undefined,running=0});
- {{report,What,Data},From} ->
- State1 = report1(What,Data,State),
- return(From,ok),
- loop(State1);
- {stop,From} ->
- return(From,ok);
- {'EXIT',Pid,Reason} ->
- case State#state.test_runner of
- Pid ->
- io:format("Test run error: ~tp\n",[Reason]),
- loop(State);
- _ ->
- loop(State)
- end;
- {{test_info,_Type,_Data},From} ->
- return(From,ok),
- loop(State)
- end.
-
-call(Msg) ->
- case whereis(?MODULE) of
- undefined -> {error,no_proc};
- Pid ->
- MRef = erlang:monitor(process,Pid),
- Ref = make_ref(),
- Pid ! {Msg,{self(),Ref}},
- receive
- {Ref, Result} ->
- erlang:demonitor(MRef, [flush]),
- Result;
- {'DOWN',MRef,process,_,Reason} ->
- {error,{process_down,Pid,Reason}}
- end
- end.
-
-return({To,Ref},Result) ->
- To ! {Ref, Result},
- ok.
-
-run_test1(State=#state{tests=Tests,current_log_dir=LogDir,
- logopts=LogOpts}) ->
- Self=self(),
- RunTest = fun() ->
- ct_util:mark_process(),
- case ct_run:do_run(Tests,[],LogDir,LogOpts) of
- {error,_Reason} ->
- aborted();
- _ ->
- ok
- end,
- unlink(Self)
- end,
- Pid = spawn_link(RunTest),
- {Total,Tests1} =
- receive
- {{test_info,start_info,{_,_,Cases}},From} ->
- return(From,ok),
- {Cases,Tests};
- EXIT = {'EXIT',_,_} ->
- self() ! EXIT,
- {0,[]}
- after 30000 ->
- {0,[]}
- end,
- State#state{test_runner=Pid,running=length(Tests1),
- total=Total,ok=0,fail=0,skip=0,testruns=[]}.
-
-
-ct_install(#state{config=Config,event_handler=EvHandlers,
- current_log_dir=LogDir}) ->
- ct_run:install([{config,Config},{event_handler,EvHandlers}],LogDir).
-%%%-----------------------------------------------------------------
-%%% HTML
-start_page1() ->
- header("Visual Test Server Start Page",start_page_frameset()).
-
-start_page_frameset() ->
- frameset(
- "ROWS=\"60,*\"",
- [frame(["NAME=\"title\" SRC=\"./title_frame\""]),
- frameset(
- "COLS=\"150,*\"",
- [frame(["NAME=\"menu\" SRC=\"./menu_frame\""]),
- frame(["NAME=\"main\" SRC=\"./welcome_frame\""])])]).
-
-
-title_frame1() ->
- header(body("BGCOLOR=lightgrey TEXT=darkgreen",title_body())).
-
-title_body() ->
- p("ALIGN=center",font("SIZE=\"+3\"",b("Visual Test Server"))).
-
-welcome_frame1() ->
- header(body(welcome_body())).
-
-welcome_body() ->
- table(
- "WIDTH=100% HEIGHT=60%",
- [tr("VALIGN=middle",
- td("ALIGN=center",
- font("SIZE=6",
- ["Welcome to the",br(),
- "Visual Test Server"])))]).
-
-menu_frame1() ->
- header(body(menu_body())).
-
-menu_body() ->
- [h2("Content"),
- ul([
- li(href(["TARGET=\"main\""],"./config_frame","Config")),
- li(href(["TARGET=\"main\""],"./run_frame","Run")),
- li(href(["TARGET=\"main\""],"./result_frameset","Result"))
- ]),
- h2("Logs"),
- ul([
- li(href(["TARGET=\"new\""],"/log_dir/index.html","Last Runs")),
- li(href(["TARGET=\"new\""],"/log_dir/all_runs.html","All Runs"))
- ])
- ].
-
-config_frame1(State) ->
- header("Config",body(config_body(State))).
-
-config_body(State) ->
- Entry = [input("TYPE=file NAME=browse SIZE=40"),
- input("TYPE=hidden NAME=file")],
- BrowseForm =
- form(
- "NAME=read_file_form METHOD=post ACTION=\"./browse_config_file\"",
- table(
- "BORDER=0",
- [tr(td("1. Locate config file")),
- tr(td(Entry))])),
- AddForm =
- form(
- "NAME=add_file_form METHOD=post ACTION=\"./add_config_file\"",
- table(
- "BORDER=0",
- [tr(td("2. Paste full config file name here")),
- tr(
- [td(input("TYPE=text NAME=file SIZE=40")),
- td("ALIGN=center",
- input("TYPE=submit onClick=\"file.value=browse.value;\""
- " VALUE=\"Add\""))])])),
-
- {Text,RemoveForm} =
- case State#state.config of
- [] ->
- T = "Before running the tests, one or more configuration "
- "files may be added. Locate the config file, copy its "
- "full name, paste this into the text field below, then "
- "click the \"Add\" button.",
- R = "",
- {T,R};
- Files ->
- T = "The currently known configuration files are listed below. "
- "To add a file, type the filename in the entry and "
- "click the \"Add\" button. "
- "To remove a file, select it and click the \"Remove\" "
- "button.",
- ConfigFiles = [option(File) || File <- Files],
- Select = select("NAME=file TITLE=\"Select Config File\""
- " MULTIPLE=true",
- ConfigFiles),
- R =
- form(["NAME=remove_config METHOD=post ",
- "ACTION=\"./remove_config_file\""],
- table(
- "BORDER=0",
- [tr(td("ALIGN=center",Select)),
- tr(td("ALIGN=center",
- input("TYPE=submit VALUE=\"Remove\"")))])),
- {T,R}
- end,
-
- [h1("ALIGN=center","Config"),
- table(
- "WIDTH=450 ALIGN=center CELLPADDING=5",
- [tr(td(["BGCOLOR=",?INFO_BG_COLOR],Text)),
- tr(td("")),
- tr(td("")),
- tr(td("ALIGN=left",BrowseForm)),
- tr(td("ALIGN=left",AddForm)),
- tr(td("ALIGN=left",RemoveForm))])].
-
-add_config_file1(Input,State) ->
- State1 =
- case get_input_data(Input,"file") of
- "" ->
- State;
- File ->
- State#state{config=[File|State#state.config]}
- end,
- Return = config_frame1(State1),
- {Return,State1}.
-
-remove_config_file1(Input,State) ->
- Files = get_all_input_data(Input,"file"),
- State1 = State#state{config=State#state.config--Files},
- Return = config_frame1(State1),
- {Return,State1}.
-
-
-
-run_frame1(State) ->
- header("Run Test",body(run_body(State))).
-
-run_body(#state{running=Running}) when Running>0 ->
- [h1("ALIGN=center","Run Test"),
- p(["Test are ongoing: ",href("./result_frameset","Results")])];
-run_body(State) ->
- ConfigList =
- case State#state.config of
- [] ->
- ul(["none"]);
- CfgFiles ->
- ul([li(File) || File <- CfgFiles])
- end,
- ConfigFiles = [h3("Config Files"),
- ConfigList],
- {ok,CWD} = file:get_cwd(),
- CurrWD = [h3("Current Working Directory"), ul(CWD)],
- AddDirForm =
- form(
- "NAME=add_dir_form METHOD=post ACTION=\"./add_test_dir\"",
- table(
- "BORDER=0",
- [tr(td("COLSPAN=2","Enter test directory")),
- tr(
- [td(input("TYPE=text NAME=dir SIZE=40")),
- td("ALIGN=center",
- input("TYPE=submit onClick=\"dir.value=browse.value;\""
- " VALUE=\"Add Test Dir\""))])])),
- {LoadedTestsTable,Submit} =
- case create_testdir_entries(State#state.tests,1) of
- [] -> {"",""};
- TestDirs ->
- Heading = tr([th(""),
- th("ALIGN=left","Directory"),
- th("ALIGN=left","Suite"),
- th("ALIGN=left","Case")]),
- {table("CELLPADDING=5",[Heading,TestDirs]),
- submit_button()}
- end,
- Body =
- table(
- "WIDTH=450 ALIGN=center",
- [tr(td("")),
- tr(td("")),
- tr(td(ConfigFiles)),
- tr(td("")),
- tr(td(CurrWD)),
- tr(td("")),
- tr(td(AddDirForm)),
- tr(td("")),
- tr(td(LoadedTestsTable)),
- tr(td(Submit))
- ]),
- [h1("ALIGN=center","Run Test"), Body].
-
-create_testdir_entries([{Dir,Suite,Case}|Tests],N) ->
- [testdir_entry(Dir,Suite,Case,N)|create_testdir_entries(Tests,N+1)];
-create_testdir_entries([],_N) ->
- [].
-
-testdir_entry(Dir,Suite,Case,N) ->
- NStr = vts_integer_to_list(N),
- tr([td(delete_button(NStr)),
- td(Dir),
- td(suite_select(Dir,Suite,NStr)),
- td(case_select(Dir,Suite,Case,NStr))]).
-
-delete_button(N) ->
- form(["NAME=remove_dir_form METHOD=post ACTION=\"./remove_test_dir\""],
- [input(["TYPE=hidden NAME=dir VALUE=\'",N,"\'"]),
- input(["TYPE=submit VALUE=X"])]).
-
-suite_select(Dir,Suite,N) ->
- case filelib:wildcard(filename:join(Dir,"*_SUITE.erl")) of
- [] ->
- select("NAME=suite TITLE=\"Select suite\"","");
- Suites0 ->
- Suites = [filename:basename(filename:rootname(S)) || S <- Suites0],
- select("NAME=suite TITLE=\"Select suite\"",
- options(["all"|Suites],atom_to_list(Suite),N,"select_suite"))
- end.
-
-case_select(_Dir,all,_,N) ->
- select("NAME=case TITLE=\"Select case\"",
- options(["all"],"all",N,"select_case"));
-case_select(Dir,Suite,Case,N) ->
- MakeResult =
- case application:get_env(common_test, auto_compile) of
- {ok,false} ->
- ok;
- _ ->
- UserInclude =
- case application:get_env(common_test, include) of
- {ok,UserInclDirs} when length(UserInclDirs) > 0 ->
- [{i,UserInclDir} || UserInclDir <- UserInclDirs];
- _ ->
- []
- end,
- ct_run:run_make(Dir,Suite,UserInclude)
- end,
- case MakeResult of
- ok ->
- true = code:add_pathz(Dir),
- case catch apply(Suite,all,[]) of
- {'EXIT',Reason} ->
- io:format("\n~tp\n",[Reason]),
- red(["COULD NOT READ TESTCASES!!",br(),
- "See erlang shell for info"]);
- {skip,_Reason} ->
- select("NAME=case TITLE=\"Select case\"",
- options(["all"],"all",N,"select_case"));
- AllCasesAtoms ->
- AllCases = [atom_to_list(C) || C <- AllCasesAtoms,
- is_atom(C)],
- select("NAME=case TITLE=\"Select case\"",
- options(["all"|AllCases],atom_to_list(Case),
- N,"select_case"))
- end;
- _Error ->
- red(["COMPILATION ERROR!!",br(),
- "See erlang shell for info",br(),
- "Reload this page when errors are fixed"])
- end.
-
-
-options([Selected|Elements],Selected,N,Func) ->
- [option(["SELECTED ",
- "onClick=\"document.location.href=\'./",Func,"?n=",N,
- "&selected=",Selected,"\';\""],
- Selected)|
- options(Elements,Selected,N,Func)];
-options([Element|Elements],Selected,N,Func) ->
- [option(["onClick=\"document.location.href=\'./",Func,"?n=",N,
- "&selected=",Element,"\';\""],
- Element)|
- options(Elements,Selected,N,Func)];
-options([],_Selected,_N,_Func) ->
- [].
-
-add_test_dir1(Input, State) ->
- State1 =
- case get_input_data(Input,"dir") of
- "" -> State;
- Dir0 ->
- Dir = case ct_util:is_test_dir(Dir0) of
- true -> Dir0;
- false -> ct_util:get_testdir(Dir0, all)
- end,
- case filelib:is_dir(Dir) of
- true ->
- Test = ct_run:tests(Dir),
- State#state{tests=State#state.tests++Test};
- false ->
- State
- end
- end,
- Return = run_frame1(State1),
- {Return,State1}.
-
-remove_test_dir1(Input,State) ->
- N = list_to_integer(get_input_data(Input,"dir")),
- State1 = State#state{tests=delete_test(N,State#state.tests)},
- Return = run_frame1(State1),
- {Return,State1}.
-
-delete_test(1,[_|T]) ->
- T;
-delete_test(N,[H|T]) ->
- [H|delete_test(N-1,T)].
-
-select_suite1(Input,State) ->
- N = list_to_integer(get_input_data(Input,"n")),
- Suite = list_to_atom(get_input_data(Input,"selected")),
- Tests1 = replace_suite(N,Suite,State#state.tests),
- State1 = State#state{tests=Tests1},
- Return = run_frame1(State1),
- {Return,State1}.
-
-replace_suite(1,Suite,[{Dir,_,_}|T]) ->
- [Test] = ct_run:tests(Dir,Suite),
- [Test|T];
-replace_suite(N,Suite,[H|T]) ->
- [H|replace_suite(N-1,Suite,T)].
-
-select_case1(Input,State) ->
- N = list_to_integer(get_input_data(Input,"n")),
- Case = list_to_atom(get_input_data(Input,"selected")),
- Tests1 = replace_case(N,Case,State#state.tests),
- State1 = State#state{tests=Tests1},
- Return = run_frame1(State1),
- {Return,State1}.
-
-replace_case(1,Case,[{Dir,Suite,_}|T]) ->
- [Test] = ct_run:tests(Dir,Suite,Case),
- [Test|T];
-replace_case(N,Case,[H|T]) ->
- [H|replace_case(N-1,Case,T)].
-
-
-submit_button() ->
- form(["NAME=run_test_form METHOD=post ACTION=\"./run_test\""],
- [input("TYPE=submit VALUE=\"Run Test\"")]).
-
-
-redirect_to_result_frameset1() ->
- Head =
- ["<META HTTP-EQUIV=\"refresh\" CONTENT=\"1; URL=./result_frameset\">"],
- [header("",Head,body("Please wait..."))].
-
-result_frameset1(State) ->
- header("Results",result_frameset2(State)).
-
-result_frameset2(State) ->
- ResultLog =
- case {State#state.current_log_dir,State#state.running} of
- {undefined,0} ->
- "./no_result_log_frame";
- {undefined,_} ->
- "./redirect_to_result_log_frame";
- {_Dir,0} ->
- filename:join(["/log_dir","index.html"]);
- {_Dir,_} when State#state.testruns == [] ->
- %% crash before first test
- "./no_result_log_frame";
- {_Dir,_} ->
- {_,CurrentLog} = hd(State#state.testruns),
- CurrentLog
- end,
- frameset(
- "COLS=\"200,*\"",
- [frame(["NAME=\"result_summary\" SRC=\"./result_summary_frame\""]),
- frame(["NAME=\"result_log\" SRC=\"",ResultLog,"\""])]).
-
-redirect_to_result_log_frame1(State) ->
- ResultLog =
- case {State#state.testruns,State#state.running} of
- {[],0} ->
- "./no_result_log_frame";
- {[],_} ->
- "./redirect_to_result_log_frame";
- {[{_,CurrentLog}|_],_} ->
- CurrentLog
- end,
- Head = ["<META HTTP-EQUIV=\"refresh\" CONTENT=\"1; URL=",ResultLog,"\">"],
- [header("",Head,body("Please wait..."))].
-
-result_summary_frame1(State) ->
- case {State#state.running,State#state.reload_results} of
- {0,false} ->
- header("Result Summary",body(result_summary_body(State)));
- _ ->
- Head =
- "<SCRIPT LANGUAGE=\"JavaScript1.2\">\n"
- "\n"
- "function startReloadInterval() {\n"
- " intervalId = setInterval(\"reloadPage()\",5000)\n"
- "}\n"
- "\n"
- "function reloadPage() {\n"
- " location.reload()\n"
- " parent.result_log.location.reload()\n"
-% " parent.result_log.scrollBy(0, window.innerHeight)\n"
- "}\n"
- "</SCRIPT>\n",
- header("Result Summary",Head,
- body("onLoad=\"startReloadInterval()\" BGCOLOR=\"#FFFFFF\"",
- result_summary_body(State)))
- end.
-
-result_summary_body(State) ->
- N = State#state.ok + State#state.fail + State#state.skip,
- [h2("Result Summary"),
- p([b(vts_integer_to_list(N))," cases executed (of ",
- b(vts_integer_to_list(State#state.total)),")"]),
- p([green([b(vts_integer_to_list(State#state.ok))," successful"]),br(),
- red([b(vts_integer_to_list(State#state.fail))," failed"]),br(),
- orange([b(vts_integer_to_list(State#state.skip))," skipped"])]),
- executed_test_list(State)].
-
-executed_test_list(#state{testruns=[]}) ->
- [];
-executed_test_list(#state{testruns=TestRuns}) ->
- [h2("Executed Tests"),
- table(
- "",
- [tr(td(href("TARGET=\"result_log\"",Log,Name))) ||
- {Name,Log} <- lists:reverse(TestRuns)])].
-
-
-no_result_log_frame1() ->
- header("Test Results",body(no_result_log_body())).
-
-no_result_log_body() ->
- [h1("ALIGN=center","Test Results"),
- p(["There are currently no test results available. ",
- br(),href("TARGET=\"main\"","./run_frame","You can run tests here")])].
-
-report1(tests_start,{TestName,_N},State) ->
- {ok,LogDir} = ct_logs:get_log_dir(),
- TestRuns =
- case State#state.testruns of
- [{TestName,_}|_]=TR ->
- TR;
- TR ->
- [{TestName,get_test_log(TestName,LogDir)}|TR]
- end,
- State#state{testruns=TestRuns};
-report1(tests_done,{_Ok,_Fail,_Skip},State) ->
- {ok, _} = timer:send_after(5000, self(),stop_reload_results),
- State#state{running=State#state.running-1,reload_results=true};
-report1(tc_start,{_Suite,_Case},State) ->
- State;
-report1(tc_done,{_Suite,init_per_suite,_},State) ->
- State;
-report1(tc_done,{_Suite,end_per_suite,_},State) ->
- State;
-report1(tc_done,{_Suite,init_per_group,_},State) ->
- State;
-report1(tc_done,{_Suite,end_per_group,_},State) ->
- State;
-report1(tc_done,{_Suite,_Case,ok},State) ->
- State#state{ok=State#state.ok+1};
-report1(tc_done,{_Suite,_Case,{failed,_Reason}},State) ->
- State#state{fail=State#state.fail+1};
-report1(tc_done,{_Suite,_Case,{skipped,_Reason}},State) ->
- State#state{skip=State#state.skip+1};
-report1(tc_user_skip,{_Suite,_Case,_Reason},State) ->
- State#state{skip=State#state.skip+1};
-report1(tc_auto_skip,{_Suite,_Case,_Reason},State) ->
- State#state{skip=State#state.skip+1};
-report1(loginfo,_,State) ->
- State.
-
-get_test_log(TestName,LogDir) ->
- [Log] =
- filelib:wildcard(
- filename:join([TestName++".logs","run*","suite.log.html"])),
- filename:join(["/log_dir",LogDir,Log]).
-
-
-
-%get_description(Suite,Case) ->
-% case erlang:function_exported(Suite,Case,0) of
-% true ->
-% case catch apply(Suite,Case,[]) of
-% {'EXIT',_Reason} ->
-% "-";
-% Info ->
-% case lists:keysearch(doc,1,Info) of
-% {value,{doc,Doc}} when is_list(Doc) ->
-% Doc;
-% _ ->
-% "-"
-% end
-% end;
-% false ->
-% "-"
-% end.
-
-%%%-----------------------------------------------------------------
-%%% Internal library
-header(Body) ->
- header("","",Body).
-header(Title,Body) ->
- header(Title,"",Body).
-header(Title,Head,Body) ->
- ["Pragma:no-cache\r\n",
- "Content-type: text/html\r\n\r\n",
- html_header(Title,Head,Body)].
-
-html_header(Title,Head,Body) ->
- ["<HTML>\n",
- "<HEAD>\n",
- "<TITLE>", Title, "</TITLE>\n",
- Head,
- "</HEAD>\n",
- Body,
- "</HTML>"].
-
-body(Text) ->
- ["<BODY BGCOLOR=\"#FFFFFF\">\n",Text,"<\BODY>\n"].
-body(Args,Text) ->
- ["<BODY ", Args, ">\n", Text,"<\BODY>\n"].
-
-
-frameset(Args,Frames) ->
- ["<FRAMESET ",Args,">\n", Frames, "\n</FRAMESET>\n"].
-frame(Args) ->
- ["<FRAME ",Args, ">\n"].
-
-table(Args,Text) ->
- ["<TABLE ", Args, ">\n", Text, "\n</TABLE>\n"].
-tr(Text) ->
- ["<TR>\n", Text, "\n</TR>\n"].
-tr(Args,Text) ->
- ["<TR ", Args, ">\n", Text, "\n</TR>\n"].
-th(Text) ->
- ["<TH>", Text, "</TH>"].
-th(Args,Text) ->
- ["<TH ", Args, ">\n", Text, "\n</TH>\n"].
-td(Text) ->
- ["<TD>", Text, "</TD>"].
-td(Args,Text) ->
- ["<TD ", Args, ">", Text, "</TD>"].
-
-b(Text) ->
- ["<B>",Text,"</B>"].
-%em(Text) ->
-% ["<EM>",Text,"</EM>\n"].
-%pre(Text) ->
-% ["<PRE>",Text,"</PRE>"].
-href(Link,Text) ->
- ["<A HREF=\"",Link,"\">",Text,"</A>"].
-href(Args,Link,Text) ->
- ["<A HREF=\"",Link,"\" ",Args,">",Text,"</A>"].
-form(Args,Text) ->
- ["<FORM ",Args,">\n",Text,"\n</FORM>\n"].
-input(Args) ->
- ["<INPUT ", Args, ">\n"].
-select(Args,Text) ->
- ["<SELECT ", Args, ">\n", Text, "\n</SELECT>\n"].
-option(Text) ->
- ["<OPTION>\n", Text, "\n</OPTION>\n"].
-option(Args,Text) ->
- ["<OPTION ", Args, ">\n", Text, "\n</OPTION>\n"].
-h1(Args,Text) ->
- ["<H1 ", Args, ">",Text,"</H1>\n"].
-h2(Text) ->
- ["<H2>",Text,"</H2>\n"].
-h3(Text) ->
- ["<H3>",Text,"</H3>\n"].
-%%h4(Text) ->
-%% ["<H4>",Text,"</H4>\n"].
-font(Args,Text) ->
- ["<FONT ",Args,">\n",Text,"\n</FONT>\n"].
-p(Text) ->
- ["<P>",Text,"</P>\n"].
-p(Args, Text) ->
- ["<P ", Args, ">",Text,"</P>\n"].
-ul(Text) ->
- ["<UL>", Text, "</UL>\n"].
-li(Text) ->
- ["<LI>", Text, "</LI>\n"].
-br() ->
- "<BR>\n".
-
-red(Text) -> color(red,Text).
-green(Text) -> color(green,Text).
-orange(Text) -> color(orange,Text).
-color(Color,Text) when is_atom(Color) ->
- font(["COLOR=",atom_to_list(Color)],Text).
-
-get_all_input_data(Input,Key)->
- List = parse(Input),
- get_all_input_data(List,Key,[]).
-get_all_input_data([{Key,Value}|List],Key,Acc) ->
- get_all_input_data(List,Key,[Value|Acc]);
-get_all_input_data([{_OtherKey,_Value}|List],Key,Acc) ->
- get_all_input_data(List,Key,Acc);
-get_all_input_data([],_Key,Acc) ->
- Acc.
-
-get_input_data(Input,Key)->
- case lists:keysearch(Key,1,parse(Input)) of
- {value,{Key,Value}} ->
- Value;
- false ->
- undefined
- end.
-
-parse(Input) ->
- uri_string:dissect_query(Input).
-
-vts_integer_to_list(X) when is_atom(X) ->
- atom_to_list(X);
-vts_integer_to_list(X) when is_integer(X) ->
- integer_to_list(X).
diff --git a/lib/common_test/test/ct_test_support.erl b/lib/common_test/test/ct_test_support.erl
index 388d5d46c6..1d50f4f97f 100644
--- a/lib/common_test/test/ct_test_support.erl
+++ b/lib/common_test/test/ct_test_support.erl
@@ -219,17 +219,8 @@ get_opts(Config) ->
end,
LogDir =
case os:getenv("CT_USE_TMP_DIR") of
- false ->
- case os:type() of
- {win32,_} ->
- if TempDir == undefined -> PrivDir;
- true -> TempDir
- end;
- _ ->
- PrivDir
- end;
- _ ->
- TempDir
+ false -> PrivDir;
+ _ -> TempDir
end,
%% Copy test variables to app environment on new node
diff --git a/lib/common_test/vsn.mk b/lib/common_test/vsn.mk
index 8dcb69c1c6..ddc518f474 100644
--- a/lib/common_test/vsn.mk
+++ b/lib/common_test/vsn.mk
@@ -1 +1 @@
-COMMON_TEST_VSN = 1.17.3
+COMMON_TEST_VSN = 1.18
diff --git a/lib/compiler/Makefile b/lib/compiler/Makefile
index 3678f48b7c..f18df11e9f 100644
--- a/lib/compiler/Makefile
+++ b/lib/compiler/Makefile
@@ -36,4 +36,6 @@ SPECIAL_TARGETS =
#
include $(ERL_TOP)/make/otp_subdir.mk
+DIA_PLT_APPS=crypto hipe
+
include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/compiler/doc/src/notes.xml b/lib/compiler/doc/src/notes.xml
index 209e40c0f5..4db09e8059 100644
--- a/lib/compiler/doc/src/notes.xml
+++ b/lib/compiler/doc/src/notes.xml
@@ -32,6 +32,99 @@
<p>This document describes the changes made to the Compiler
application.</p>
+<section><title>Compiler 7.4.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>Code such as the following would crash the compiler in
+ OTP 22: <c>[some_atom = fun some_function/1]</c></p>
+ <p>
+ Own Id: OTP-15833</p>
+ </item>
+ <item>
+ <p>Compilation could get really slow (in the order of
+ minutes instead of seconds) when compiling huge
+ functions. (Thanks to Kostis Sagonas for reporting this
+ bug.)</p>
+ <p>
+ Own Id: OTP-15923</p>
+ </item>
+ <item>
+ <p>Fixed a bug in the validator that could reject valid
+ code.</p>
+ <p>
+ Own Id: OTP-15954 Aux Id: ERL-995 </p>
+ </item>
+ <item>
+ <p>In rare circumstances, when two clauses had identical
+ bodies and guard tests that tested a single boolean
+ variable, the guard test for the second clause could be
+ discarded, executing the second clause unconditionally if
+ the first clause was not executed.</p>
+ <p>
+ Own Id: OTP-15963</p>
+ </item>
+ <item>
+ <p>Fixed extremely slow compilation for huge functions
+ doing predominantly pattern matching.</p>
+ <p>
+ Own Id: OTP-15966 Aux Id: ERL-1014 </p>
+ </item>
+ <item>
+ <p>The compiler could generate unsafe code (that would
+ crash the runtime system) for map pattern matching. The
+ code could be unsafe if the matched key was not present
+ in the map at runtime. </p>
+ <p>
+ Own Id: OTP-15968 Aux Id: ERL-1017 </p>
+ </item>
+ <item>
+ <p>Correct code using try/after could fail to compile
+ when using the option '<c>no_type_opt</c>'.</p>
+ <p>
+ Own Id: OTP-15969 Aux Id: ERL-997 </p>
+ </item>
+ <item>
+ <p>The compiler could crash when compiling code that
+ called '<c>length/1</c>' on a binary extracted using the
+ binary syntax.</p>
+ <p>
+ Own Id: OTP-15970 Aux Id: ERL-1013 </p>
+ </item>
+ <item>
+ <p>Fixed a bug where the compiler could fail with an
+ internal consistency failure error when compiling receive
+ statements.</p>
+ <p>
+ Own Id: OTP-15982 Aux Id: ERL-1022 </p>
+ </item>
+ <item>
+ <p>Fixed a problem where the compiler would crash when
+ compiling binary matching in a function head.</p>
+ <p>
+ Own Id: OTP-15985 Aux Id: ERL-1026 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Compiler 7.4.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>Fixed a compiler crash introduced in <c>22.0.6</c>
+ (OTP-15952).</p>
+ <p>
+ Own Id: OTP-15953 Aux Id: ERL-999 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Compiler 7.4.3</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/compiler/src/beam_call_types.erl b/lib/compiler/src/beam_call_types.erl
index e76ad79365..904d82a62d 100644
--- a/lib/compiler/src/beam_call_types.erl
+++ b/lib/compiler/src/beam_call_types.erl
@@ -24,7 +24,68 @@
-import(lists, [duplicate/2,foldl/3]).
--export([types/3]).
+-export([will_succeed/3, types/3]).
+
+%%
+%% Returns whether a call will succeed or not.
+%%
+%% Note that it only answers 'yes' for functions in the 'erlang' module as
+%% calls to other modules may fail due to not being loaded, even if we consider
+%% the module to be known.
+%%
+
+-spec will_succeed(Mod, Func, ArgTypes) -> Result when
+ Mod :: atom(),
+ Func :: atom(),
+ ArgTypes :: [normal_type()],
+ Result :: yes | no | maybe.
+
+will_succeed(erlang, BoolOp, [LHS, RHS]) when BoolOp =:= 'and';
+ BoolOp =:= 'or' ->
+ case {succeeds_if_type(LHS, beam_types:make_boolean()),
+ succeeds_if_type(RHS, beam_types:make_boolean())} of
+ {yes, yes} -> yes;
+ {no, _} -> no;
+ {_, no} -> no;
+ {_, _} -> maybe
+ end;
+will_succeed(erlang, bit_size, [Arg]) ->
+ succeeds_if_type(Arg, #t_bitstring{});
+will_succeed(erlang, byte_size, [Arg]) ->
+ succeeds_if_type(Arg, #t_bitstring{});
+will_succeed(erlang, map_size, [Arg]) ->
+ succeeds_if_type(Arg, #t_map{});
+will_succeed(erlang, 'not', [Arg]) ->
+ succeeds_if_type(Arg, beam_types:make_boolean());
+will_succeed(erlang, setelement, [#t_integer{elements={Min,Max}},
+ #t_tuple{exact=Exact,size=Size}, _]) ->
+ case Min >= 1 andalso Max =< Size of
+ true -> yes;
+ false when Exact -> no;
+ false -> maybe
+ end;
+will_succeed(erlang, size, [Arg]) ->
+ succeeds_if_type(Arg, #t_bitstring{});
+will_succeed(erlang, tuple_size, [Arg]) ->
+ succeeds_if_type(Arg, #t_tuple{});
+will_succeed(Mod, Func, Args) ->
+ Arity = length(Args),
+ case erl_bifs:is_safe(Mod, Func, Arity) of
+ true ->
+ yes;
+ false ->
+ case erl_bifs:is_exit_bif(Mod, Func, Arity) of
+ true -> no;
+ false -> maybe
+ end
+ end.
+
+succeeds_if_type(ArgType, Required) ->
+ case beam_types:meet(ArgType, Required) of
+ ArgType -> yes;
+ none -> no;
+ _ -> maybe
+ end.
%%
%% Returns the inferred return and argument types for known functions, and
diff --git a/lib/compiler/src/beam_jump.erl b/lib/compiler/src/beam_jump.erl
index 74f80ca70e..85fd89bbe8 100644
--- a/lib/compiler/src/beam_jump.erl
+++ b/lib/compiler/src/beam_jump.erl
@@ -580,8 +580,6 @@ is_unreachable_after(I) -> is_exit_instruction(I).
-spec is_exit_instruction(instruction()) -> boolean().
-is_exit_instruction({call_ext,_,{extfunc,M,F,A}}) ->
- erl_bifs:is_exit_bif(M, F, A);
is_exit_instruction(if_end) -> true;
is_exit_instruction({case_end,_}) -> true;
is_exit_instruction({try_case_end,_}) -> true;
diff --git a/lib/compiler/src/beam_kernel_to_ssa.erl b/lib/compiler/src/beam_kernel_to_ssa.erl
index 474cb1a851..2406a634e6 100644
--- a/lib/compiler/src/beam_kernel_to_ssa.erl
+++ b/lib/compiler/src/beam_kernel_to_ssa.erl
@@ -680,13 +680,8 @@ call_cg(Func0, As, [#k_var{name=R}|MoreRs]=Rs, Le, St0) ->
set_ssa_var(Dummy, #b_literal{val=unused}, S)
end, St1, MoreRs),
- case FailCtx of
- {no_catch, _} ->
- {[Call],St2};
- {in_catch, _} ->
- {TestIs,St} = make_succeeded(Ret, FailCtx, St2),
- {[Call|TestIs],St}
- end
+ {TestIs,St} = make_succeeded(Ret, FailCtx, St2),
+ {[Call|TestIs],St}
end.
enter_cg(Func0, As0, Le, St0) ->
diff --git a/lib/compiler/src/beam_ssa_codegen.erl b/lib/compiler/src/beam_ssa_codegen.erl
index 10c3419314..ff880c6296 100644
--- a/lib/compiler/src/beam_ssa_codegen.erl
+++ b/lib/compiler/src/beam_ssa_codegen.erl
@@ -766,9 +766,8 @@ defined(Linear, #cg{regs=Regs}) ->
def([{L,#cg_blk{is=Is0,last=Last}=Blk0}|Bs], DefMap0, Regs) ->
Def0 = def_get(L, DefMap0),
- {Is,Def} = def_is(Is0, Regs, Def0, []),
- Successors = successors(Last),
- DefMap = def_successors(Successors, Def, DefMap0),
+ {Is,Def,MaybeDef} = def_is(Is0, Regs, Def0, []),
+ DefMap = def_successors(Last, Def, MaybeDef, DefMap0),
Blk = Blk0#cg_blk{is=Is},
[{L,Blk}|def(Bs, DefMap, Regs)];
def([], _, _) -> [].
@@ -782,6 +781,11 @@ def_get(L, DefMap) ->
def_is([#cg_alloc{anno=Anno0}=I0|Is], Regs, Def, Acc) ->
I = I0#cg_alloc{anno=Anno0#{def_yregs=>Def}},
def_is(Is, Regs, Def, [I|Acc]);
+def_is([#cg_set{op=succeeded,args=[Var]}=I], Regs, Def, Acc) ->
+ %% Var will only be defined on the success branch of the `br`
+ %% for this block.
+ MaybeDef = def_add_yreg(Var, [], Regs),
+ {reverse(Acc, [I]),Def,MaybeDef};
def_is([#cg_set{op=kill_try_tag,args=[#b_var{}=Tag]}=I|Is], Regs, Def0, Acc) ->
Def = ordsets:del_element(Tag, Def0),
def_is(Is, Regs, Def, [I|Acc]);
@@ -824,7 +828,7 @@ def_is([#cg_set{anno=Anno0,dst=Dst}=I0|Is], Regs, Def0, Acc) ->
Def = def_add_yreg(Dst, Def0, Regs),
def_is(Is, Regs, Def, [I|Acc]);
def_is([], _, Def, Acc) ->
- {reverse(Acc),Def}.
+ {reverse(Acc),Def,[]}.
def_add_yreg(Dst, Def, Regs) ->
case is_yreg(Dst, Regs) of
@@ -832,6 +836,12 @@ def_add_yreg(Dst, Def, Regs) ->
false -> Def
end.
+def_successors(#cg_br{bool=#b_var{},succ=Succ,fail=Fail}, Def, MaybeDef, DefMap0) ->
+ DefMap = def_successors([Fail], ordsets:subtract(Def, MaybeDef), DefMap0),
+ def_successors([Succ], Def, DefMap);
+def_successors(Last, Def, [], DefMap) ->
+ def_successors(successors(Last), Def, DefMap).
+
def_successors([S|Ss], Def0, DefMap) ->
case DefMap of
#{S:=Def1} ->
diff --git a/lib/compiler/src/beam_ssa_dead.erl b/lib/compiler/src/beam_ssa_dead.erl
index 88767456a3..55ded77d43 100644
--- a/lib/compiler/src/beam_ssa_dead.erl
+++ b/lib/compiler/src/beam_ssa_dead.erl
@@ -30,7 +30,7 @@
-import(lists, [append/1,keymember/3,last/1,member/2,
takewhile/2,reverse/1]).
--type used_vars() :: #{beam_ssa:label():=ordsets:ordset(beam_ssa:var_name())}.
+-type used_vars() :: #{beam_ssa:label():=cerl_sets:set(beam_ssa:var_name())}.
-type basic_type_test() :: atom() | {'is_tagged_tuple',pos_integer(),atom()}.
-type type_test() :: basic_type_test() | {'not',basic_type_test()}.
@@ -90,13 +90,11 @@ shortcut_opt(#st{bs=Blocks}=St) ->
%% the diff.)
%%
%% Unfortunately, processing the blocks in reverse post order
- %% potentially makes the time complexity quadratic or even cubic if
- %% the ordset of unset variables grows large, instead of
- %% linear for post order processing. We try to still get reasonable
- %% compilation times by optimizations that will keep the constant
- %% factor as low as possible, and we try to avoid the cubic time
- %% complexity by trying to keep the set of unset variables as small
- %% as possible.
+ %% potentially makes the time complexity quadratic, instead of
+ %% linear for post order processing. We avoid drastic slowdowns by
+ %% limiting how far we search forward to a common block that
+ %% both the success and failure label will reach (see the comment
+ %% in the first clause of shortcut_2/5).
Ls = beam_ssa:rpo(Blocks),
shortcut_opt(Ls, #{}, St).
@@ -124,10 +122,15 @@ shortcut_terminator(#b_br{bool=#b_var{}=Bool,succ=Succ0,fail=Fail0}=Br,
Is, From, Bs, St0) ->
St = St0#st{target=one_way},
RelOp = get_rel_op(Bool, Is),
- SuccBs = bind_var(Bool, #b_literal{val=true}, Bs),
+
+ %% The boolean in a `br` is seldom used by the successors. By
+ %% not binding its value unless it is actually used we might be able
+ %% to skip some work in shortcut/4 and sub/2.
+ SuccBs = bind_var_if_used(Succ0, Bool, #b_literal{val=true}, Bs, St),
BrSucc = shortcut(Succ0, From, SuccBs, St#st{rel_op=RelOp}),
- FailBs = bind_var(Bool, #b_literal{val=false}, Bs),
+ FailBs = bind_var_if_used(Fail0, Bool, #b_literal{val=false}, Bs, St),
BrFail = shortcut(Fail0, From, FailBs, St#st{rel_op=invert_op(RelOp)}),
+
case {BrSucc,BrFail} of
{#b_br{bool=#b_literal{val=true},succ=Succ},
#b_br{bool=#b_literal{val=true},succ=Fail}}
@@ -152,8 +155,14 @@ shortcut_switch([{Lit,L0}|T], Bool, From, Bs, St0) ->
[{Lit,L}|shortcut_switch(T, Bool, From, Bs, St0)];
shortcut_switch([], _, _, _, _) -> [].
+shortcut(L, _From, Bs, #st{rel_op=none,target=one_way}) when map_size(Bs) =:= 0 ->
+ %% There is no way that we can find a suitable branch, because there is no
+ %% relational operator stored, there are no bindings, and the block L can't
+ %% have any phi nodes from which we could pick bindings because when the target
+ %% is `one_way`, it implies the From block has a two-way `br` terminator.
+ #b_br{bool=#b_literal{val=true},succ=L,fail=L};
shortcut(L, From, Bs, St) ->
- shortcut_1(L, From, Bs, ordsets:new(), St).
+ shortcut_1(L, From, Bs, cerl_sets:new(), St).
shortcut_1(L, From, Bs0, UnsetVars0, St) ->
case shortcut_2(L, From, Bs0, UnsetVars0, St) of
@@ -170,7 +179,19 @@ shortcut_1(L, From, Bs0, UnsetVars0, St) ->
end.
%% Try to shortcut this block, branching to a successor.
-shortcut_2(L, From, Bs0, UnsetVars0, St) ->
+shortcut_2(L, From, Bs, UnsetVars, St) ->
+ case cerl_sets:size(UnsetVars) of
+ SetSize when SetSize > 128 ->
+ %% This is an heuristic to limit the search for a forced label
+ %% before it drastically slows down the compiler. Experiments
+ %% with scripts/diffable showed that limits larger than 31 did not
+ %% find any more opportunities for optimization.
+ none;
+ _SetSize ->
+ shortcut_3(L, From, Bs, UnsetVars, St)
+ end.
+
+shortcut_3(L, From, Bs0, UnsetVars0, St) ->
#b_blk{is=Is,last=Last} = get_block(L, St),
case eval_is(Is, From, Bs0, St) of
none ->
@@ -347,7 +368,7 @@ update_unset_vars(L, Is, Br, UnsetVars, #st{skippable=Skippable}) ->
%% Some variables defined in this block are used by
%% successors. We must update the set of unset variables.
SetInThisBlock = [V || #b_set{dst=V} <- Is],
- ordsets:union(UnsetVars, ordsets:from_list(SetInThisBlock))
+ cerl_sets:union(UnsetVars, cerl_sets:from_list(SetInThisBlock))
end.
shortcut_two_way(#b_br{succ=Succ,fail=Fail}, From, Bs0, UnsetVars0, St0) ->
@@ -376,14 +397,14 @@ is_br_safe(UnsetVars, Br, #st{us=Us}=St) ->
%% A two-way branch never branches to a phi node, so there
%% is no need to check for phi nodes here.
- not member(V, UnsetVars) andalso
- ordsets:is_disjoint(Used0, UnsetVars) andalso
- ordsets:is_disjoint(Used1, UnsetVars);
+ not cerl_sets:is_element(V, UnsetVars) andalso
+ cerl_sets:is_disjoint(Used0, UnsetVars) andalso
+ cerl_sets:is_disjoint(Used1, UnsetVars);
#b_br{succ=Same,fail=Same} ->
%% An unconditional branch must not jump to
%% a phi node.
not is_forbidden(Same, St) andalso
- ordsets:is_disjoint(map_get(Same, Us), UnsetVars)
+ cerl_sets:is_disjoint(map_get(Same, Us), UnsetVars)
end.
is_forbidden(L, St) ->
@@ -500,6 +521,15 @@ eval_switch_1([], _Arg, _PrevOp, Fail) ->
%% Fail is now either the failure label or 'none'.
Fail.
+bind_var_if_used(L, Var, Val0, Bs, #st{us=Us}) ->
+ case cerl_sets:is_element(Var, map_get(L, Us)) of
+ true ->
+ Val = get_value(Val0, Bs),
+ Bs#{Var=>Val};
+ false ->
+ Bs
+ end.
+
bind_var(Var, Val0, Bs) ->
Val = get_value(Val0, Bs),
Bs#{Var=>Val}.
@@ -989,7 +1019,7 @@ used_vars([{L,#b_blk{is=Is}=Blk}|Bs], UsedVars0, Skip0) ->
%% shortcut_opt/1.
Successors = beam_ssa:successors(Blk),
- Used0 = used_vars_succ(Successors, L, UsedVars0, []),
+ Used0 = used_vars_succ(Successors, L, UsedVars0, cerl_sets:new()),
Used = used_vars_blk(Blk, Used0),
UsedVars = used_vars_phis(Is, L, Used, UsedVars0),
@@ -1000,8 +1030,8 @@ used_vars([{L,#b_blk{is=Is}=Blk}|Bs], UsedVars0, Skip0) ->
%% shortcut_opt/1.
Defined0 = [Def || #b_set{dst=Def} <- Is],
- Defined = ordsets:from_list(Defined0),
- MaySkip = ordsets:is_disjoint(Defined, Used0),
+ Defined = cerl_sets:from_list(Defined0),
+ MaySkip = cerl_sets:is_disjoint(Defined, Used0),
case MaySkip of
true ->
Skip = Skip0#{L=>true},
@@ -1018,11 +1048,11 @@ used_vars_succ([S|Ss], L, LiveMap, Live0) ->
#{Key:=Live} ->
%% The successor has a phi node, and the value for
%% this block in the phi node is a variable.
- used_vars_succ(Ss, L, LiveMap, ordsets:union(Live, Live0));
+ used_vars_succ(Ss, L, LiveMap, cerl_sets:union(Live, Live0));
#{S:=Live} ->
%% No phi node in the successor, or the value for
%% this block in the phi node is a literal.
- used_vars_succ(Ss, L, LiveMap, ordsets:union(Live, Live0));
+ used_vars_succ(Ss, L, LiveMap, cerl_sets:union(Live, Live0));
#{} ->
%% A peek_message block which has not been processed yet.
used_vars_succ(Ss, L, LiveMap, Live0)
@@ -1040,7 +1070,7 @@ used_vars_phis(Is, L, Live0, UsedVars0) ->
case [{P,V} || {#b_var{}=V,P} <- PhiArgs] of
[_|_]=PhiVars ->
PhiLive0 = rel2fam(PhiVars),
- PhiLive = [{{L,P},ordsets:union(ordsets:from_list(Vs), Live0)} ||
+ PhiLive = [{{L,P},cerl_sets:union(cerl_sets:from_list(Vs), Live0)} ||
{P,Vs} <- PhiLive0],
maps:merge(UsedVars, maps:from_list(PhiLive));
[] ->
@@ -1050,14 +1080,14 @@ used_vars_phis(Is, L, Live0, UsedVars0) ->
end.
used_vars_blk(#b_blk{is=Is,last=Last}, Used0) ->
- Used = ordsets:union(Used0, beam_ssa:used(Last)),
+ Used = cerl_sets:union(Used0, cerl_sets:from_list(beam_ssa:used(Last))),
used_vars_is(reverse(Is), Used).
used_vars_is([#b_set{op=phi}|Is], Used) ->
used_vars_is(Is, Used);
used_vars_is([#b_set{dst=Dst}=I|Is], Used0) ->
- Used1 = ordsets:union(Used0, beam_ssa:used(I)),
- Used = ordsets:del_element(Dst, Used1),
+ Used1 = cerl_sets:union(Used0, cerl_sets:from_list(beam_ssa:used(I))),
+ Used = cerl_sets:del_element(Dst, Used1),
used_vars_is(Is, Used);
used_vars_is([], Used) ->
Used.
@@ -1066,8 +1096,9 @@ used_vars_is([], Used) ->
%%% Common utilities.
%%%
-sub(#b_set{args=Args}=I, Sub) ->
- I#b_set{args=[sub_arg(A, Sub) || A <- Args]}.
+sub(#b_set{args=Args}=I, Sub) when map_size(Sub) =/= 0 ->
+ I#b_set{args=[sub_arg(A, Sub) || A <- Args]};
+sub(I, _Sub) -> I.
sub_arg(#b_var{}=Old, Sub) ->
case Sub of
diff --git a/lib/compiler/src/beam_ssa_lint.erl b/lib/compiler/src/beam_ssa_lint.erl
index a003607dab..224095d4c4 100644
--- a/lib/compiler/src/beam_ssa_lint.erl
+++ b/lib/compiler/src/beam_ssa_lint.erl
@@ -65,13 +65,19 @@ format_error({{_M,F,A},{phi_inside_block, Name, Id}}) ->
[F, A, format_var(Name), Id]);
format_error({{_M,F,A},{undefined_label_in_phi, Label, I}}) ->
io_lib:format("~p/~p: Unknown block label ~p in phi node ~ts",
- [F, A, Label, format_instr(I)]).
+ [F, A, Label, format_instr(I)]);
+format_error({{_M,F,A},{succeeded_not_preceded, I}}) ->
+ io_lib:format("~p/~p: ~ts does not reference the preceding instruction",
+ [F, A, format_instr(I)]);
+format_error({{_M,F,A},{succeeded_not_last, I}}) ->
+ io_lib:format("~p/~p: ~ts is not the last instruction in its block",
+ [F, A, format_instr(I)]).
format_instr(I) ->
[$',beam_ssa_pp:format_instr(I),$'].
format_var(V) ->
- beam_ssa_pp:format_var(#b_var{name=V}).
+ beam_ssa_pp:format_var(V).
validate_function(F) ->
try
@@ -86,34 +92,36 @@ validate_function(F) ->
erlang:raise(Class, Error, Stack)
end.
--type defined_vars() :: gb_sets:set(beam_ssa:var_name()).
+-type defined_vars() :: gb_sets:set(beam_ssa:argument()).
-record(vvars,
{blocks :: #{ beam_ssa:label() => beam_ssa:b_blk() },
branch_def_vars :: #{
- %% Describes the variable state at the time of this exact branch (phi
- %% node validation).
- {From :: beam_ssa:label(), To :: beam_ssa:label()} => defined_vars(),
- %% Describes the variable state common to all branches leading to this
- %% label (un/redefined variable validation).
- beam_ssa:label() => defined_vars() },
+ %% Describes the variable state at the time of
+ %% this exact branch (phi node validation).
+ {From :: beam_ssa:label(),
+ To :: beam_ssa:label()} => defined_vars(),
+ %% Describes the variable state common to all
+ %% branches leading to this label (un/redefined
+ %% variable validation).
+ beam_ssa:label() => defined_vars() },
defined_vars :: defined_vars()}).
-spec validate_variables(beam_ssa:b_function()) -> ok.
validate_variables(#b_function{ args = Args, bs = Blocks }) ->
%% Prefill the mapping with function arguments.
- ArgNames = vvars_get_varnames(Args),
- DefVars = gb_sets:from_list(ArgNames),
+ Args = vvars_get_variables(Args),
+ DefVars = gb_sets:from_list(Args),
Entry = 0,
State = #vvars{blocks = Blocks,
branch_def_vars = #{ Entry => DefVars },
defined_vars = DefVars},
- ok = vvars_assert_unique(Blocks, ArgNames),
+ ok = vvars_assert_unique(Blocks, Args),
vvars_phi_nodes(vvars_block(Entry, State)).
%% Checks the uniqueness of all variables across all blocks.
--spec vvars_assert_unique(Blocks, [beam_ssa:var_name()]) -> ok when
+-spec vvars_assert_unique(Blocks, [beam_ssa:b_var()]) -> ok when
Blocks :: #{ beam_ssa:label() => beam_ssa:b_blk() }.
vvars_assert_unique(Blocks, Args) ->
BlockIs = [Is || #b_blk{is=Is} <- maps:values(Blocks)],
@@ -124,12 +132,12 @@ vvars_assert_unique(Blocks, Args) ->
ok.
-spec vvars_assert_unique_1(Is, Defined) -> ok when
- Is :: list(beam_ssa:b_set()),
- Defined :: #{ beam_ssa:var_name() => beam_ssa:b_set() }.
-vvars_assert_unique_1([#b_set{dst=#b_var{name=DstName}}=I|Is], Defined) ->
+ Is :: list(beam_ssa:b_set()),
+ Defined :: #{ beam_ssa:b_var() => beam_ssa:b_set() }.
+vvars_assert_unique_1([#b_set{dst=Dst}=I|Is], Defined) ->
case Defined of
- #{DstName:=Old} -> throw({redefined_variable, DstName, Old, I});
- _ -> vvars_assert_unique_1(Is, Defined#{DstName=>I})
+ #{Dst:=Old} -> throw({redefined_variable, Dst, Old, I});
+ _ -> vvars_assert_unique_1(Is, Defined#{Dst=>I})
end;
vvars_assert_unique_1([], Defined) ->
Defined.
@@ -141,17 +149,17 @@ vvars_phi_nodes(#vvars{ blocks = Blocks }=State) ->
ok.
-spec vvars_phi_nodes_1(Is, Id, State) -> ok when
- Is :: list(beam_ssa:b_set()),
- Id :: beam_ssa:label(),
- State :: #vvars{}.
+ Is :: list(beam_ssa:b_set()),
+ Id :: beam_ssa:label(),
+ State :: #vvars{}.
vvars_phi_nodes_1([#b_set{ op = phi, args = Phis }=I | Is], Id, State) ->
ok = vvars_assert_phi_paths(Phis, I, Id, State),
ok = vvars_assert_phi_vars(Phis, I, Id, State),
vvars_phi_nodes_1(Is, Id, State);
vvars_phi_nodes_1([_ | Is], Id, _State) ->
- case [Dst || #b_set{op=phi,dst=#b_var{name=Dst}} <- Is] of
- [Name|_] ->
- throw({phi_inside_block, Name, Id});
+ case [Dst || #b_set{op=phi,dst=Dst} <- Is] of
+ [Var|_] ->
+ throw({phi_inside_block, Var, Id});
[] ->
ok
end;
@@ -161,10 +169,10 @@ vvars_phi_nodes_1([], _Id, _State) ->
%% Checks whether all paths leading to this phi node are represented, and that
%% it doesn't reference any non-existent paths.
-spec vvars_assert_phi_paths(Phis, I, Id, State) -> ok when
- Phis :: list({beam_ssa:argument(), beam_ssa:label()}),
- Id :: beam_ssa:label(),
- I :: beam_ssa:b_set(),
- State :: #vvars{}.
+ Phis :: list({beam_ssa:argument(), beam_ssa:label()}),
+ Id :: beam_ssa:label(),
+ I :: beam_ssa:b_set(),
+ State :: #vvars{}.
vvars_assert_phi_paths(Phis, I, Id, State) ->
BranchKeys = maps:keys(State#vvars.branch_def_vars),
RequiredPaths = ordsets:from_list([From || {From, To} <- BranchKeys, To =:= Id]),
@@ -173,34 +181,34 @@ vvars_assert_phi_paths(Phis, I, Id, State) ->
[_|_]=MissingPaths -> throw({missing_phi_paths, MissingPaths, I});
[] -> ok
end.
- %% %% The following test is sometimes useful to find missing optimizations.
- %% %% It is commented out, though, because it can be triggered by
- %% %% by weird but legal code.
- %% case ordsets:subtract(ProvidedPaths, RequiredPaths) of
- %% [_|_]=GarbagePaths -> throw({garbage_phi_paths, GarbagePaths, I});
- %% [] -> ok
- %% end.
+%% %% The following test is sometimes useful to find missing optimizations.
+%% %% It is commented out, though, because it can be triggered by
+%% %% by weird but legal code.
+%% case ordsets:subtract(ProvidedPaths, RequiredPaths) of
+%% [_|_]=GarbagePaths -> throw({garbage_phi_paths, GarbagePaths, I});
+%% [] -> ok
+%% end.
%% Checks whether all variables used in this phi node are defined in the branch
%% they arrived on.
-spec vvars_assert_phi_vars(Phis, I, Id, State) -> ok when
- Phis :: list({beam_ssa:argument(), beam_ssa:label()}),
- Id :: beam_ssa:label(),
- I :: beam_ssa:b_set(),
- State :: #vvars{}.
+ Phis :: list({beam_ssa:argument(), beam_ssa:label()}),
+ Id :: beam_ssa:label(),
+ I :: beam_ssa:b_set(),
+ State :: #vvars{}.
vvars_assert_phi_vars(Phis, I, Id, #vvars{blocks=Blocks,
branch_def_vars=BranchDefVars}) ->
Vars = [{Var, From} || {#b_var{}=Var, From} <- Phis],
- foreach(fun({#b_var{name=VarName}, From}) ->
+ foreach(fun({Var, From}) ->
BranchKey = {From, Id},
case BranchDefVars of
#{BranchKey:=DefVars} ->
- case gb_sets:is_member(VarName, DefVars) of
+ case gb_sets:is_member(Var, DefVars) of
true -> ok;
- false -> throw({unknown_variable, VarName, I})
+ false -> throw({unknown_variable, Var, I})
end;
#{} ->
- throw({unknown_phi_variable, VarName, BranchKey, I})
+ throw({unknown_phi_variable, Var, BranchKey, I})
end
end, Vars),
Labels = [From || {#b_literal{},From} <- Phis],
@@ -214,32 +222,44 @@ vvars_assert_phi_vars(Phis, I, Id, #vvars{blocks=Blocks,
end, Labels).
-spec vvars_block(Id, State) -> #vvars{} when
- Id :: beam_ssa:label(),
- State :: #vvars{}.
+ Id :: beam_ssa:label(),
+ State :: #vvars{}.
vvars_block(Id, State0) ->
#{ Id := #b_blk{ is = Is, last = Terminator} } = State0#vvars.blocks,
#{ Id := DefVars } = State0#vvars.branch_def_vars,
State = State0#vvars{ defined_vars = DefVars },
vvars_terminator(Terminator, Id, vvars_block_1(Is, State)).
--spec vvars_block_1(Blocks, State) -> #vvars{} when
- Blocks :: list(beam_ssa:b_blk()),
- State :: #vvars{}.
+-spec vvars_block_1(Is, State) -> #vvars{} when
+ Is :: list(#b_set{}),
+ State :: #vvars{}.
vvars_block_1([], State) ->
State;
-vvars_block_1([#b_set{ dst = #b_var{ name = DstName }, op = phi } | Is], State0) ->
+vvars_block_1([#b_set{dst=OpVar,args=OpArgs}=I,
+ #b_set{op=succeeded,args=[OpVar],dst=SuccVar}], State) ->
+ ok = vvars_assert_args(OpArgs, I, State),
+ vvars_save_var(SuccVar, vvars_save_var(OpVar, State));
+vvars_block_1([#b_set{op=succeeded,args=Args}=I | [_|_]], State) ->
+ ok = vvars_assert_args(Args, I, State),
+ %% 'succeeded' must be the last instruction in its block.
+ throw({succeeded_not_last, I});
+vvars_block_1([#b_set{op=succeeded,args=Args}=I], State)->
+ ok = vvars_assert_args(Args, I, State),
+ %% 'succeeded' must be be directly preceded by the operation it checks.
+ throw({succeeded_not_preceded, I});
+vvars_block_1([#b_set{ dst = Dst, op = phi } | Is], State) ->
%% We don't check phi node arguments at this point since we may not have
%% visited their definition yet. They'll be handled later on in
%% vvars_phi_nodes/1 after all blocks are processed.
- vvars_block_1(Is, vvars_save_var(DstName, State0));
-vvars_block_1([#b_set{ dst = #b_var{ name = DstName }, args = Args }=I | Is], State0) ->
- ok = vvars_assert_args(Args, I, State0),
- vvars_block_1(Is, vvars_save_var(DstName, State0)).
+ vvars_block_1(Is, vvars_save_var(Dst, State));
+vvars_block_1([#b_set{ dst = Dst, args = Args }=I | Is], State) ->
+ ok = vvars_assert_args(Args, I, State),
+ vvars_block_1(Is, vvars_save_var(Dst, State)).
-spec vvars_terminator(Terminator, From, State) -> #vvars{} when
- Terminator :: beam_ssa:terminator(),
- From :: beam_ssa:label(),
- State :: #vvars{}.
+ Terminator :: beam_ssa:terminator(),
+ From :: beam_ssa:label(),
+ State :: #vvars{}.
vvars_terminator(#b_ret{ arg = Arg }=I, _From, State) ->
ok = vvars_assert_args([Arg], I, State),
State;
@@ -264,62 +284,62 @@ vvars_terminator(#b_br{ bool = Arg, succ = Succ, fail = Fail }=I, From, State) -
vvars_terminator_1(Labels, From, State).
-spec vvars_terminator_1(Labels, From, State) -> #vvars{} when
- Labels :: list(beam_ssa:label()),
- From :: beam_ssa:label(),
- State :: #vvars{}.
+ Labels :: list(beam_ssa:label()),
+ From :: beam_ssa:label(),
+ State :: #vvars{}.
vvars_terminator_1(Labels0, From, State0) ->
%% Filter out all branches that have already been taken. This should result
%% in either all of Labels0 or an empty list.
Labels = [To || To <- Labels0,
- not maps:is_key({From, To}, State0#vvars.branch_def_vars)],
+ not maps:is_key({From, To}, State0#vvars.branch_def_vars)],
true = Labels =:= Labels0 orelse Labels =:= [], %Assertion
State1 = foldl(fun(To, State) ->
- vvars_save_branch(From, To, State)
+ vvars_save_branch(From, To, State)
end, State0, Labels),
foldl(fun(To, State) ->
- vvars_block(To, State)
+ vvars_block(To, State)
end, State1, Labels).
%% Gets all variable names in args, ignoring literals etc
--spec vvars_get_varnames(Args) -> list(beam_ssa:var_name()) when
- Args :: list(beam_ssa:argument()).
-vvars_get_varnames(Args) ->
- [Name || #b_var{ name = Name } <- Args].
+-spec vvars_get_variables(Args) -> list(beam_ssa:b_var()) when
+ Args :: list(beam_ssa:argument()).
+vvars_get_variables(Args) ->
+ [Var || #b_var{}=Var <- Args].
%% Checks that all variables in Args are defined in all paths leading to the
%% current State.
-spec vvars_assert_args(Args, I, State) -> ok when
- Args :: list(beam_ssa:argument()),
- I :: beam_ssa:terminator() | beam_ssa:b_set(),
- State :: #vvars{}.
+ Args :: list(beam_ssa:argument()),
+ I :: beam_ssa:terminator() | beam_ssa:b_set(),
+ State :: #vvars{}.
vvars_assert_args(Args, I, #vvars{defined_vars=DefVars}=State) ->
foreach(fun(#b_remote{mod=Mod,name=Name}) ->
vvars_assert_args([Mod,Name], I, State);
- (#b_var{name=Name}) ->
- case gb_sets:is_member(Name, DefVars) of
+ (#b_var{}=Var) ->
+ case gb_sets:is_member(Var, DefVars) of
true -> ok;
- false -> throw({unknown_variable,Name,I})
+ false -> throw({unknown_variable,Var,I})
end;
(_) -> ok
end, Args).
%% Checks that all given labels are defined in State.
-spec vvars_assert_labels(Labels, I, State) -> ok when
- Labels :: list(beam_ssa:label()),
- I :: beam_ssa:terminator(),
- State :: #vvars{}.
+ Labels :: list(beam_ssa:label()),
+ I :: beam_ssa:terminator(),
+ State :: #vvars{}.
vvars_assert_labels(Labels, I, #vvars{blocks=Blocks}) ->
foreach(fun(Label) ->
- case maps:is_key(Label, Blocks) of
- false -> throw({unknown_block, Label, I});
- true -> ok
- end
+ case maps:is_key(Label, Blocks) of
+ false -> throw({unknown_block, Label, I});
+ true -> ok
+ end
end, Labels).
-spec vvars_save_branch(From, To, State) -> #vvars{} when
- From :: beam_ssa:label(),
- To :: beam_ssa:label(),
- State :: #vvars{}.
+ From :: beam_ssa:label(),
+ To :: beam_ssa:label(),
+ State :: #vvars{}.
vvars_save_branch(From, To, State) ->
DefVars = State#vvars.defined_vars,
Branches0 = State#vvars.branch_def_vars,
@@ -335,15 +355,15 @@ vvars_save_branch(From, To, State) ->
end.
-spec vvars_merge_branches(New, Existing) -> defined_vars() when
- New :: defined_vars(),
- Existing :: defined_vars().
+ New :: defined_vars(),
+ Existing :: defined_vars().
vvars_merge_branches(New, Existing) ->
gb_sets:intersection(New, Existing).
--spec vvars_save_var(VarName, State) -> #vvars{} when
- VarName :: beam_ssa:var_name(),
- State :: #vvars{}.
-vvars_save_var(VarName, State0) ->
+-spec vvars_save_var(Var, State) -> #vvars{} when
+ Var :: #b_var{},
+ State :: #vvars{}.
+vvars_save_var(Var, State0) ->
%% vvars_assert_unique guarantees that variables are never set twice.
- DefVars = gb_sets:insert(VarName, State0#vvars.defined_vars),
+ DefVars = gb_sets:insert(Var, State0#vvars.defined_vars),
State0#vvars{ defined_vars = DefVars }.
diff --git a/lib/compiler/src/beam_ssa_opt.erl b/lib/compiler/src/beam_ssa_opt.erl
index cce539f582..580abf4ed9 100644
--- a/lib/compiler/src/beam_ssa_opt.erl
+++ b/lib/compiler/src/beam_ssa_opt.erl
@@ -1960,12 +1960,24 @@ is_merge_allowed(_, #b_blk{}, #b_blk{is=[#b_set{op=exception_trampoline}|_]}) ->
false;
is_merge_allowed(_, #b_blk{is=[#b_set{op=exception_trampoline}|_]}, #b_blk{}) ->
false;
-is_merge_allowed(L, #b_blk{last=#b_br{}}=Blk, #b_blk{}) ->
+is_merge_allowed(L, #b_blk{last=#b_br{}}=Blk, #b_blk{is=Is}) ->
%% The predecessor block must have exactly one successor (L) for
%% the merge to be safe.
case beam_ssa:successors(Blk) of
- [L] -> true;
- [_|_] -> false
+ [L] ->
+ case Is of
+ [#b_set{op=phi,args=[_]}|_] ->
+ %% The type optimizer pass must have been
+ %% turned off, since it would have removed this
+ %% redundant phi node. Refuse to merge the blocks
+ %% to ensure that this phi node remains at the
+ %% beginning of a block.
+ false;
+ _ ->
+ true
+ end;
+ [_|_] ->
+ false
end;
is_merge_allowed(_, #b_blk{last=#b_switch{}}, #b_blk{}) ->
false.
diff --git a/lib/compiler/src/beam_ssa_pre_codegen.erl b/lib/compiler/src/beam_ssa_pre_codegen.erl
index 28b2e55c43..bf2503a2a5 100644
--- a/lib/compiler/src/beam_ssa_pre_codegen.erl
+++ b/lib/compiler/src/beam_ssa_pre_codegen.erl
@@ -603,6 +603,10 @@ bs_instrs([{L,#b_blk{is=Is0}=Blk}|Bs], CtxChain, Acc0) ->
bs_instrs([], _, Acc) ->
reverse(Acc).
+bs_instrs_is([#b_set{op=succeeded}=I|Is], CtxChain, Acc) ->
+ %% This instruction refers to a specific operation, so we must not
+ %% substitute the context argument.
+ bs_instrs_is(Is, CtxChain, [I | Acc]);
bs_instrs_is([#b_set{op=Op,args=Args0}=I0|Is], CtxChain, Acc) ->
Args = [bs_subst_ctx(A, CtxChain) || A <- Args0],
I1 = I0#b_set{args=Args},
@@ -703,20 +707,30 @@ legacy_bs_is([], _Last, _IsYreg, Count, Copies, Acc) ->
exception_trampolines(#st{ssa=Blocks0}=St) ->
RPO = reverse(beam_ssa:rpo(Blocks0)),
- Blocks = et_1(RPO, #{}, Blocks0),
+ Blocks = et_1(RPO, #{}, #{}, Blocks0),
St#st{ssa=Blocks}.
-et_1([L | Ls], Trampolines, Blocks) ->
+et_1([L | Ls], Trampolines, Exceptions, Blocks) ->
#{ L := #b_blk{is=Is,last=Last0}=Block0 } = Blocks,
case {Is, Last0} of
- {[#b_set{op=exception_trampoline}], #b_br{succ=Succ}} ->
- et_1(Ls, Trampolines#{ L => Succ }, maps:remove(L, Blocks));
+ {[#b_set{op=exception_trampoline,args=[Arg]}], #b_br{succ=Succ}} ->
+ et_1(Ls,
+ Trampolines#{ L => Succ },
+ Exceptions#{ L => Arg },
+ maps:remove(L, Blocks));
{_, #b_br{succ=Same,fail=Same}} when Same =:= ?EXCEPTION_BLOCK ->
%% The exception block is just a marker saying that we should raise
%% an exception (= {f,0}) instead of jumping to a particular fail
%% block. Since it's not a reachable block we can't allow
%% unconditional jumps to it except through a trampoline.
error({illegal_jump_to_exception_block, L});
+ {_, #b_br{succ=Same,fail=Same}}
+ when map_get(Same, Trampolines) =:= ?EXCEPTION_BLOCK ->
+ %% This block always fails at runtime (and we are not in a
+ %% try/catch); rewrite the terminator to a return.
+ Last = #b_ret{arg=map_get(Same, Exceptions)},
+ Block = Block0#b_blk{last=Last},
+ et_1(Ls, Trampolines, Exceptions, Blocks#{ L := Block });
{_, #b_br{succ=Succ0,fail=Fail0}} ->
Succ = maps:get(Succ0, Trampolines, Succ0),
Fail = maps:get(Fail0, Trampolines, Fail0),
@@ -724,14 +738,14 @@ et_1([L | Ls], Trampolines, Blocks) ->
Succ =/= Succ0; Fail =/= Fail0 ->
Last = Last0#b_br{succ=Succ,fail=Fail},
Block = Block0#b_blk{last=Last},
- et_1(Ls, Trampolines, Blocks#{ L := Block });
+ et_1(Ls, Trampolines, Exceptions, Blocks#{ L := Block });
Succ =:= Succ0, Fail =:= Fail0 ->
- et_1(Ls, Trampolines, Blocks)
+ et_1(Ls, Trampolines, Exceptions, Blocks)
end;
{_, _} ->
- et_1(Ls, Trampolines, Blocks)
+ et_1(Ls, Trampolines, Exceptions, Blocks)
end;
-et_1([], _Trampolines, Blocks) ->
+et_1([], _Trampolines, _Exceptions, Blocks) ->
Blocks.
%% sanitize(St0) -> St.
@@ -1062,9 +1076,8 @@ use_set_tuple_element(#st{ssa=Blocks0}=St) ->
Blocks = use_ste_1(RPO, Uses, Blocks0),
St#st{ssa=Blocks}.
-use_ste_1([L|Ls], Uses, Blocks0) ->
- {Blk0,Blocks} = use_ste_across(L, Uses, Blocks0),
- #b_blk{is=Is0} = Blk0,
+use_ste_1([L|Ls], Uses, Blocks) ->
+ #b_blk{is=Is0} = Blk0 = map_get(L, Blocks),
case use_ste_is(Is0, Uses) of
Is0 ->
use_ste_1(Ls, Uses, Blocks);
@@ -1127,69 +1140,6 @@ extract_ste(#b_set{op=call,dst=Dst,
end;
extract_ste(#b_set{}) -> none.
-%%% Optimize accross blocks within a try/catch block.
-
-use_ste_across(L, Uses, Blocks) ->
- case map_get(L, Blocks) of
- #b_blk{last=#b_br{bool=#b_var{}}}=Blk ->
- try
- use_ste_across_1(L, Blk, Uses, Blocks)
- catch
- throw:not_possible ->
- {Blk,Blocks}
- end;
- #b_blk{}=Blk ->
- {Blk,Blocks}
- end.
-
-use_ste_across_1(L, Blk0, Uses, Blocks0) ->
- #b_blk{is=IsThis,last=#b_br{bool=Bool,succ=Next}} = Blk0,
- case reverse(IsThis) of
- [#b_set{op=succeeded,dst=Bool,args=[Result]}=Succ0,
- #b_set{op=call,args=[#b_remote{}|_],dst=Result}=Call1|Prefix] ->
- case is_single_use(Bool, Uses) andalso
- is_n_uses(2, Result, Uses) of
- true -> ok;
- false -> throw(not_possible)
- end,
- Call2 = use_ste_across_next(Next, Uses, Blocks0),
- Is = [Call1,Call2],
- case use_ste_is(Is, decrement_uses(Result, Uses)) of
- [#b_set{}=Call,#b_set{op=set_tuple_element}=Ste] ->
- Blocks1 = use_ste_fix_next(Ste, Next, Blocks0),
- Succ = Succ0#b_set{args=[Call#b_set.dst]},
- Blk = Blk0#b_blk{is=reverse(Prefix, [Call,Succ])},
- Blocks = Blocks1#{L:=Blk},
- {Blk,Blocks};
- _ ->
- throw(not_possible)
- end;
- _ ->
- throw(not_possible)
- end.
-
-use_ste_across_next(Next, Uses, Blocks) ->
- case map_get(Next, Blocks) of
- #b_blk{is=[#b_set{op=call,dst=Result,args=[#b_remote{}|_]}=Call,
- #b_set{op=succeeded,dst=Bool,args=[Result]}],
- last=#b_br{bool=Bool}} ->
- case is_single_use(Bool, Uses) andalso
- is_n_uses(2, Result, Uses) of
- true -> ok;
- false -> throw(not_possible)
- end,
- Call;
- #b_blk{} ->
- throw(not_possible)
- end.
-
-use_ste_fix_next(Ste, Next, Blocks) ->
- Blk0 = map_get(Next, Blocks),
- #b_blk{is=[#b_set{op=call},#b_set{op=succeeded}],last=Br0} = Blk0,
- Br = beam_ssa:normalize(Br0#b_br{bool=#b_literal{val=true}}),
- Blk = Blk0#b_blk{is=[Ste],last=Br},
- Blocks#{Next:=Blk}.
-
%% Count how many times each variable is used.
count_uses(Blocks) ->
@@ -1199,7 +1149,7 @@ count_uses_blk([#b_blk{is=Is,last=Last}|Bs], CountMap0) ->
F = fun(I, CountMap) ->
foldl(fun(Var, Acc) ->
case Acc of
- #{Var:=3} -> Acc;
+ #{Var:=2} -> Acc;
#{Var:=C} -> Acc#{Var:=C+1};
#{} -> Acc#{Var=>1}
end
@@ -1209,16 +1159,6 @@ count_uses_blk([#b_blk{is=Is,last=Last}|Bs], CountMap0) ->
count_uses_blk(Bs, CountMap);
count_uses_blk([], CountMap) -> CountMap.
-decrement_uses(V, Uses) ->
- #{V:=C} = Uses,
- Uses#{V:=C-1}.
-
-is_n_uses(N, V, Uses) ->
- case Uses of
- #{V:=N} -> true;
- #{} -> false
- end.
-
is_single_use(V, Uses) ->
case Uses of
#{V:=1} -> true;
@@ -1401,14 +1341,9 @@ need_frame_1([#b_set{op=call,args=[Func|_]}|Is], Context) ->
#b_remote{mod=#b_literal{val=Mod},
name=#b_literal{val=Name},
arity=Arity} when is_atom(Mod), is_atom(Name) ->
- case erl_bifs:is_exit_bif(Mod, Name, Arity) of
- true ->
- false;
- false ->
- Context =:= body orelse
- Is =/= [] orelse
- is_trap_bif(Mod, Name, Arity)
- end;
+ Context =:= body orelse
+ Is =/= [] orelse
+ is_trap_bif(Mod, Name, Arity);
#b_remote{} ->
%% This is an apply(), which always needs a frame.
true;
@@ -1614,21 +1549,51 @@ fix_receive([], _Defs, Blocks, Count) ->
{Blocks,Count}.
%% find_loop_exit([Label], Blocks) -> Label | none.
-%% Find the block to which control is transferred when the
-%% the receive loop is exited.
-
-find_loop_exit([L1,L2|_Ls], Blocks) ->
- Path1 = beam_ssa:rpo([L1], Blocks),
- Path2 = beam_ssa:rpo([L2], Blocks),
- find_loop_exit_1(Path1, cerl_sets:from_list(Path2));
-find_loop_exit(_, _) -> none.
-
-find_loop_exit_1([H|T], OtherPath) ->
- case cerl_sets:is_element(H, OtherPath) of
- true -> H;
- false -> find_loop_exit_1(T, OtherPath)
+%% Given the list of all blocks with the remove_message instructions
+%% for this receive, find the block to which control is transferred
+%% when the receive loop is exited (if any).
+
+find_loop_exit([_,_|_]=RmBlocks, Blocks) ->
+ %% We used to only analyze the path from two of the remove_message
+ %% blocks. That would fail to find a common block if one or both
+ %% of the blocks happened to raise an exception. To be sure that
+ %% we always find a common block if there is one (shared by at
+ %% least two clauses), we must analyze the path from all
+ %% remove_message blocks.
+ {Dominators,_} = beam_ssa:dominators(Blocks),
+ RmSet = cerl_sets:from_list(RmBlocks),
+ Rpo = beam_ssa:rpo(RmBlocks, Blocks),
+ find_loop_exit_1(Rpo, RmSet, Dominators);
+find_loop_exit(_, _) ->
+ %% There is (at most) a single clause. There is no common
+ %% loop exit block.
+ none.
+
+find_loop_exit_1([?EXCEPTION_BLOCK|Ls], RmSet, Dominators) ->
+ %% ?EXCEPTION_BLOCK is a marker and not an actual block, so it is not
+ %% the block we are looking for.
+ find_loop_exit_1(Ls, RmSet, Dominators);
+find_loop_exit_1([L|Ls], RmSet, Dominators) ->
+ DomBy = map_get(L, Dominators),
+ case any(fun(E) -> cerl_sets:is_element(E, RmSet) end, DomBy) of
+ true ->
+ %% This block is dominated by one of the remove_message blocks,
+ %% which means that the block is part of only one clause.
+ %% It is not the block we are looking for.
+ find_loop_exit_1(Ls, RmSet, Dominators);
+ false ->
+ %% This block is the first block that is not dominated by
+ %% any of the blocks with remove_message instructions,
+ %% which means that at least two of the receive clauses
+ %% will ultimately transfer control to it. It is the block
+ %% we are looking for.
+ L
end;
-find_loop_exit_1([], _) -> none.
+find_loop_exit_1([], _, _) ->
+ %% None of clauses transfers control to a common block after the receive
+ %% statement. That means that the receive statement is a the end of a
+ %% function (or that all clauses raise exceptions).
+ none.
%% find_rm_blocks(StartLabel, Blocks) -> [Label].
%% Find all blocks that start with remove_message within the receive
diff --git a/lib/compiler/src/beam_ssa_share.erl b/lib/compiler/src/beam_ssa_share.erl
index 85ab088d14..fa728992f8 100644
--- a/lib/compiler/src/beam_ssa_share.erl
+++ b/lib/compiler/src/beam_ssa_share.erl
@@ -303,8 +303,12 @@ canonical_is([#b_ret{arg=Arg}], VarMap, Acc0) ->
Acc0
end,
{{ret,canonical_arg(Arg, VarMap),Acc1},VarMap};
-canonical_is([#b_br{bool=#b_var{},fail=Fail}], VarMap, Acc) ->
- {{br,succ,Fail,Acc},VarMap};
+canonical_is([#b_br{bool=#b_var{}=Arg,fail=Fail}], VarMap, Acc) ->
+ %% A previous buggy version of this code omitted the canonicalized
+ %% argument in the return value. Unfortunately, that worked most
+ %% of the time, except when `br` terminator referenced a variable
+ %% defined in a previous block instead of in the same block.
+ {{br,canonical_arg(Arg, VarMap),succ,Fail,Acc},VarMap};
canonical_is([#b_br{succ=Succ}], VarMap, Acc) ->
{{br,Succ,Acc},VarMap};
canonical_is([], VarMap, Acc) ->
diff --git a/lib/compiler/src/beam_ssa_type.erl b/lib/compiler/src/beam_ssa_type.erl
index 0912ecb2c2..d93191c689 100644
--- a/lib/compiler/src/beam_ssa_type.erl
+++ b/lib/compiler/src/beam_ssa_type.erl
@@ -25,7 +25,7 @@
-include("beam_types.hrl").
-import(lists, [all/2,any/2,droplast/1,duplicate/2,foldl/3,last/1,member/2,
- keyfind/3,reverse/1,reverse/2,sort/1,split/2,zip/2]).
+ keyfind/3,reverse/1,sort/1,split/2,zip/2]).
-define(UNICODE_MAX, (16#10FFFF)).
@@ -171,27 +171,17 @@ opt([], D, Acc) ->
opt_1(L, #b_blk{is=Is0,last=Last0}=Blk0, Bs, Ts0,
#d{ds=Ds0,sub=Sub0,func_db=Fdb0}=D0, Acc) ->
- case opt_is(Is0, Ts0, Ds0, Fdb0, D0, Sub0, []) of
- {Is,Ts,Ds,Fdb,Sub} ->
- D1 = D0#d{ds=Ds,sub=Sub,func_db=Fdb},
- Last1 = simplify_terminator(Last0, Sub, Ts, Ds),
- Last2 = opt_terminator(Last1, Ts, Ds),
- {Last,D} = update_successors(Last2, Ts, D1),
- Blk = Blk0#b_blk{is=Is,last=Last},
- opt(Bs, D, [{L,Blk}|Acc]);
- {no_return,Ret,Is,Ds,Fdb,Sub} ->
- %% This call will never reach the successor block.
- %% Rewrite the terminator to a 'ret', and remove
- %% all type information for this label. That can
- %% potentially narrow the type of the phi node
- %% in the former successor.
- Ls = maps:remove(L, D0#d.ls),
- RetType = beam_types:join([none|D0#d.ret_type]),
- D = D0#d{ds=Ds,ls=Ls,sub=Sub,
- func_db=Fdb,ret_type=[RetType]},
- Blk = Blk0#b_blk{is=Is,last=Ret},
- opt(Bs, D, [{L,Blk}|Acc])
- end.
+ {Is,Ts,Ds,Fdb,Sub} = opt_is(Is0, Ts0, Ds0, Fdb0, D0, Sub0, []),
+
+ D1 = D0#d{ds=Ds,sub=Sub,func_db=Fdb},
+
+ Last1 = simplify_terminator(Last0, Sub, Ts, Ds),
+ Last2 = opt_terminator(Last1, Ts, Ds),
+
+ {Last, D} = update_successors(Last2, Ts, D1),
+
+ Blk = Blk0#b_blk{is=Is,last=Last},
+ opt(Bs, D, [{L,Blk}|Acc]).
simplify_terminator(#b_br{bool=Bool}=Br, Sub, Ts, _Ds) ->
Br#b_br{bool=simplify_arg(Bool, Sub, Ts)};
@@ -224,166 +214,74 @@ opt_is([#b_set{op=phi,dst=Dst,args=Args0}=I0|Is],
Ds = Ds0#{Dst=>I},
opt_is(Is, Ts, Ds, Fdb, D, Sub0, [I|Acc])
end;
-opt_is([#b_set{op=call,args=Args0,dst=Dst}=I0|Is],
- Ts0, Ds0, Fdb0, D, Sub0, Acc) ->
- Args = simplify_args(Args0, Sub0, Ts0),
+opt_is([#b_set{op=call,args=Args0}=I0|Is],
+ Ts, Ds, Fdb0, D, Sub, Acc) ->
+ Args = simplify_args(Args0, Sub, Ts),
I1 = beam_ssa:normalize(I0#b_set{args=Args}),
- {Ts1,Ds,Fdb,I2} = opt_call(I1, D, Ts0, Ds0, Fdb0),
- case {map_get(Dst, Ts1),Is} of
- {Type,[#b_set{op=succeeded}]} when Type =/= none ->
- %% This call instruction is inside a try/catch
- %% block. Don't attempt to simplify it.
- opt_is(Is, Ts1, Ds, Fdb, D, Sub0, [I2|Acc]);
- {none,[#b_set{op=succeeded}]} ->
- %% This call instruction is inside a try/catch
- %% block, but we know it will never return and
- %% later optimizations may try to exploit that.
- %%
- %% For example, if we have an expression that
- %% either returns this call or a tuple, we know
- %% that the expression always returns a tuple
- %% and can turn a later element/3 into
- %% get_tuple_element.
- %%
- %% This is sound but difficult to validate in a
- %% meaningful way as try/catch currently forces
- %% us to maintain the illusion that the success
- %% block is reachable even when its not, so we
- %% disable the optimization to keep things
- %% simple.
- Ts = Ts1#{ Dst := any },
- opt_is(Is, Ts, Ds, Fdb, D, Sub0, [I2|Acc]);
- {none,_} ->
- %% This call never returns. The rest of the
- %% instructions will not be executed.
- Ret = #b_ret{arg=Dst},
- {no_return,Ret,reverse(Acc, [I2]),Ds,Fdb,Sub0};
- {_,_} ->
- case simplify_call(I2) of
- #b_set{}=I ->
- opt_is(Is, Ts1, Ds, Fdb, D, Sub0, [I|Acc]);
- #b_literal{}=Lit ->
- Sub = Sub0#{Dst=>Lit},
- Ts = maps:remove(Dst, Ts1),
- opt_is(Is, Ts, Ds0, Fdb, D, Sub, Acc);
- #b_var{}=Var ->
- Ts = maps:remove(Dst, Ts1),
- Sub = Sub0#{Dst=>Var},
- opt_is(Is, Ts, Ds0, Fdb, D, Sub, Acc)
- end
- end;
+ {I, Fdb} = opt_call(I1, Ts, Fdb0, D),
+ opt_simplify(I, Is, Ts, Ds, Fdb, D, Sub, Acc);
opt_is([#b_set{op=make_fun,args=Args0}=I0|Is],
Ts0, Ds0, Fdb0, D, Sub0, Acc) ->
Args = simplify_args(Args0, Sub0, Ts0),
I1 = beam_ssa:normalize(I0#b_set{args=Args}),
{Ts,Ds,Fdb,I} = opt_make_fun(I1, D, Ts0, Ds0, Fdb0),
opt_is(Is, Ts, Ds, Fdb, D, Sub0, [I|Acc]);
-opt_is([#b_set{op=succeeded,args=[Arg],dst=Dst}=I],
+opt_is([#b_set{op=succeeded,args=[Arg],dst=Dst,anno=Anno}=I],
Ts0, Ds0, Fdb, D, Sub0, Acc) ->
- case Ds0 of
- #{ Arg := #b_set{op=call} } ->
- %% The success check of a call is part of exception handling and
- %% must not be optimized away. We still have to update its type
- %% though.
- Ts = update_types(I, Ts0, Ds0),
- Ds = Ds0#{Dst=>I},
-
- opt_is([], Ts, Ds, Fdb, D, Sub0, [I|Acc]);
- #{} ->
- Args = simplify_args([Arg], Sub0, Ts0),
- Type = type(succeeded, Args, Ts0, Ds0),
- case beam_types:get_singleton_value(Type) of
- {ok, Lit} ->
- Sub = Sub0#{Dst=>#b_literal{val=Lit}},
- opt_is([], Ts0, Ds0, Fdb, D, Sub, Acc);
- error ->
- Ts = Ts0#{Dst=>Type},
- Ds = Ds0#{Dst=>I},
- opt_is([], Ts, Ds, Fdb, D, Sub0, [I|Acc])
- end
+ Type = case Ds0 of
+ #{ Arg := #b_set{op=call} } ->
+ %% Calls can always throw exceptions and their return types
+ %% are what they return on success, so we must avoid
+ %% simplifying arguments in case `Arg` would become a
+ %% literal, which would trick 'succeeded' into thinking it
+ %% can't fail.
+ type(succeeded, [Arg], Anno, Ts0, Ds0);
+ #{} ->
+ Args = simplify_args([Arg], Sub0, Ts0),
+ type(succeeded, Args, Anno, Ts0, Ds0)
+ end,
+ case beam_types:get_singleton_value(Type) of
+ {ok, Lit} ->
+ Sub = Sub0#{ Dst => #b_literal{val=Lit} },
+ opt_is([], Ts0, Ds0, Fdb, D, Sub, Acc);
+ error ->
+ Ts = Ts0#{ Dst => Type },
+ Ds = Ds0#{ Dst => I },
+ opt_is([], Ts, Ds, Fdb, D, Sub0, [I | Acc])
end;
-opt_is([#b_set{args=Args0,dst=Dst}=I0|Is],
- Ts0, Ds0, Fdb, D, Sub0, Acc) ->
- Args = simplify_args(Args0, Sub0, Ts0),
- I1 = beam_ssa:normalize(I0#b_set{args=Args}),
- case simplify(I1, Ts0) of
+opt_is([#b_set{args=Args0}=I0|Is],
+ Ts, Ds, Fdb, D, Sub, Acc) ->
+ Args = simplify_args(Args0, Sub, Ts),
+ I = beam_ssa:normalize(I0#b_set{args=Args}),
+ opt_simplify(I, Is, Ts, Ds, Fdb, D, Sub, Acc);
+opt_is([], Ts, Ds, Fdb, _D, Sub, Acc) ->
+ {reverse(Acc), Ts, Ds, Fdb, Sub}.
+
+opt_simplify(#b_set{dst=Dst}=I0, Is, Ts0, Ds0, Fdb, D, Sub0, Acc) ->
+ case simplify(I0, Ts0) of
#b_set{}=I2 ->
I = beam_ssa:normalize(I2),
Ts = update_types(I, Ts0, Ds0),
- Ds = Ds0#{Dst=>I},
+ Ds = Ds0#{ Dst => I },
opt_is(Is, Ts, Ds, Fdb, D, Sub0, [I|Acc]);
#b_literal{}=Lit ->
- Sub = Sub0#{Dst=>Lit},
+ Sub = Sub0#{ Dst => Lit },
opt_is(Is, Ts0, Ds0, Fdb, D, Sub, Acc);
#b_var{}=Var ->
case Is of
[#b_set{op=succeeded,dst=SuccDst,args=[Dst]}] ->
- %% We must remove this 'succeeded' instruction.
- Sub = Sub0#{Dst=>Var,SuccDst=>#b_literal{val=true}},
+ %% We must remove this 'succeeded' instruction since the
+ %% variable it checks is gone.
+ Sub = Sub0#{ Dst => Var, SuccDst => #b_literal{val=true} },
opt_is([], Ts0, Ds0, Fdb, D, Sub, Acc);
_ ->
- Sub = Sub0#{Dst=>Var},
+ Sub = Sub0#{ Dst => Var},
opt_is(Is, Ts0, Ds0, Fdb, D, Sub, Acc)
end
- end;
-opt_is([], Ts, Ds, Fdb, _D, Sub, Acc) ->
- {reverse(Acc), Ts, Ds, Fdb, Sub}.
-
-simplify_call(#b_set{op=call,args=[#b_remote{}=Rem|Args]}=I) ->
- case Rem of
- #b_remote{mod=#b_literal{val=Mod},
- name=#b_literal{val=Name}} ->
- case erl_bifs:is_pure(Mod, Name, length(Args)) of
- true ->
- simplify_remote_call(Mod, Name, Args, I);
- false ->
- I
- end;
- #b_remote{} ->
- I
- end;
-simplify_call(I) -> I.
-
-%% Simplify a remote call to a pure BIF.
-simplify_remote_call(erlang, '++', [#b_literal{val=[]},Tl], _I) ->
- Tl;
-simplify_remote_call(erlang, setelement,
- [#b_literal{val=Pos},
- #b_literal{val=Tuple},
- #b_var{}=Value], I)
- when is_integer(Pos), 1 =< Pos, Pos =< tuple_size(Tuple) ->
- %% Position is a literal integer and the shape of the
- %% tuple is known.
- Els0 = [#b_literal{val=El} || El <- tuple_to_list(Tuple)],
- {Bef,[_|Aft]} = split(Pos - 1, Els0),
- Els = Bef ++ [Value|Aft],
- I#b_set{op=put_tuple,args=Els};
-simplify_remote_call(Mod, Name, Args0, I) ->
- case make_literal_list(Args0) of
- none ->
- I;
- Args ->
- %% The arguments are literals. Try to evaluate the BIF.
- try apply(Mod, Name, Args) of
- Val ->
- case cerl:is_literal_term(Val) of
- true ->
- #b_literal{val=Val};
- false ->
- %% The value can't be expressed as a literal
- %% (e.g. a pid).
- I
- end
- catch
- _:_ ->
- %% Failed. Don't bother trying to optimize
- %% the call.
- I
- end
end.
-opt_call(#b_set{dst=Dst,args=[#b_local{}=Callee|Args]}=I0, D, Ts0, Ds0, Fdb0) ->
- {Ts, Ds, I} = opt_local_call(I0, Ts0, Ds0, Fdb0),
+opt_call(#b_set{dst=Dst,args=[#b_local{}=Callee|Args]}=I0, Ts, Fdb0, D) ->
+ I = opt_local_call_return(I0, Callee, Fdb0),
case Fdb0 of
#{ Callee := #func_info{exported=false,arg_types=ArgTypes0}=Info } ->
%% Match contexts are treated as bitstrings when optimizing
@@ -400,36 +298,22 @@ opt_call(#b_set{dst=Dst,args=[#b_local{}=Callee|Args]}=I0, D, Ts0, Ds0, Fdb0) ->
ArgTypes = update_arg_types(Types, ArgTypes0, CallId),
Fdb = Fdb0#{ Callee => Info#func_info{arg_types=ArgTypes} },
- {Ts, Ds, Fdb, I};
+ {I, Fdb};
#{} ->
%% We can't narrow the argument types of exported functions as they
%% can receive anything as part of an external call.
- {Ts, Ds, Fdb0, I}
+ {I, Fdb0}
end;
-opt_call(#b_set{dst=Dst,args=[#b_var{}=Fun|Args]}=I, _D, Ts0, Ds0, Fdb) ->
- Type = #t_fun{arity=length(Args)},
- Ts = Ts0#{ Fun => Type, Dst => any },
- Ds = Ds0#{ Dst => I },
- {Ts, Ds, Fdb, I};
-opt_call(#b_set{dst=Dst}=I, _D, Ts0, Ds0, Fdb) ->
- %% #b_remote{} and literal funs
- Ts = update_types(I, Ts0, Ds0),
- Ds = Ds0#{ Dst => I },
- {Ts, Ds, Fdb, I}.
+opt_call(I, _Ts, Fdb, _D) ->
+ {I, Fdb}.
-opt_local_call(#b_set{dst=Dst,args=[Id|_]}=I0, Ts0, Ds0, Fdb) ->
- Type = case Fdb of
- #{ Id := #func_info{ret_type=[T]} } -> T;
- #{} -> any
- end,
- I = case Type of
- any -> I0;
- none -> I0;
- _ -> beam_ssa:add_anno(result_type, Type, I0)
- end,
- Ts = Ts0#{ Dst => Type },
- Ds = Ds0#{ Dst => I },
- {Ts, Ds, I}.
+opt_local_call_return(I, Callee, Fdb) ->
+ case Fdb of
+ #{ Callee := #func_info{ret_type=[Type]} } when Type =/= any ->
+ beam_ssa:add_anno(result_type, Type, I);
+ #{} ->
+ I
+ end.
%% While we have no way to know which arguments a fun will be called with, we
%% do know its free variables and can update their types as if this were a
@@ -597,17 +481,12 @@ simplify(#b_set{op={bif,Op},args=Args}=I, Ts) ->
eval_bif(beam_ssa:add_anno(float_op, AnnoArgs, I), Ts)
end;
simplify(#b_set{op=get_tuple_element,args=[Tuple,#b_literal{val=N}]}=I, Ts) ->
- case normalized_type(Tuple, Ts) of
- #t_tuple{size=Size,elements=Es} when Size > N ->
- ElemType = beam_types:get_element_type(N + 1, Es),
- case beam_types:get_singleton_value(ElemType) of
- {ok, Val} -> #b_literal{val=Val};
- error -> I
- end;
- none ->
- %% Will never be executed because of type conflict.
- %% #b_literal{val=ignored};
- I
+ #t_tuple{size=Size,elements=Es} = normalized_type(Tuple, Ts),
+ true = Size > N, %Assertion.
+ ElemType = beam_types:get_element_type(N + 1, Es),
+ case beam_types:get_singleton_value(ElemType) of
+ {ok, Val} -> #b_literal{val=Val};
+ error -> I
end;
simplify(#b_set{op=is_nonempty_list,args=[Src]}=I, Ts) ->
case normalized_type(Src, Ts) of
@@ -631,8 +510,59 @@ simplify(#b_set{op=wait_timeout,args=[#b_literal{val=0}]}, _Ts) ->
#b_literal{val=true};
simplify(#b_set{op=wait_timeout,args=[#b_literal{val=infinity}]}=I, _Ts) ->
I#b_set{op=wait,args=[]};
+simplify(#b_set{op=call,args=[#b_remote{}=Rem|Args]}=I, _Ts) ->
+ case Rem of
+ #b_remote{mod=#b_literal{val=Mod},
+ name=#b_literal{val=Name}} ->
+ case erl_bifs:is_pure(Mod, Name, length(Args)) of
+ true ->
+ simplify_remote_call(Mod, Name, Args, I);
+ false ->
+ I
+ end;
+ #b_remote{} ->
+ I
+ end;
simplify(I, _Ts) -> I.
+%% Simplify a remote call to a pure BIF.
+simplify_remote_call(erlang, '++', [#b_literal{val=[]},Tl], _I) ->
+ Tl;
+simplify_remote_call(erlang, setelement,
+ [#b_literal{val=Pos},
+ #b_literal{val=Tuple},
+ #b_var{}=Value], I)
+ when is_integer(Pos), 1 =< Pos, Pos =< tuple_size(Tuple) ->
+ %% Position is a literal integer and the shape of the
+ %% tuple is known.
+ Els0 = [#b_literal{val=El} || El <- tuple_to_list(Tuple)],
+ {Bef,[_|Aft]} = split(Pos - 1, Els0),
+ Els = Bef ++ [Value|Aft],
+ I#b_set{op=put_tuple,args=Els};
+simplify_remote_call(Mod, Name, Args0, I) ->
+ case make_literal_list(Args0) of
+ none ->
+ I;
+ Args ->
+ %% The arguments are literals. Try to evaluate the BIF.
+ try apply(Mod, Name, Args) of
+ Val ->
+ case cerl:is_literal_term(Val) of
+ true ->
+ #b_literal{val=Val};
+ false ->
+ %% The value can't be expressed as a literal
+ %% (e.g. a pid).
+ I
+ end
+ catch
+ _:_ ->
+ %% Failed. Don't bother trying to optimize
+ %% the call.
+ I
+ end
+ end.
+
any_non_numeric_argument([#b_literal{val=Lit}|_], _Ts) ->
is_non_numeric(Lit);
any_non_numeric_argument([#b_var{}=V|T], Ts) ->
@@ -908,82 +838,78 @@ update_successor(S, Ts0, #d{ls=Ls}=D) ->
D#d{ls=Ls#{S=>Ts0}}
end.
-update_types(#b_set{op=Op,dst=Dst,args=Args}, Ts, Ds) ->
- T = type(Op, Args, Ts, Ds),
+update_types(#b_set{op=Op,dst=Dst,anno=Anno,args=Args}, Ts, Ds) ->
+ T = type(Op, Args, Anno, Ts, Ds),
Ts#{Dst=>T}.
-type(phi, Args, Ts, _Ds) ->
+type(phi, Args, _Anno, Ts, _Ds) ->
Types = [raw_type(A, Ts) || {A,_} <- Args],
beam_types:join(Types);
-type({bif,Bif}, Args, Ts, _Ds) ->
+type({bif,Bif}, Args, _Anno, Ts, _Ds) ->
ArgTypes = normalized_types(Args, Ts),
{RetType, _, _} = beam_call_types:types(erlang, Bif, ArgTypes),
RetType;
-type(bs_init, _Args, _Ts, _Ds) ->
+type(bs_init, _Args, _Anno, _Ts, _Ds) ->
#t_bitstring{};
-type(bs_extract, [Ctx], _Ts, Ds) ->
+type(bs_extract, [Ctx], _Anno, _Ts, Ds) ->
#b_set{op=bs_match,args=Args} = map_get(Ctx, Ds),
bs_match_type(Args);
-type(bs_match, _Args, _Ts, _Ds) ->
+type(bs_match, _Args, _Anno, _Ts, _Ds) ->
#t_bs_context{};
-type(bs_get_tail, _Args, _Ts, _Ds) ->
+type(bs_get_tail, _Args, _Anno, _Ts, _Ds) ->
#t_bitstring{};
+type(call, [#b_local{} | _Args], Anno, _Ts, _Ds) ->
+ case Anno of
+ #{ result_type := Type } -> Type;
+ #{} -> any
+ end;
type(call, [#b_remote{mod=#b_literal{val=Mod},
- name=#b_literal{val=Name}}|Args], Ts, _Ds) ->
+ name=#b_literal{val=Name}}|Args], _Anno, Ts, _Ds) ->
ArgTypes = normalized_types(Args, Ts),
{RetType, _, _} = beam_call_types:types(Mod, Name, ArgTypes),
RetType;
-type(get_tuple_element, [Tuple, Offset], Ts, _Ds) ->
+type(get_tuple_element, [Tuple, Offset], _Anno, Ts, _Ds) ->
#t_tuple{size=Size,elements=Es} = normalized_type(Tuple, Ts),
#b_literal{val=N} = Offset,
true = Size > N, %Assertion.
beam_types:get_element_type(N + 1, Es);
-type(is_nonempty_list, [_], _Ts, _Ds) ->
+type(is_nonempty_list, [_], _Anno, _Ts, _Ds) ->
beam_types:make_boolean();
-type(is_tagged_tuple, [_,#b_literal{},#b_literal{}], _Ts, _Ds) ->
+type(is_tagged_tuple, [_,#b_literal{},#b_literal{}], _Anno, _Ts, _Ds) ->
beam_types:make_boolean();
-type(make_fun, [#b_local{arity=TotalArity}|Env], _Ts, _Ds) ->
+type(make_fun, [#b_local{arity=TotalArity}|Env], _Anno, _Ts, _Ds) ->
#t_fun{arity=TotalArity-length(Env)};
-type(put_map, _Args, _Ts, _Ds) ->
+type(put_map, _Args, _Anno, _Ts, _Ds) ->
#t_map{};
-type(put_list, _Args, _Ts, _Ds) ->
+type(put_list, _Args, _Anno, _Ts, _Ds) ->
cons;
-type(put_tuple, Args, Ts, _Ds) ->
+type(put_tuple, Args, _Anno, Ts, _Ds) ->
{Es, _} = foldl(fun(Arg, {Es0, Index}) ->
Type = raw_type(Arg, Ts),
Es = beam_types:set_element_type(Index, Type, Es0),
{Es, Index + 1}
end, {#{}, 1}, Args),
#t_tuple{exact=true,size=length(Args),elements=Es};
-type(succeeded, [#b_var{}=Src], Ts, Ds) ->
+type(succeeded, [#b_var{}=Src], _Anno, Ts, _Ds)
+ when map_get(Src, Ts) =:= none ->
+ beam_types:make_atom(false);
+type(succeeded, [#b_var{}=Src], _Anno, Ts, Ds) ->
case maps:get(Src, Ds) of
#b_set{op={bif,Bif},args=BifArgs} ->
- Types = normalized_types(BifArgs, Ts),
- case {Bif,Types} of
- {BoolOp,[T1,T2]} when BoolOp =:= 'and'; BoolOp =:= 'or' ->
- BothBool = beam_types:is_boolean_type(T1) andalso
- beam_types:is_boolean_type(T2),
- case BothBool of
- true -> beam_types:make_atom(true);
- false -> beam_types:make_boolean()
- end;
- {byte_size,[#t_bitstring{}]} ->
- beam_types:make_atom(true);
- {bit_size,[#t_bitstring{}]} ->
- beam_types:make_atom(true);
- {map_size,[#t_map{}]} ->
- beam_types:make_atom(true);
- {'not',[Type]} ->
- case beam_types:is_boolean_type(Type) of
- true -> beam_types:make_atom(true);
- false -> beam_types:make_boolean()
- end;
- {size,[#t_bitstring{}]} ->
- beam_types:make_atom(true);
- {tuple_size,[#t_tuple{}]} ->
- beam_types:make_atom(true);
- {_,_} ->
- beam_types:make_boolean()
+ ArgTypes = normalized_types(BifArgs, Ts),
+ case beam_call_types:will_succeed(erlang, Bif, ArgTypes) of
+ yes -> beam_types:make_atom(true);
+ no -> beam_types:make_atom(false);
+ maybe -> beam_types:make_boolean()
+ end;
+ #b_set{op=call,args=[#b_remote{mod=#b_literal{val=Mod},
+ name=#b_literal{val=Func}} |
+ CallArgs]} ->
+ ArgTypes = normalized_types(CallArgs, Ts),
+ case beam_call_types:will_succeed(Mod, Func, ArgTypes) of
+ yes -> beam_types:make_atom(true);
+ no -> beam_types:make_atom(false);
+ maybe -> beam_types:make_boolean()
end;
#b_set{op=get_hd} ->
beam_types:make_atom(true);
@@ -991,14 +917,16 @@ type(succeeded, [#b_var{}=Src], Ts, Ds) ->
beam_types:make_atom(true);
#b_set{op=get_tuple_element} ->
beam_types:make_atom(true);
+ #b_set{op=put_tuple} ->
+ beam_types:make_atom(true);
#b_set{op=wait} ->
beam_types:make_atom(false);
#b_set{} ->
beam_types:make_boolean()
end;
-type(succeeded, [#b_literal{}], _Ts, _Ds) ->
+type(succeeded, [#b_literal{}], _Anno, _Ts, _Ds) ->
beam_types:make_atom(true);
-type(_, _, _, _) -> any.
+type(_, _, _, _, _) -> any.
%% will_succeed(TestOperation, Type) -> yes|no|maybe.
%% Test whether TestOperation applied to an argument of type Type
@@ -1423,6 +1351,9 @@ infer_success_type({bif,Op}, Args, Ts, _Ds) ->
true -> {PosTypes, PosTypes};
false -> {PosTypes, []}
end;
+infer_success_type(call, [#b_var{}=Fun|Args], _Ts, _Ds) ->
+ T = {Fun, #t_fun{arity=length(Args)}},
+ {[T], []};
infer_success_type(bs_start_match, [#b_var{}=Bin], _Ts, _Ds) ->
T = {Bin,#t_bitstring{}},
{[T], [T]};
diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl
index afede2b54d..d55aeed9a9 100644
--- a/lib/compiler/src/beam_validator.erl
+++ b/lib/compiler/src/beam_validator.erl
@@ -151,8 +151,8 @@ validate_0(Module, [{function,Name,Ar,Entry,Code}|Fs], Ft) ->
%% instructions.
-type tag() :: initialized |
uninitialized |
- {catchtag, [label()]} |
- {trytag, [label()]}.
+ {catchtag, ordsets:ordset(label())} |
+ {trytag, ordsets:ordset(label())}.
-type x_regs() :: #{ {x, index()} => #value_ref{} }.
-type y_regs() :: #{ {y, index()} => tag() | #value_ref{} }.
@@ -180,7 +180,7 @@ validate_0(Module, [{function,Name,Ar,Entry,Code}|Fs], Ft) ->
hf=0,
%% Floating point state.
fls=undefined,
- %% List of hot catch/try labels
+ %% List of hot catch/try tags
ct=[],
%% Previous instruction was setelement/3.
setelem=false,
@@ -395,16 +395,33 @@ valfun_1({init,Reg}, Vst) ->
create_tag(initialized, init, [], Reg, Vst);
valfun_1({test_heap,Heap,Live}, Vst) ->
test_heap(Heap, Live, Vst);
-valfun_1({bif,Op,{f,_},Ss,Dst}=I, Vst) ->
- case erl_bifs:is_safe(erlang, Op, length(Ss)) of
- true ->
- %% It can't fail, so we finish handling it here (not updating
- %% catch state).
- {RetType, _, _} = bif_types(Op, Ss, Vst),
- extract_term(RetType, {bif,Op}, Ss, Dst, Vst);
- false ->
- %% Since the BIF can fail, make sure that any catch state
- %% is updated.
+valfun_1({bif,Op,{f,0},Ss,Dst}=I, Vst0) ->
+ case will_bif_succeed(Op, Ss, Vst0) of
+ yes ->
+ %% This BIF cannot fail, handle it here without updating catch
+ %% state.
+ validate_bif(Op, cannot_fail, Ss, Dst, Vst0);
+ no ->
+ %% The stack will be scanned, so Y registers must be initialized.
+ Vst = branch_exception(Vst0),
+ verify_y_init(Vst),
+ kill_state(Vst);
+ maybe ->
+ %% The BIF can fail, make sure that any catch state is updated.
+ Vst = branch_exception(Vst0),
+ valfun_2(I, Vst)
+ end;
+valfun_1({gc_bif,Op,{f,0},Live,Ss,Dst}=I, Vst0) ->
+ case will_bif_succeed(Op, Ss, Vst0) of
+ yes ->
+ validate_gc_bif(Op, cannot_fail, Ss, Dst, Live, Vst0);
+ no ->
+ Vst = branch_exception(Vst0),
+ verify_y_init(Vst),
+ kill_state(Vst);
+ maybe ->
+ Vst = branch_exception(Vst0),
+ assert_float_checked(Vst),
valfun_2(I, Vst)
end;
%% Put instructions.
@@ -451,6 +468,19 @@ valfun_1({put,Src}, Vst0) ->
St = St0#st{puts_left={PutsLeft-1,{Dst,Sz,Es}}},
Vst#vst{current=St}
end;
+%% This instruction never fails, though it may be invalid in some contexts; see
+%% val_dsetel/2
+valfun_1({set_tuple_element,Src,Tuple,N}, Vst) ->
+ I = N + 1,
+ assert_term(Src, Vst),
+ assert_type(#t_tuple{size=I}, Tuple, Vst),
+ %% Manually update the tuple type; we can't rely on the ordinary update
+ %% helpers as we must support overwriting (rather than just widening or
+ %% narrowing) known elements, and we can't use extract_term either since
+ %% the source tuple may be aliased.
+ #t_tuple{elements=Es0}=Type = normalize(get_term_type(Tuple, Vst)),
+ Es = beam_types:set_element_type(I, get_term_type(Src, Vst), Es0),
+ override_type(Type#t_tuple{elements=Es}, Tuple, Vst);
%% Instructions for optimization of selective receives.
valfun_1({recv_mark,{f,Fail}}, Vst) when is_integer(Fail) ->
Vst;
@@ -461,6 +491,9 @@ valfun_1(remove_message, Vst) ->
%% The message term is no longer fragile. It can be used
%% without restrictions.
remove_fragility(Vst);
+valfun_1({'%', {type_info, _Reg, none}}, Vst) ->
+ %% Unreachable code, typically after a call that never returns.
+ kill_state(Vst);
valfun_1({'%', {type_info, Reg, #t_bs_context{}=Type}}, Vst) ->
%% This is a gross hack, but we'll be rid of it once we have proper union
%% types.
@@ -482,22 +515,42 @@ valfun_1({'%',_}, Vst) ->
Vst;
valfun_1({line,_}, Vst) ->
Vst;
-%% Exception generating calls
-valfun_1({call_ext,Live,Func}=I, Vst) ->
- case call_types(Func, Live, Vst) of
- {none, _, _} ->
- verify_live(Live, Vst),
- %% The stack will be scanned, so Y registers
- %% must be initialized.
- verify_y_init(Vst),
- kill_state(Vst);
- _ ->
- valfun_2(I, Vst)
- end;
+%%
+%% Calls; these may be okay when the try/catch state or stack is undecided,
+%% depending on whether they always succeed or always fail.
+%%
+valfun_1({apply,Live}, Vst) ->
+ validate_body_call(apply, Live+2, Vst);
+valfun_1({apply_last,Live,N}, Vst) ->
+ validate_tail_call(N, apply, Live+2, Vst);
+valfun_1({call_fun,Live}, Vst) ->
+ Fun = {x,Live},
+ assert_term(Fun, Vst),
+
+ %% An exception is raised on error, hence branching to 0.
+ branch(0, Vst,
+ fun(SuccVst0) ->
+ SuccVst = update_type(fun meet/2, #t_fun{arity=Live},
+ Fun, SuccVst0),
+ validate_body_call('fun', Live+1, SuccVst)
+ end);
+valfun_1({call,Live,Func}, Vst) ->
+ validate_body_call(Func, Live, Vst);
+valfun_1({call_ext,Live,Func}, Vst) ->
+ validate_body_call(Func, Live, Vst);
+valfun_1({call_only,Live,Func}, Vst) ->
+ validate_tail_call(none, Func, Live, Vst);
+valfun_1({call_ext_only,Live,Func}, Vst) ->
+ validate_tail_call(none, Func, Live, Vst);
+valfun_1({call_last,Live,Func,N}, Vst) ->
+ validate_tail_call(N, Func, Live, Vst);
+valfun_1({call_ext_last,Live,Func,N}, Vst) ->
+ validate_tail_call(N, Func, Live, Vst);
valfun_1(_I, #vst{current=#st{ct=undecided}}) ->
error(unknown_catch_try_state);
%%
%% Allocate and deallocate, et.al
+%%
valfun_1({allocate,Stk,Live}, Vst) ->
allocate(uninitialized, Stk, 0, Live, Vst);
valfun_1({allocate_heap,Stk,Heap,Live}, Vst) ->
@@ -524,25 +577,25 @@ valfun_1({'catch',Dst,{f,Fail}}, Vst) when Fail =/= none ->
init_try_catch_branch(catchtag, Dst, Fail, Vst);
valfun_1({'try',Dst,{f,Fail}}, Vst) when Fail =/= none ->
init_try_catch_branch(trytag, Dst, Fail, Vst);
-valfun_1({catch_end,Reg}, #vst{current=#st{ct=[Fail|_]}}=Vst0) ->
+valfun_1({catch_end,Reg}, #vst{current=#st{ct=[Tag|_]}}=Vst0) ->
case get_tag_type(Reg, Vst0) of
- {catchtag,Fail} ->
+ {catchtag,_Fail}=Tag ->
%% {x,0} contains the caught term, if any.
create_term(any, catch_end, [], {x,0}, kill_catch_tag(Reg, Vst0));
Type ->
error({wrong_tag_type,Type})
end;
-valfun_1({try_end,Reg}, #vst{current=#st{ct=[Fail|_]}}=Vst) ->
+valfun_1({try_end,Reg}, #vst{current=#st{ct=[Tag|_]}}=Vst) ->
case get_tag_type(Reg, Vst) of
- {trytag,Fail} ->
+ {trytag,_Fail}=Tag ->
%% Kill the catch tag, note that x registers are unaffected.
kill_catch_tag(Reg, Vst);
Type ->
error({wrong_tag_type,Type})
end;
-valfun_1({try_case,Reg}, #vst{current=#st{ct=[Fail|_]}}=Vst0) ->
+valfun_1({try_case,Reg}, #vst{current=#st{ct=[Tag|_]}}=Vst0) ->
case get_tag_type(Reg, Vst0) of
- {trytag,Fail} ->
+ {trytag,_Fail}=Tag ->
%% Kill the catch tag and all x registers.
Vst1 = prune_x_regs(0, kill_catch_tag(Reg, Vst0)),
@@ -553,6 +606,7 @@ valfun_1({try_case,Reg}, #vst{current=#st{ct=[Fail|_]}}=Vst0) ->
Type ->
error({wrong_tag_type,Type})
end;
+%% Simple getters that can't fail.
valfun_1({get_list,Src,D1,D2}, Vst0) ->
assert_not_literal(Src),
assert_type(cons, Src, Vst0),
@@ -579,25 +633,81 @@ valfun_1({jump,{f,Lbl}}, Vst) ->
%% The next instruction is never executed.
kill_state(SuccVst)
end);
-valfun_1(I, Vst) ->
+valfun_1(I, Vst0) ->
+ Vst = branch_exception(Vst0),
valfun_2(I, Vst).
-init_try_catch_branch(Tag, Dst, Fail, Vst0) ->
- Vst1 = create_tag({Tag,[Fail]}, 'try_catch', [], Dst, Vst0),
- #vst{current=#st{ct=Fails}=St0} = Vst1,
- St = St0#st{ct=[[Fail]|Fails]},
- Vst = Vst0#vst{current=St},
+validate_tail_call(Deallocate, Func, Live, #vst{current=#st{numy=NumY}}=Vst0) ->
+ assert_float_checked(Vst0),
+ case will_call_succeed(Func, Vst0) of
+ yes when Deallocate =:= NumY ->
+ %% This call cannot fail, handle it without updating catch state.
+ tail_call(Func, Live, Vst0);
+ maybe when Deallocate =:= NumY ->
+ %% The call can fail, make sure that any catch state is updated.
+ Vst = branch_exception(Vst0),
+ tail_call(Func, Live, Vst);
+ no ->
+ %% The stack will be scanned, so Y registers must be initialized.
+ %%
+ %% Note that the compiler is allowed to emit garbage values for
+ %% "Deallocate" as we know that it will not be used in this case.
+ Vst = branch_exception(Vst0),
+ verify_live(Live, Vst),
+ verify_y_init(Vst),
+ kill_state(Vst);
+ _ when Deallocate =/= NumY ->
+ error({allocated, NumY})
+ end.
+
+validate_body_call(Func, Live,
+ #vst{current=#st{numy=NumY}}=Vst0) when is_integer(NumY)->
+ assert_float_checked(Vst0),
+ case will_call_succeed(Func, Vst0) of
+ yes ->
+ call(Func, Live, Vst0);
+ maybe ->
+ Vst = branch_exception(Vst0),
+ call(Func, Live, Vst);
+ no ->
+ Vst = branch_exception(Vst0),
+ verify_live(Live, Vst),
+ verify_y_init(Vst),
+ kill_state(Vst)
+ end;
+validate_body_call(_, _, #vst{current=#st{numy=NumY}}) ->
+ error({allocated, NumY}).
+
+assert_float_checked(Vst) ->
+ case get_fls(Vst) of
+ undefined -> ok;
+ checked -> ok;
+ Fls -> error({unsafe_instruction,{float_error_state,Fls}})
+ end.
+
+init_try_catch_branch(Kind, Dst, Fail, Vst0) ->
+ Tag = {Kind, [Fail]},
+ Vst = create_tag(Tag, 'try_catch', [], Dst, Vst0),
branch(Fail, Vst,
- fun(CatchVst) ->
- #vst{current=#st{ys=Ys}} = CatchVst,
- maps:fold(fun init_catch_handler_1/3, CatchVst, Ys)
+ fun(CatchVst0) ->
+ %% We add the tag here because branch/4 rejects jumps to
+ %% labels referenced by try tags.
+ #vst{current=#st{ct=Tags,ys=Ys}=St0} = CatchVst0,
+ St = St0#st{ct=[Tag|Tags]},
+ CatchVst = CatchVst0#vst{current=St},
+
+ maps:fold(fun init_catch_handler_1/3, CatchVst, Ys)
end,
- fun(SuccVst) ->
- %% All potentially-throwing instructions after this
- %% one will implicitly branch to the fail label;
- %% see valfun_2/2
- SuccVst
+ fun(SuccVst0) ->
+ #vst{current=#st{ct=Tags}=St0} = SuccVst0,
+ St = St0#st{ct=[Tag|Tags]},
+ SuccVst = SuccVst0#vst{current=St},
+
+ %% All potentially-throwing instructions after this one will
+ %% implicitly branch to the current try/catch handler; see
+ %% the base case of valfun_1/2
+ SuccVst
end).
%% Set the initial state at the try/catch label. Assume that Y registers
@@ -609,19 +719,20 @@ init_catch_handler_1(Reg, uninitialized, Vst) ->
init_catch_handler_1(_, _, Vst) ->
Vst.
-valfun_2(I, #vst{current=#st{ct=[[Fail]|_]}}=Vst) when is_integer(Fail) ->
+branch_exception(#vst{current=#st{ct=[{_,[Fail]}|_]}}=Vst)
+ when is_integer(Fail) ->
%% We have an active try/catch tag and we can jump there from this
%% instruction, so we need to update the branched state of the try/catch
%% handler.
- valfun_3(I, branch_state(Fail, Vst));
-valfun_2(I, #vst{current=#st{ct=[]}}=Vst) ->
- valfun_3(I, Vst);
-valfun_2(_, _) ->
+ fork_state(Fail, Vst);
+branch_exception(#vst{current=#st{ct=[]}}=Vst) ->
+ Vst;
+branch_exception(_) ->
error(ambiguous_catch_try_state).
%% Handle the remaining floating point instructions here.
%% Floating point.
-valfun_3({fconv,Src,{fr,_}=Dst}, Vst) ->
+valfun_2({fconv,Src,{fr,_}=Dst}, Vst) ->
assert_term(Src, Vst),
%% An exception is raised on error, hence branching to 0.
@@ -630,72 +741,32 @@ valfun_3({fconv,Src,{fr,_}=Dst}, Vst) ->
SuccVst = update_type(fun meet/2, number, Src, SuccVst0),
set_freg(Dst, SuccVst)
end);
-valfun_3({bif,fadd,_,[_,_]=Ss,Dst}, Vst) ->
+valfun_2({bif,fadd,_,[_,_]=Ss,Dst}, Vst) ->
float_op(Ss, Dst, Vst);
-valfun_3({bif,fdiv,_,[_,_]=Ss,Dst}, Vst) ->
+valfun_2({bif,fdiv,_,[_,_]=Ss,Dst}, Vst) ->
float_op(Ss, Dst, Vst);
-valfun_3({bif,fmul,_,[_,_]=Ss,Dst}, Vst) ->
+valfun_2({bif,fmul,_,[_,_]=Ss,Dst}, Vst) ->
float_op(Ss, Dst, Vst);
-valfun_3({bif,fnegate,_,[_]=Ss,Dst}, Vst) ->
+valfun_2({bif,fnegate,_,[_]=Ss,Dst}, Vst) ->
float_op(Ss, Dst, Vst);
-valfun_3({bif,fsub,_,[_,_]=Ss,Dst}, Vst) ->
+valfun_2({bif,fsub,_,[_,_]=Ss,Dst}, Vst) ->
float_op(Ss, Dst, Vst);
-valfun_3(fclearerror, Vst) ->
+valfun_2(fclearerror, Vst) ->
case get_fls(Vst) of
- undefined -> ok;
- checked -> ok;
- Fls -> error({bad_floating_point_state,Fls})
+ undefined -> ok;
+ checked -> ok;
+ Fls -> error({bad_floating_point_state,Fls})
end,
set_fls(cleared, Vst);
-valfun_3({fcheckerror,_}, Vst) ->
+valfun_2({fcheckerror,_}, Vst) ->
assert_fls(cleared, Vst),
set_fls(checked, Vst);
-valfun_3(I, Vst) ->
- %% The instruction is not a float instruction.
- case get_fls(Vst) of
- undefined ->
- valfun_4(I, Vst);
- checked ->
- valfun_4(I, Vst);
- Fls ->
- error({unsafe_instruction,{float_error_state,Fls}})
- end.
+valfun_2(I, Vst) ->
+ assert_float_checked(Vst),
+ valfun_3(I, Vst).
%% Instructions that can cause exceptions.
-valfun_4({apply,Live}, Vst) ->
- call(apply, Live+2, Vst);
-valfun_4({apply_last,Live,_}, Vst) ->
- tail_call(apply, Live+2, Vst);
-valfun_4({call_fun,Live}, Vst) ->
- Fun = {x,Live},
- assert_term(Fun, Vst),
-
- %% An exception is raised on error, hence branching to 0.
- branch(0, Vst,
- fun(SuccVst0) ->
- SuccVst = update_type(fun meet/2, #t_fun{arity=Live},
- Fun, SuccVst0),
- call('fun', Live+1, SuccVst)
- end);
-valfun_4({call,Live,Func}, Vst) ->
- call(Func, Live, Vst);
-valfun_4({call_ext,Live,Func}, Vst) ->
- %% Exception BIFs has already been taken care of above.
- call(Func, Live, Vst);
-valfun_4({call_only,Live,Func}, Vst) ->
- tail_call(Func, Live, Vst);
-valfun_4({call_ext_only,Live,Func}, Vst) ->
- tail_call(Func, Live, Vst);
-valfun_4({call_last,Live,Func,StkSize}, #vst{current=#st{numy=StkSize}}=Vst) ->
- tail_call(Func, Live, Vst);
-valfun_4({call_last,_,_,_}, #vst{current=#st{numy=NumY}}) ->
- error({allocated,NumY});
-valfun_4({call_ext_last,Live,Func,StkSize},
- #vst{current=#st{numy=StkSize}}=Vst) ->
- tail_call(Func, Live, Vst);
-valfun_4({call_ext_last,_,_,_}, #vst{current=#st{numy=NumY}}) ->
- error({allocated,NumY});
-valfun_4({make_fun2,{f,Lbl},_,_,NumFree}, #vst{ft=Ft}=Vst0) ->
+valfun_3({make_fun2,{f,Lbl},_,_,NumFree}, #vst{ft=Ft}=Vst0) ->
#{ arity := Arity0 } = gb_trees:get(Lbl, Ft),
Arity = Arity0 - NumFree,
@@ -707,31 +778,22 @@ valfun_4({make_fun2,{f,Lbl},_,_,NumFree}, #vst{ft=Ft}=Vst0) ->
create_term(#t_fun{arity=Arity}, make_fun, [], {x,0}, Vst);
%% Other BIFs
-valfun_4({bif,raise,{f,0},Src,_Dst}, Vst) ->
+valfun_3({bif,raise,{f,0},Src,_Dst}, Vst) ->
validate_src(Src, Vst),
kill_state(Vst);
-valfun_4(raw_raise=I, Vst) ->
+valfun_3(raw_raise=I, Vst) ->
call(I, 3, Vst);
-valfun_4({bif,Op,{f,Fail},Ss,Dst}, Vst) ->
+valfun_3({bif,Op,{f,Fail},Ss,Dst}, Vst) ->
validate_src(Ss, Vst),
- validate_bif(bif, Op, Fail, Ss, Dst, Vst, Vst);
-valfun_4({gc_bif,Op,{f,Fail},Live,Ss,Dst}, #vst{current=St0}=Vst0) ->
- validate_src(Ss, Vst0),
- verify_live(Live, Vst0),
- verify_y_init(Vst0),
-
- %% Heap allocations and X registers are killed regardless of whether we
- %% fail or not, as we may fail after GC.
- St = kill_heap_allocation(St0),
- Vst = prune_x_regs(Live, Vst0#vst{current=St}),
-
- validate_bif(gc_bif, Op, Fail, Ss, Dst, Vst0, Vst);
-valfun_4(return, #vst{current=#st{numy=none}}=Vst) ->
+ validate_bif(Op, Fail, Ss, Dst, Vst);
+valfun_3({gc_bif,Op,{f,Fail},Live,Ss,Dst}, Vst) ->
+ validate_gc_bif(Op, Fail, Ss, Dst, Live, Vst);
+valfun_3(return, #vst{current=#st{numy=none}}=Vst) ->
assert_durable_term({x,0}, Vst),
kill_state(Vst);
-valfun_4(return, #vst{current=#st{numy=NumY}}) ->
+valfun_3(return, #vst{current=#st{numy=NumY}}) ->
error({stack_frame,NumY});
-valfun_4({loop_rec,{f,Fail},Dst}, Vst) ->
+valfun_3({loop_rec,{f,Fail},Dst}, Vst) ->
%% This term may not be part of the root set until remove_message/0 is
%% executed. If control transfers to the loop_rec_end/1 instruction, no
%% part of this term must be stored in a Y register.
@@ -740,66 +802,55 @@ valfun_4({loop_rec,{f,Fail},Dst}, Vst) ->
{Ref, SuccVst} = new_value(any, loop_rec, [], SuccVst0),
mark_fragile(Dst, set_reg_vref(Ref, Dst, SuccVst))
end);
-valfun_4({wait,_}, Vst) ->
+valfun_3({wait,_}, Vst) ->
verify_y_init(Vst),
kill_state(Vst);
-valfun_4({wait_timeout,_,Src}, Vst) ->
+valfun_3({wait_timeout,_,Src}, Vst) ->
assert_term(Src, Vst),
verify_y_init(Vst),
prune_x_regs(0, Vst);
-valfun_4({loop_rec_end,_}, Vst) ->
+valfun_3({loop_rec_end,_}, Vst) ->
verify_y_init(Vst),
kill_state(Vst);
-valfun_4(timeout, Vst) ->
+valfun_3(timeout, Vst) ->
prune_x_regs(0, Vst);
-valfun_4(send, Vst) ->
+valfun_3(send, Vst) ->
call(send, 2, Vst);
-valfun_4({set_tuple_element,Src,Tuple,N}, Vst) ->
- I = N + 1,
- assert_term(Src, Vst),
- assert_type(#t_tuple{size=I}, Tuple, Vst),
- %% Manually update the tuple type; we can't rely on the ordinary update
- %% helpers as we must support overwriting (rather than just widening or
- %% narrowing) known elements, and we can't use extract_term either since
- %% the source tuple may be aliased.
- #t_tuple{elements=Es0}=Type = normalize(get_term_type(Tuple, Vst)),
- Es = beam_types:set_element_type(I, get_term_type(Src, Vst), Es0),
- override_type(Type#t_tuple{elements=Es}, Tuple, Vst);
%% Match instructions.
-valfun_4({select_val,Src,{f,Fail},{list,Choices}}, Vst) ->
+valfun_3({select_val,Src,{f,Fail},{list,Choices}}, Vst) ->
assert_term(Src, Vst),
assert_choices(Choices),
validate_select_val(Fail, Choices, Src, Vst);
-valfun_4({select_tuple_arity,Tuple,{f,Fail},{list,Choices}}, Vst) ->
+valfun_3({select_tuple_arity,Tuple,{f,Fail},{list,Choices}}, Vst) ->
assert_type(#t_tuple{}, Tuple, Vst),
assert_arities(Choices),
validate_select_tuple_arity(Fail, Choices, Tuple, Vst);
%% New bit syntax matching instructions.
-valfun_4({test,bs_start_match3,{f,Fail},Live,[Src],Dst}, Vst) ->
+valfun_3({test,bs_start_match3,{f,Fail},Live,[Src],Dst}, Vst) ->
validate_bs_start_match(Fail, Live, bsm_match_state(), Src, Dst, Vst);
-valfun_4({test,bs_start_match2,{f,Fail},Live,[Src,Slots],Dst}, Vst) ->
+valfun_3({test,bs_start_match2,{f,Fail},Live,[Src,Slots],Dst}, Vst) ->
validate_bs_start_match(Fail, Live, bsm_match_state(Slots), Src, Dst, Vst);
-valfun_4({test,bs_match_string,{f,Fail},[Ctx,_,_]}, Vst) ->
+valfun_3({test,bs_match_string,{f,Fail},[Ctx,_,_]}, Vst) ->
assert_type(#t_bs_context{}, Ctx, Vst),
branch(Fail, Vst, fun(V) -> V end);
-valfun_4({test,bs_skip_bits2,{f,Fail},[Ctx,Src,_,_]}, Vst) ->
+valfun_3({test,bs_skip_bits2,{f,Fail},[Ctx,Src,_,_]}, Vst) ->
assert_type(#t_bs_context{}, Ctx, Vst),
assert_term(Src, Vst),
branch(Fail, Vst, fun(V) -> V end);
-valfun_4({test,bs_test_tail2,{f,Fail},[Ctx,_]}, Vst) ->
+valfun_3({test,bs_test_tail2,{f,Fail},[Ctx,_]}, Vst) ->
assert_type(#t_bs_context{}, Ctx, Vst),
branch(Fail, Vst, fun(V) -> V end);
-valfun_4({test,bs_test_unit,{f,Fail},[Ctx,_]}, Vst) ->
+valfun_3({test,bs_test_unit,{f,Fail},[Ctx,_]}, Vst) ->
assert_type(#t_bs_context{}, Ctx, Vst),
branch(Fail, Vst, fun(V) -> V end);
-valfun_4({test,bs_skip_utf8,{f,Fail},[Ctx,Live,_]}, Vst) ->
+valfun_3({test,bs_skip_utf8,{f,Fail},[Ctx,Live,_]}, Vst) ->
validate_bs_skip_utf(Fail, Ctx, Live, Vst);
-valfun_4({test,bs_skip_utf16,{f,Fail},[Ctx,Live,_]}, Vst) ->
+valfun_3({test,bs_skip_utf16,{f,Fail},[Ctx,Live,_]}, Vst) ->
validate_bs_skip_utf(Fail, Ctx, Live, Vst);
-valfun_4({test,bs_skip_utf32,{f,Fail},[Ctx,Live,_]}, Vst) ->
+valfun_3({test,bs_skip_utf32,{f,Fail},[Ctx,Live,_]}, Vst) ->
validate_bs_skip_utf(Fail, Ctx, Live, Vst);
-valfun_4({test,bs_get_integer2=Op,{f,Fail},Live,
+valfun_3({test,bs_get_integer2=Op,{f,Fail},Live,
[Ctx,{integer,Size},Unit,{field_flags,Flags}],Dst},Vst)
when Size * Unit =< 64 ->
Type = case member(unsigned, Flags) of
@@ -811,66 +862,66 @@ valfun_4({test,bs_get_integer2=Op,{f,Fail},Live,
#t_integer{}
end,
validate_bs_get(Op, Fail, Ctx, Live, Type, Dst, Vst);
-valfun_4({test,bs_get_integer2=Op,{f,Fail},Live,
+valfun_3({test,bs_get_integer2=Op,{f,Fail},Live,
[Ctx,_Size,_Unit,_Flags],Dst},Vst) ->
validate_bs_get(Op, Fail, Ctx, Live, #t_integer{}, Dst, Vst);
-valfun_4({test,bs_get_float2=Op,{f,Fail},Live,[Ctx,_,_,_],Dst}, Vst) ->
+valfun_3({test,bs_get_float2=Op,{f,Fail},Live,[Ctx,_,_,_],Dst}, Vst) ->
validate_bs_get(Op, Fail, Ctx, Live, float, Dst, Vst);
-valfun_4({test,bs_get_binary2=Op,{f,Fail},Live,[Ctx,_,Unit,_],Dst}, Vst) ->
+valfun_3({test,bs_get_binary2=Op,{f,Fail},Live,[Ctx,_,Unit,_],Dst}, Vst) ->
validate_bs_get(Op, Fail, Ctx, Live, #t_bitstring{unit=Unit}, Dst, Vst);
-valfun_4({test,bs_get_utf8=Op,{f,Fail},Live,[Ctx,_],Dst}, Vst) ->
+valfun_3({test,bs_get_utf8=Op,{f,Fail},Live,[Ctx,_],Dst}, Vst) ->
Type = beam_types:make_integer(0, ?UNICODE_MAX),
validate_bs_get(Op, Fail, Ctx, Live, Type, Dst, Vst);
-valfun_4({test,bs_get_utf16=Op,{f,Fail},Live,[Ctx,_],Dst}, Vst) ->
+valfun_3({test,bs_get_utf16=Op,{f,Fail},Live,[Ctx,_],Dst}, Vst) ->
Type = beam_types:make_integer(0, ?UNICODE_MAX),
validate_bs_get(Op, Fail, Ctx, Live, Type, Dst, Vst);
-valfun_4({test,bs_get_utf32=Op,{f,Fail},Live,[Ctx,_],Dst}, Vst) ->
+valfun_3({test,bs_get_utf32=Op,{f,Fail},Live,[Ctx,_],Dst}, Vst) ->
Type = beam_types:make_integer(0, ?UNICODE_MAX),
validate_bs_get(Op, Fail, Ctx, Live, Type, Dst, Vst);
-valfun_4({bs_save2,Ctx,SavePoint}, Vst) ->
+valfun_3({bs_save2,Ctx,SavePoint}, Vst) ->
bsm_save(Ctx, SavePoint, Vst);
-valfun_4({bs_restore2,Ctx,SavePoint}, Vst) ->
+valfun_3({bs_restore2,Ctx,SavePoint}, Vst) ->
bsm_restore(Ctx, SavePoint, Vst);
-valfun_4({bs_get_position, Ctx, Dst, Live}, Vst0) ->
+valfun_3({bs_get_position, Ctx, Dst, Live}, Vst0) ->
assert_type(#t_bs_context{}, Ctx, Vst0),
verify_live(Live, Vst0),
verify_y_init(Vst0),
Vst = prune_x_regs(Live, Vst0),
create_term(#t_abstract{kind=ms_position}, bs_get_position, [Ctx],
Dst, Vst, Vst0);
-valfun_4({bs_set_position, Ctx, Pos}, Vst) ->
+valfun_3({bs_set_position, Ctx, Pos}, Vst) ->
assert_type(#t_bs_context{}, Ctx, Vst),
assert_type(#t_abstract{kind=ms_position}, Pos, Vst),
Vst;
%% Other test instructions.
-valfun_4({test,has_map_fields,{f,Lbl},Src,{list,List}}, Vst) ->
+valfun_3({test,has_map_fields,{f,Lbl},Src,{list,List}}, Vst) ->
assert_type(#t_map{}, Src, Vst),
assert_unique_map_keys(List),
branch(Lbl, Vst, fun(V) -> V end);
-valfun_4({test,is_atom,{f,Lbl},[Src]}, Vst) ->
+valfun_3({test,is_atom,{f,Lbl},[Src]}, Vst) ->
type_test(Lbl, #t_atom{}, Src, Vst);
-valfun_4({test,is_binary,{f,Lbl},[Src]}, Vst) ->
+valfun_3({test,is_binary,{f,Lbl},[Src]}, Vst) ->
type_test(Lbl, #t_bitstring{unit=8}, Src, Vst);
-valfun_4({test,is_bitstr,{f,Lbl},[Src]}, Vst) ->
+valfun_3({test,is_bitstr,{f,Lbl},[Src]}, Vst) ->
type_test(Lbl, #t_bitstring{}, Src, Vst);
-valfun_4({test,is_boolean,{f,Lbl},[Src]}, Vst) ->
+valfun_3({test,is_boolean,{f,Lbl},[Src]}, Vst) ->
type_test(Lbl, beam_types:make_boolean(), Src, Vst);
-valfun_4({test,is_float,{f,Lbl},[Src]}, Vst) ->
+valfun_3({test,is_float,{f,Lbl},[Src]}, Vst) ->
type_test(Lbl, float, Src, Vst);
-valfun_4({test,is_tuple,{f,Lbl},[Src]}, Vst) ->
+valfun_3({test,is_tuple,{f,Lbl},[Src]}, Vst) ->
type_test(Lbl, #t_tuple{}, Src, Vst);
-valfun_4({test,is_integer,{f,Lbl},[Src]}, Vst) ->
+valfun_3({test,is_integer,{f,Lbl},[Src]}, Vst) ->
type_test(Lbl, #t_integer{}, Src, Vst);
-valfun_4({test,is_nonempty_list,{f,Lbl},[Src]}, Vst) ->
+valfun_3({test,is_nonempty_list,{f,Lbl},[Src]}, Vst) ->
type_test(Lbl, cons, Src, Vst);
-valfun_4({test,is_number,{f,Lbl},[Src]}, Vst) ->
+valfun_3({test,is_number,{f,Lbl},[Src]}, Vst) ->
type_test(Lbl, number, Src, Vst);
-valfun_4({test,is_list,{f,Lbl},[Src]}, Vst) ->
+valfun_3({test,is_list,{f,Lbl},[Src]}, Vst) ->
type_test(Lbl, list, Src, Vst);
-valfun_4({test,is_map,{f,Lbl},[Src]}, Vst) ->
+valfun_3({test,is_map,{f,Lbl},[Src]}, Vst) ->
type_test(Lbl, #t_map{}, Src, Vst);
-valfun_4({test,is_nil,{f,Lbl},[Src]}, Vst) ->
+valfun_3({test,is_nil,{f,Lbl},[Src]}, Vst) ->
%% is_nil is an exact check against the 'nil' value, and should not be
%% treated as a simple type test.
assert_term(Src, Vst),
@@ -881,16 +932,16 @@ valfun_4({test,is_nil,{f,Lbl},[Src]}, Vst) ->
fun(SuccVst) ->
update_eq_types(Src, nil, SuccVst)
end);
-valfun_4({test,test_arity,{f,Lbl},[Tuple,Sz]}, Vst) when is_integer(Sz) ->
+valfun_3({test,test_arity,{f,Lbl},[Tuple,Sz]}, Vst) when is_integer(Sz) ->
assert_type(#t_tuple{}, Tuple, Vst),
Type = #t_tuple{exact=true,size=Sz},
type_test(Lbl, Type, Tuple, Vst);
-valfun_4({test,is_tagged_tuple,{f,Lbl},[Src,Sz,Atom]}, Vst) ->
+valfun_3({test,is_tagged_tuple,{f,Lbl},[Src,Sz,Atom]}, Vst) ->
assert_term(Src, Vst),
Es = #{ 1 => get_literal_type(Atom) },
Type = #t_tuple{exact=true,size=Sz,elements=Es},
type_test(Lbl, Type, Src, Vst);
-valfun_4({test,is_eq_exact,{f,Lbl},[Src,Val]=Ss}, Vst) ->
+valfun_3({test,is_eq_exact,{f,Lbl},[Src,Val]=Ss}, Vst) ->
validate_src(Ss, Vst),
branch(Lbl, Vst,
fun(FailVst) ->
@@ -899,7 +950,7 @@ valfun_4({test,is_eq_exact,{f,Lbl},[Src,Val]=Ss}, Vst) ->
fun(SuccVst) ->
update_eq_types(Src, Val, SuccVst)
end);
-valfun_4({test,is_ne_exact,{f,Lbl},[Src,Val]=Ss}, Vst) ->
+valfun_3({test,is_ne_exact,{f,Lbl},[Src,Val]=Ss}, Vst) ->
validate_src(Ss, Vst),
branch(Lbl, Vst,
fun(FailVst) ->
@@ -908,30 +959,30 @@ valfun_4({test,is_ne_exact,{f,Lbl},[Src,Val]=Ss}, Vst) ->
fun(SuccVst) ->
update_ne_types(Src, Val, SuccVst)
end);
-valfun_4({test,_Op,{f,Lbl},Src}, Vst) ->
+valfun_3({test,_Op,{f,Lbl},Src}, Vst) ->
%% is_pid, is_reference, et cetera.
validate_src(Src, Vst),
branch(Lbl, Vst, fun(V) -> V end);
-valfun_4({bs_add,{f,Fail},[A,B,_],Dst}, Vst) ->
+valfun_3({bs_add,{f,Fail},[A,B,_],Dst}, Vst) ->
assert_term(A, Vst),
assert_term(B, Vst),
branch(Fail, Vst,
fun(SuccVst) ->
create_term(#t_integer{}, bs_add, [A, B], Dst, SuccVst)
end);
-valfun_4({bs_utf8_size,{f,Fail},A,Dst}, Vst) ->
+valfun_3({bs_utf8_size,{f,Fail},A,Dst}, Vst) ->
assert_term(A, Vst),
branch(Fail, Vst,
fun(SuccVst) ->
create_term(#t_integer{}, bs_utf8_size, [A], Dst, SuccVst)
end);
-valfun_4({bs_utf16_size,{f,Fail},A,Dst}, Vst) ->
+valfun_3({bs_utf16_size,{f,Fail},A,Dst}, Vst) ->
assert_term(A, Vst),
branch(Fail, Vst,
fun(SuccVst) ->
create_term(#t_integer{}, bs_utf16_size, [A], Dst, SuccVst)
end);
-valfun_4({bs_init2,{f,Fail},Sz,Heap,Live,_,Dst}, Vst0) ->
+valfun_3({bs_init2,{f,Fail},Sz,Heap,Live,_,Dst}, Vst0) ->
verify_live(Live, Vst0),
verify_y_init(Vst0),
if
@@ -947,7 +998,7 @@ valfun_4({bs_init2,{f,Fail},Sz,Heap,Live,_,Dst}, Vst0) ->
create_term(#t_bitstring{unit=8}, bs_init2, [], Dst,
SuccVst, SuccVst0)
end);
-valfun_4({bs_init_bits,{f,Fail},Sz,Heap,Live,_,Dst}, Vst0) ->
+valfun_3({bs_init_bits,{f,Fail},Sz,Heap,Live,_,Dst}, Vst0) ->
verify_live(Live, Vst0),
verify_y_init(Vst0),
if
@@ -962,7 +1013,7 @@ valfun_4({bs_init_bits,{f,Fail},Sz,Heap,Live,_,Dst}, Vst0) ->
SuccVst = prune_x_regs(Live, SuccVst0),
create_term(#t_bitstring{}, bs_init_bits, [], Dst, SuccVst)
end);
-valfun_4({bs_append,{f,Fail},Bits,Heap,Live,Unit,Bin,_Flags,Dst}, Vst0) ->
+valfun_3({bs_append,{f,Fail},Bits,Heap,Live,Unit,Bin,_Flags,Dst}, Vst0) ->
verify_live(Live, Vst0),
verify_y_init(Vst0),
assert_term(Bits, Vst0),
@@ -974,7 +1025,7 @@ valfun_4({bs_append,{f,Fail},Bits,Heap,Live,Unit,Bin,_Flags,Dst}, Vst0) ->
create_term(#t_bitstring{unit=Unit}, bs_append,
[Bin], Dst, SuccVst, SuccVst0)
end);
-valfun_4({bs_private_append,{f,Fail},Bits,Unit,Bin,_Flags,Dst}, Vst) ->
+valfun_3({bs_private_append,{f,Fail},Bits,Unit,Bin,_Flags,Dst}, Vst) ->
assert_term(Bits, Vst),
assert_term(Bin, Vst),
branch(Fail, Vst,
@@ -982,55 +1033,55 @@ valfun_4({bs_private_append,{f,Fail},Bits,Unit,Bin,_Flags,Dst}, Vst) ->
create_term(#t_bitstring{unit=Unit}, bs_private_append,
[Bin], Dst, SuccVst)
end);
-valfun_4({bs_put_string,Sz,_}, Vst) when is_integer(Sz) ->
+valfun_3({bs_put_string,Sz,_}, Vst) when is_integer(Sz) ->
Vst;
-valfun_4({bs_put_binary,{f,Fail},Sz,_,_,Src}, Vst) ->
+valfun_3({bs_put_binary,{f,Fail},Sz,_,_,Src}, Vst) ->
assert_term(Sz, Vst),
assert_term(Src, Vst),
branch(Fail, Vst,
fun(SuccVst) ->
update_type(fun meet/2, #t_bitstring{}, Src, SuccVst)
end);
-valfun_4({bs_put_float,{f,Fail},Sz,_,_,Src}, Vst) ->
+valfun_3({bs_put_float,{f,Fail},Sz,_,_,Src}, Vst) ->
assert_term(Sz, Vst),
assert_term(Src, Vst),
branch(Fail, Vst,
fun(SuccVst) ->
update_type(fun meet/2, float, Src, SuccVst)
end);
-valfun_4({bs_put_integer,{f,Fail},Sz,_,_,Src}, Vst) ->
+valfun_3({bs_put_integer,{f,Fail},Sz,_,_,Src}, Vst) ->
assert_term(Sz, Vst),
assert_term(Src, Vst),
branch(Fail, Vst,
fun(SuccVst) ->
update_type(fun meet/2, #t_integer{}, Src, SuccVst)
end);
-valfun_4({bs_put_utf8,{f,Fail},_,Src}, Vst) ->
+valfun_3({bs_put_utf8,{f,Fail},_,Src}, Vst) ->
assert_term(Src, Vst),
branch(Fail, Vst,
fun(SuccVst) ->
update_type(fun meet/2, #t_integer{}, Src, SuccVst)
end);
-valfun_4({bs_put_utf16,{f,Fail},_,Src}, Vst) ->
+valfun_3({bs_put_utf16,{f,Fail},_,Src}, Vst) ->
assert_term(Src, Vst),
branch(Fail, Vst,
fun(SuccVst) ->
update_type(fun meet/2, #t_integer{}, Src, SuccVst)
end);
-valfun_4({bs_put_utf32,{f,Fail},_,Src}, Vst) ->
+valfun_3({bs_put_utf32,{f,Fail},_,Src}, Vst) ->
assert_term(Src, Vst),
branch(Fail, Vst,
fun(SuccVst) ->
update_type(fun meet/2, #t_integer{}, Src, SuccVst)
end);
%% Map instructions.
-valfun_4({put_map_assoc=Op,{f,Fail},Src,Dst,Live,{list,List}}, Vst) ->
+valfun_3({put_map_assoc=Op,{f,Fail},Src,Dst,Live,{list,List}}, Vst) ->
verify_put_map(Op, Fail, Src, Dst, Live, List, Vst);
-valfun_4({put_map_exact=Op,{f,Fail},Src,Dst,Live,{list,List}}, Vst) ->
+valfun_3({put_map_exact=Op,{f,Fail},Src,Dst,Live,{list,List}}, Vst) ->
verify_put_map(Op, Fail, Src, Dst, Live, List, Vst);
-valfun_4({get_map_elements,{f,Fail},Src,{list,List}}, Vst) ->
+valfun_3({get_map_elements,{f,Fail},Src,{list,List}}, Vst) ->
verify_get_map(Fail, Src, List, Vst);
-valfun_4(_, _) ->
+valfun_3(_, _) ->
error(unknown_instruction).
verify_get_map(Fail, Src, List, Vst0) ->
@@ -1053,8 +1104,11 @@ verify_get_map(Fail, Src, List, Vst0) ->
%% {get_map_elements,{f,7},{x,1},{list,[{atom,a},{x,1},{atom,b},{x,2}]}}.
%%
%% If 'a' exists but not 'b', {x,1} is overwritten when we jump to {f,7}.
+%%
+%% We must be careful to preserve the uninitialized status for Y registers
+%% that have been allocated but not yet defined.
clobber_map_vals([Key,Dst|T], Map, Vst0) ->
- case is_reg_defined(Dst, Vst0) of
+ case is_reg_initialized(Dst, Vst0) of
true ->
Vst = extract_term(any, {bif,map_get}, [Key, Map], Dst, Vst0),
clobber_map_vals(T, Map, Vst);
@@ -1064,6 +1118,17 @@ clobber_map_vals([Key,Dst|T], Map, Vst0) ->
clobber_map_vals([], _Map, Vst) ->
Vst.
+is_reg_initialized({x,_}=Reg, #vst{current=#st{xs=Xs}}) ->
+ is_map_key(Reg, Xs);
+is_reg_initialized({y,_}=Reg, #vst{current=#st{ys=Ys}}) ->
+ case Ys of
+ #{Reg:=Val} ->
+ Val =/= uninitialized;
+ #{} ->
+ false
+ end;
+is_reg_initialized(V, #vst{}) -> error({not_a_register, V}).
+
extract_map_keys([Key,_Val|T]) ->
[Key|extract_map_keys(T)];
extract_map_keys([]) -> [].
@@ -1097,7 +1162,40 @@ verify_put_map(Op, Fail, Src, Dst, Live, List, Vst0) ->
%% gc_bifs as X registers are pruned prior to calling this function, which may
%% have clobbered the sources.
%%
-validate_bif(Kind, Op, Fail, Ss, Dst, OrigVst, Vst) ->
+
+validate_bif(Op, Fail, Ss, Dst, Vst) ->
+ validate_src(Ss, Vst),
+ validate_bif_1(bif, Op, Fail, Ss, Dst, Vst, Vst).
+
+validate_gc_bif(Op, Fail, Ss, Dst, Live, #vst{current=St0}=Vst0) ->
+ validate_src(Ss, Vst0),
+ verify_live(Live, Vst0),
+ verify_y_init(Vst0),
+
+ %% Heap allocations and X registers are killed regardless of whether we
+ %% fail or not, as we may fail after GC.
+ St = kill_heap_allocation(St0),
+ Vst = prune_x_regs(Live, Vst0#vst{current=St}),
+ validate_src(Ss, Vst),
+
+ validate_bif_1(gc_bif, Op, Fail, Ss, Dst, Vst, Vst).
+
+validate_bif_1(Kind, Op, cannot_fail, Ss, Dst, OrigVst, Vst0) ->
+ %% This BIF explicitly cannot fail; it will not jump to a guard nor throw
+ %% an exception. Validation will fail if it returns 'none' or has a type
+ %% conflict on one of its arguments.
+
+ {Type, ArgTypes, _CanSubtract} = bif_types(Op, Ss, Vst0),
+ ZippedArgs = zip(Ss, ArgTypes),
+
+ Vst = foldl(fun({A, T}, V) ->
+ update_type(fun meet/2, T, A, V)
+ end, Vst0, ZippedArgs),
+
+ true = Type =/= none, %Assertion.
+
+ extract_term(Type, {Kind, Op}, Ss, Dst, Vst, OrigVst);
+validate_bif_1(Kind, Op, Fail, Ss, Dst, OrigVst, Vst) ->
{Type, ArgTypes, CanSubtract} = bif_types(Op, Ss, Vst),
ZippedArgs = zip(Ss, ArgTypes),
@@ -1115,7 +1213,7 @@ validate_bif(Kind, Op, Fail, Ss, Dst, OrigVst, Vst) ->
SuccVst = foldl(fun({A, T}, V) ->
update_type(fun meet/2, T, A, V)
end, SuccVst0, ZippedArgs),
- extract_term(Type, {Kind,Op}, Ss, Dst, SuccVst, OrigVst)
+ extract_term(Type, {Kind, Op}, Ss, Dst, SuccVst, OrigVst)
end,
branch(Fail, Vst, FailFun, SuccFun).
@@ -1219,8 +1317,9 @@ call(Name, Live, #vst{current=St0}=Vst0) ->
verify_call_args(Name, Live, Vst0),
verify_y_init(Vst0),
case call_types(Name, Live, Vst0) of
+ {none, _, _} ->
+ kill_state(Vst0);
{RetType, _, _} ->
- %% Type is never 'none' because it has been handled earlier.
St = St0#st{f=init_fregs()},
Vst = prune_x_regs(0, Vst0#vst{current=St}),
create_term(RetType, call, [], {x,0}, Vst)
@@ -1531,21 +1630,8 @@ validate_select_val(Fail, [Val,{f,L}|T], Src, Vst0) ->
update_ne_types(Src, Val, FailVst)
end),
validate_select_val(Fail, T, Src, Vst);
-validate_select_val(Fail, [], Src, Vst) ->
+validate_select_val(Fail, [], _Src, Vst) ->
branch(Fail, Vst,
- fun(FailVst) ->
- FailType = get_term_type(Src, FailVst),
- case beam_types:get_singleton_value(FailType) of
- {ok, Value} ->
- %% This is the only possible value at the fail
- %% label, so we can infer types as if we matched it
- %% directly.
- Lit = value_to_literal(Value),
- update_eq_types(Src, Lit, FailVst);
- error ->
- FailVst
- end
- end,
fun(SuccVst) ->
%% The next instruction is never executed.
kill_state(SuccVst)
@@ -1595,42 +1681,32 @@ infer_types(CompareOp, LHS, RHS, #vst{current=#st{vs=Vs}}=Vst0) ->
Vst0
end.
-infer_types_1(#value{op={bif,'=:='},args=[LHS,RHS]}, Val, Op, Vst0) ->
+infer_types_1(#value{op={bif,'=:='},args=[LHS,RHS]}, Val, Op, Vst) ->
case Val of
{atom, Bool} when Op =:= eq_exact, Bool; Op =:= ne_exact, not Bool ->
- Vst = infer_types(eq_exact, RHS, LHS, Vst0),
- infer_types(eq_exact, LHS, RHS, Vst);
+ update_eq_types(LHS, RHS, Vst);
{atom, Bool} when Op =:= ne_exact, Bool; Op =:= eq_exact, not Bool ->
- Vst = infer_types(ne_exact, RHS, LHS, Vst0),
- infer_types(ne_exact, LHS, RHS, Vst);
+ update_ne_types(LHS, RHS, Vst);
_ ->
- Vst0
+ Vst
end;
-infer_types_1(#value{op={bif,'=/='},args=[LHS,RHS]}, Val, Op, Vst0) ->
+infer_types_1(#value{op={bif,'=/='},args=[LHS,RHS]}, Val, Op, Vst) ->
case Val of
{atom, Bool} when Op =:= ne_exact, Bool; Op =:= eq_exact, not Bool ->
- Vst = infer_types(ne_exact, RHS, LHS, Vst0),
- infer_types(ne_exact, LHS, RHS, Vst);
+ update_ne_types(LHS, RHS, Vst);
{atom, Bool} when Op =:= eq_exact, Bool; Op =:= ne_exact, not Bool ->
- Vst = infer_types(eq_exact, RHS, LHS, Vst0),
- infer_types(eq_exact, LHS, RHS, Vst);
+ update_eq_types(LHS, RHS, Vst);
_ ->
- Vst0
+ Vst
end;
infer_types_1(#value{op={bif,element},args=[{integer,Index},Tuple]},
Val, Op, Vst) when Index >= 1 ->
- Merge = case Op of
- eq_exact -> fun meet/2;
- ne_exact -> fun subtract/2
- end,
- case is_value_alive(Tuple, Vst) of
- true ->
- ElementType = get_term_type(Val, Vst),
- Es = beam_types:set_element_type(Index, ElementType, #{}),
- Type = #t_tuple{size=Index,elements=Es},
- update_type(Merge, Type, Tuple, Vst);
- false ->
- Vst
+ ElementType = get_term_type(Val, Vst),
+ Es = beam_types:set_element_type(Index, ElementType, #{}),
+ Type = #t_tuple{size=Index,elements=Es},
+ case Op of
+ eq_exact -> update_type(fun meet/2, Type, Tuple, Vst);
+ ne_exact -> update_type(fun subtract/2, Type, Tuple, Vst)
end;
infer_types_1(#value{op={bif,is_atom},args=[Src]}, Val, Op, Vst) ->
infer_type_test_bif(#t_atom{}, Src, Val, Op, Vst);
@@ -1654,31 +1730,23 @@ infer_types_1(#value{op={bif,is_tuple},args=[Src]}, Val, Op, Vst) ->
infer_type_test_bif(#t_tuple{}, Src, Val, Op, Vst);
infer_types_1(#value{op={bif,tuple_size}, args=[Tuple]},
{integer,Arity}, Op, Vst) ->
- Merge = case Op of
- eq_exact -> fun meet/2;
- ne_exact -> fun subtract/2
- end,
- case is_value_alive(Tuple, Vst) of
- true ->
- Type = #t_tuple{exact=true,size=Arity},
- update_type(Merge, Type, Tuple, Vst);
- false ->
- Vst
+ Type = #t_tuple{exact=true,size=Arity},
+ case Op of
+ eq_exact -> update_type(fun meet/2, Type, Tuple, Vst);
+ ne_exact -> update_type(fun subtract/2, Type, Tuple, Vst)
end;
infer_types_1(_, _, _, Vst) ->
Vst.
-infer_type_test_bif(Type, Src, {atom, Bool}, Op, Vst) when is_boolean(Bool) ->
- case is_value_alive(Src, Vst) of
- true when Op =:= eq_exact, Bool; Op =:= ne_exact, not Bool ->
+infer_type_test_bif(Type, Src, Val, Op, Vst) ->
+ case Val of
+ {atom, Bool} when Op =:= eq_exact, Bool; Op =:= ne_exact, not Bool ->
update_type(fun meet/2, Type, Src, Vst);
- true when Op =:= ne_exact, Bool; Op =:= eq_exact, not Bool ->
+ {atom, Bool} when Op =:= ne_exact, Bool; Op =:= eq_exact, not Bool ->
update_type(fun subtract/2, Type, Src, Vst);
- false ->
+ _ ->
Vst
- end;
-infer_type_test_bif(_, _, _, _, Vst) ->
- Vst.
+ end.
%%%
%%% Keeping track of types.
@@ -1797,17 +1865,21 @@ update_type(Merge, With, Literal, Vst) ->
end.
update_eq_types(LHS, RHS, Vst0) ->
- Vst1 = infer_types(eq_exact, LHS, RHS, Vst0),
+ LType = get_term_type(LHS, Vst0),
+ RType = get_term_type(RHS, Vst0),
- T1 = get_term_type(LHS, Vst1),
- T2 = get_term_type(RHS, Vst1),
+ Vst1 = update_type(fun meet/2, RType, LHS, Vst0),
+ Vst = update_type(fun meet/2, LType, RHS, Vst1),
- Vst = update_type(fun meet/2, T2, LHS, Vst1),
- update_type(fun meet/2, T1, RHS, Vst).
+ infer_types(eq_exact, LHS, RHS, Vst).
update_ne_types(LHS, RHS, Vst0) ->
- Vst = infer_types(ne_exact, LHS, RHS, Vst0),
+ Vst1 = update_ne_types_1(LHS, RHS, Vst0),
+ Vst = update_ne_types_1(RHS, LHS, Vst1),
+ infer_types(ne_exact, LHS, RHS, Vst).
+
+update_ne_types_1(LHS, RHS, Vst0) ->
%% While updating types on equality is fairly straightforward, inequality
%% is a bit trickier since all we know is that the *value* of LHS differs
%% from RHS, so we can't blindly subtract their types.
@@ -1817,10 +1889,23 @@ update_ne_types(LHS, RHS, Vst0) ->
%% #t_integer{} we would erroneously infer that the new type is float.
%%
%% Therefore, we only subtract when we know that RHS has a specific value.
- RType = get_term_type(RHS, Vst),
+ RType = get_term_type(RHS, Vst0),
case beam_types:is_singleton_type(RType) of
- true -> update_type(fun subtract/2, RType, LHS, Vst);
- false -> Vst
+ true ->
+ Vst = update_type(fun subtract/2, RType, LHS, Vst0),
+
+ %% If LHS has a specific value after subtraction we can infer types
+ %% as if we've made an exact match, which is much stronger than
+ %% ne_exact.
+ LType = get_term_type(LHS, Vst),
+ case beam_types:get_singleton_value(LType) of
+ {ok, Value} ->
+ infer_types(eq_exact, LHS, value_to_literal(Value), Vst);
+ error ->
+ Vst
+ end;
+ false ->
+ Vst0
end.
%% Helper functions for the above.
@@ -1883,9 +1968,9 @@ new_value(Type, Op, Ss, #vst{current=#st{vs=Vs0}=St,ref_ctr=Counter}=Vst) ->
{Ref, Vst#vst{current=St#st{vs=Vs},ref_ctr=Counter+1}}.
-kill_catch_tag(Reg, #vst{current=#st{ct=[Fail|Fails]}=St}=Vst0) ->
- Vst = Vst0#vst{current=St#st{ct=Fails,fls=undefined}},
- {_, Fail} = get_tag_type(Reg, Vst), %Assertion.
+kill_catch_tag(Reg, #vst{current=#st{ct=[Tag|Tags]}=St}=Vst0) ->
+ Vst = Vst0#vst{current=St#st{ct=Tags,fls=undefined}},
+ Tag = get_tag_type(Reg, Vst), %Assertion.
kill_tag(Reg, Vst).
check_try_catch_tags(Type, {y,N}=Reg, Vst) ->
@@ -1903,10 +1988,6 @@ check_try_catch_tags(Type, {y,N}=Reg, Vst) ->
ok
end.
-is_reg_defined({x,_}=Reg, #vst{current=#st{xs=Xs}}) -> is_map_key(Reg, Xs);
-is_reg_defined({y,_}=Reg, #vst{current=#st{ys=Ys}}) -> is_map_key(Reg, Ys);
-is_reg_defined(V, #vst{}) -> error({not_a_register, V}).
-
assert_term(Src, Vst) ->
_ = get_term_type(Src, Vst),
ok.
@@ -2040,11 +2121,6 @@ get_raw_type(#value_ref{}=Ref, #vst{current=#st{vs=Vs}}) ->
get_raw_type(Src, #vst{}) ->
get_literal_type(Src).
-is_value_alive(#value_ref{}=Ref, #vst{current=#st{vs=Vs}}) ->
- is_map_key(Ref, Vs);
-is_value_alive(_, _) ->
- false.
-
get_literal_type(nil) ->
beam_types:make_type_from_value([]);
get_literal_type({atom,A}) when is_atom(A) ->
@@ -2074,10 +2150,12 @@ get_literal_type(T) ->
SuccFun :: BranchFun) -> #vst{} when
BranchFun :: fun((#vst{}) -> #vst{}).
branch(Lbl, Vst0, FailFun, SuccFun) ->
+ validate_branch(Lbl, Vst0),
#vst{current=St0} = Vst0,
+
try FailFun(Vst0) of
Vst1 ->
- Vst2 = branch_state(Lbl, Vst1),
+ Vst2 = fork_state(Lbl, Vst1),
Vst = Vst2#vst{current=St0},
try SuccFun(Vst) of
V -> V
@@ -2095,6 +2173,24 @@ branch(Lbl, Vst0, FailFun, SuccFun) ->
SuccFun(Vst0)
end.
+validate_branch(Lbl, #vst{current=#st{ct=Tags}}) ->
+ validate_branch_1(Lbl, Tags).
+
+validate_branch_1(Lbl, [{trytag, FailLbls} | Tags]) ->
+ %% 'try_case' assumes that an exception has been thrown, so a direct branch
+ %% will crash the emulator.
+ %%
+ %% (Jumping to a 'catch_end' is fine however as it will simply nop in the
+ %% absence of an exception.)
+ case ordsets:is_element(Lbl, FailLbls) of
+ true -> error({illegal_branch, try_handler, Lbl});
+ false -> validate_branch_1(Lbl, Tags)
+ end;
+validate_branch_1(Lbl, [_ | Tags]) ->
+ validate_branch_1(Lbl, Tags);
+validate_branch_1(_Lbl, []) ->
+ ok.
+
%% A shorthand version of branch/4 for when the state is only altered on
%% success.
branch(Fail, Vst, SuccFun) ->
@@ -2102,12 +2198,12 @@ branch(Fail, Vst, SuccFun) ->
%% Directly branches off the state. This is an "internal" operation that should
%% be used sparingly.
-branch_state(0, #vst{}=Vst) ->
+fork_state(0, #vst{}=Vst) ->
%% If the instruction fails, the stack may be scanned looking for a catch
%% tag. Therefore the Y registers must be initialized at this point.
verify_y_init(Vst),
Vst;
-branch_state(L, #vst{current=St,branched=B,ref_ctr=Counter0}=Vst) ->
+fork_state(L, #vst{current=St,branched=B,ref_ctr=Counter0}=Vst) ->
case gb_trees:is_defined(L, B) of
true ->
{MergedSt, Counter} = merge_states(L, St, B, Counter0),
@@ -2187,10 +2283,10 @@ merge_tags(uninitialized, _) ->
uninitialized;
merge_tags(_, uninitialized) ->
uninitialized;
-merge_tags({catchtag,T0}, {catchtag,T1}) ->
- {catchtag, ordsets:from_list(T0 ++ T1)};
-merge_tags({trytag,T0}, {trytag,T1}) ->
- {trytag, ordsets:from_list(T0 ++ T1)};
+merge_tags({trytag, LblsA}, {trytag, LblsB}) ->
+ {trytag, ordsets:union(LblsA, LblsB)};
+merge_tags({catchtag, LblsA}, {catchtag, LblsB}) ->
+ {catchtag, ordsets:union(LblsA, LblsB)};
merge_tags(_A, _B) ->
%% All other combinations leave the register initialized. Errors arising
%% from this will be caught later on.
@@ -2214,25 +2310,44 @@ merge_vrefs(RefA, RefB, Merge, Counter) ->
merge_values(Merge, VsA, VsB) ->
maps:fold(fun(Spec, New, Acc) ->
- merge_values_1(Spec, New, VsA, VsB, Acc)
+ mv_1(Spec, New, VsA, VsB, Acc)
end, #{}, Merge).
-merge_values_1(Same, Same, VsA, VsB, Acc) ->
+mv_1(Same, Same, VsA, VsB, Acc0) ->
%% We're merging different versions of the same value, so it's safe to
%% reuse old entries if the type's unchanged.
- #value{type=TypeA}=EntryA = map_get(Same, VsA),
- #value{type=TypeB}=EntryB = map_get(Same, VsB),
+ #value{type=TypeA,args=Args}=EntryA = map_get(Same, VsA),
+ #value{type=TypeB,args=Args}=EntryB = map_get(Same, VsB),
+
Entry = case join(TypeA, TypeB) of
TypeA -> EntryA;
TypeB -> EntryB;
JoinedType -> EntryA#value{type=JoinedType}
end,
- Acc#{ Same => Entry };
-merge_values_1({RefA, RefB}, New, VsA, VsB, Acc) ->
+
+ Acc = Acc0#{ Same => Entry },
+
+ %% Type inference may depend on values that are no longer reachable from a
+ %% register, so all arguments must be merged into the new state.
+ mv_args(Args, VsA, VsB, Acc);
+mv_1({RefA, RefB}, New, VsA, VsB, Acc) ->
#value{type=TypeA} = map_get(RefA, VsA),
#value{type=TypeB} = map_get(RefB, VsB),
Acc#{ New => #value{op=join,args=[],type=join(TypeA, TypeB)} }.
+mv_args([#value_ref{}=Arg | Args], VsA, VsB, Acc0) ->
+ case Acc0 of
+ #{ Arg := _ } ->
+ mv_args(Args, VsA, VsB, Acc0);
+ #{} ->
+ Acc = mv_1(Arg, Arg, VsA, VsB, Acc0),
+ mv_args(Args, VsA, VsB, Acc)
+ end;
+mv_args([_ | Args], VsA, VsB, Acc) ->
+ mv_args(Args, VsA, VsB, Acc);
+mv_args([], _VsA, _VsB, Acc) ->
+ Acc.
+
merge_fragility(FragileA, FragileB) ->
cerl_sets:union(FragileA, FragileB).
@@ -2242,10 +2357,14 @@ merge_stk(_, _) -> undecided.
merge_ct(S, S) -> S;
merge_ct(Ct0, Ct1) -> merge_ct_1(Ct0, Ct1).
-merge_ct_1([C0|Ct0], [C1|Ct1]) ->
- [ordsets:from_list(C0++C1)|merge_ct_1(Ct0, Ct1)];
-merge_ct_1([], []) -> [];
-merge_ct_1(_, _) -> undecided.
+merge_ct_1([], []) ->
+ [];
+merge_ct_1([{trytag, LblsA} | CtA], [{trytag, LblsB} | CtB]) ->
+ [{trytag, ordsets:union(LblsA, LblsB)} | merge_ct_1(CtA, CtB)];
+merge_ct_1([{catchtag, LblsA} | CtA], [{catchtag, LblsB} | CtB]) ->
+ [{catchtag, ordsets:union(LblsA, LblsB)} | merge_ct_1(CtA, CtB)];
+merge_ct_1(_, _) ->
+ undecided.
verify_y_init(#vst{current=#st{numy=NumY,ys=Ys}}=Vst) when is_integer(NumY) ->
HighestY = maps:fold(fun({y,Y}, _, Acc) -> max(Y, Acc) end, -1, Ys),
@@ -2401,6 +2520,25 @@ call_types({extfunc,M,F,A}, A, Vst) ->
call_types(_, A, Vst) ->
{any, get_call_args(A, Vst), false}.
+will_bif_succeed(fadd, [_,_], _Vst) ->
+ maybe;
+will_bif_succeed(fdiv, [_,_], _Vst) ->
+ maybe;
+will_bif_succeed(fmul, [_,_], _Vst) ->
+ maybe;
+will_bif_succeed(fnegate, [_], _Vst) ->
+ maybe;
+will_bif_succeed(fsub, [_,_], _Vst) ->
+ maybe;
+will_bif_succeed(Op, Ss, Vst) ->
+ Args = [normalize(get_term_type(Arg, Vst)) || Arg <- Ss],
+ beam_call_types:will_succeed(erlang, Op, Args).
+
+will_call_succeed({extfunc,M,F,A}, Vst) ->
+ beam_call_types:will_succeed(M, F, get_call_args(A, Vst));
+will_call_succeed(_Call, _Vst) ->
+ maybe.
+
get_call_args(Arity, Vst) ->
get_call_args_1(0, Arity, Vst).
diff --git a/lib/compiler/src/cerl_sets.erl b/lib/compiler/src/cerl_sets.erl
index f489baf238..84e488fc55 100644
--- a/lib/compiler/src/cerl_sets.erl
+++ b/lib/compiler/src/cerl_sets.erl
@@ -153,14 +153,21 @@ intersection1(S1, []) -> S1.
Set1 :: set(Element),
Set2 :: set(Element).
-is_disjoint(S1, S2) when map_size(S1) < map_size(S2) ->
- fold(fun (_, false) -> false;
- (E, true) -> not is_element(E, S2)
- end, true, S1);
+is_disjoint(S1, S2) when map_size(S1) > map_size(S2) ->
+ is_disjoint_1(S1, maps:iterator(S2));
is_disjoint(S1, S2) ->
- fold(fun (_, false) -> false;
- (E, true) -> not is_element(E, S1)
- end, true, S2).
+ is_disjoint_1(S2, maps:iterator(S1)).
+
+is_disjoint_1(Set, Iter) ->
+ case maps:next(Iter) of
+ {K, _, NextIter} ->
+ case Set of
+ #{K := _} -> false;
+ #{} -> is_disjoint_1(Set, NextIter)
+ end;
+ none ->
+ true
+ end.
%% subtract(Set1, Set2) -> Set.
%% Return all and only the elements of Set1 which are not also in
@@ -180,8 +187,21 @@ subtract(S1, S2) ->
Set1 :: set(Element),
Set2 :: set(Element).
+is_subset(S1, S2) when map_size(S1) > map_size(S2) ->
+ false;
is_subset(S1, S2) ->
- fold(fun (E, Sub) -> Sub andalso is_element(E, S2) end, true, S1).
+ is_subset_1(S2, maps:iterator(S1)).
+
+is_subset_1(Set, Iter) ->
+ case maps:next(Iter) of
+ {K, _, NextIter} ->
+ case Set of
+ #{K := _} -> is_subset_1(Set, NextIter);
+ #{} -> false
+ end;
+ none ->
+ true
+ end.
%% fold(Fun, Accumulator, Set) -> Accumulator.
%% Fold function Fun over all elements in Set and return Accumulator.
@@ -193,8 +213,16 @@ is_subset(S1, S2) ->
AccIn :: Acc,
AccOut :: Acc.
-fold(F, Init, D) ->
- lists:foldl(fun(E,Acc) -> F(E,Acc) end,Init,maps:keys(D)).
+fold(Fun, Init, Set) ->
+ fold_1(Fun, Init, maps:iterator(Set)).
+
+fold_1(Fun, Acc, Iter) ->
+ case maps:next(Iter) of
+ {K, _, NextIter} ->
+ fold_1(Fun, Fun(K,Acc), NextIter);
+ none ->
+ Acc
+ end.
%% filter(Fun, Set) -> Set.
%% Filter Set with Fun.
@@ -203,5 +231,18 @@ fold(F, Init, D) ->
Set1 :: set(Element),
Set2 :: set(Element).
-filter(F, D) ->
- maps:filter(fun(K,_) -> F(K) end, D).
+filter(Fun, Set) ->
+ maps:from_list(filter_1(Fun, maps:iterator(Set))).
+
+filter_1(Fun, Iter) ->
+ case maps:next(Iter) of
+ {K, _, NextIter} ->
+ case Fun(K) of
+ true ->
+ [{K,ok} | filter_1(Fun, NextIter)];
+ false ->
+ filter_1(Fun, NextIter)
+ end;
+ none ->
+ []
+ end.
diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl
index 42f9e8b902..21d67f5649 100644
--- a/lib/compiler/src/compile.erl
+++ b/lib/compiler/src/compile.erl
@@ -271,8 +271,11 @@ expand_opt(r22, Os) ->
[no_shared_fun_wrappers, no_swap | Os];
expand_opt({debug_info_key,_}=O, Os) ->
[encrypt_debug_info,O|Os];
-expand_opt(no_type_opt, Os) ->
- [no_ssa_opt_type_start,
+expand_opt(no_type_opt=O, Os) ->
+ %% Be sure to keep the no_type_opt option so that it will
+ %% be recorded in the BEAM file, allowing the test suites
+ %% to recompile the file with this option.
+ [O,no_ssa_opt_type_start,
no_ssa_opt_type_continue,
no_ssa_opt_type_finish | Os];
expand_opt(O, Os) -> [O|Os].
diff --git a/lib/compiler/src/erl_bifs.erl b/lib/compiler/src/erl_bifs.erl
index 94a5dfe012..1b60995de9 100644
--- a/lib/compiler/src/erl_bifs.erl
+++ b/lib/compiler/src/erl_bifs.erl
@@ -80,10 +80,12 @@ is_pure(erlang, 'or', 2) -> true;
is_pure(erlang, 'rem', 2) -> true;
is_pure(erlang, 'xor', 2) -> true;
is_pure(erlang, abs, 1) -> true;
+is_pure(erlang, atom_to_binary, 1) -> true;
is_pure(erlang, atom_to_binary, 2) -> true;
is_pure(erlang, atom_to_list, 1) -> true;
is_pure(erlang, binary_part, 2) -> true;
is_pure(erlang, binary_part, 3) -> true;
+is_pure(erlang, binary_to_atom, 1) -> true;
is_pure(erlang, binary_to_atom, 2) -> true;
is_pure(erlang, binary_to_float, 1) -> true;
is_pure(erlang, binary_to_integer, 1) -> true;
diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl
index 6fd1790c1a..49fb66126f 100644
--- a/lib/compiler/src/v3_kernel.erl
+++ b/lib/compiler/src/v3_kernel.erl
@@ -91,6 +91,7 @@
-include("core_parse.hrl").
-include("v3_kernel.hrl").
+-define(EXPAND_MAX_SIZE_SEGMENT, 1024).
%% These are not defined in v3_kernel.hrl.
get_kanno(Kthing) -> element(2, Kthing).
@@ -1170,7 +1171,7 @@ validate_bin_element_size(#k_int{val=V}) when V >= 0 -> ok;
validate_bin_element_size(#k_atom{val=all}) -> ok;
validate_bin_element_size(#k_atom{val=undefined}) -> ok;
validate_bin_element_size(_) -> throw(bad_element_size).
-
+
%% atomic_list([Cexpr], Sub, State) -> {[Kexpr],[PreKexpr],State}.
atomic_list(Ces, Sub, St) ->
@@ -1296,14 +1297,63 @@ pattern_bin_1([#c_bitstr{anno=A,val=E0,size=S0,unit=U,type=T,flags=Fs}|Es0],
_ -> Isub0
end,
{Es,{Isub,Osub},St3} = pattern_bin_1(Es0, Isub1, Osub1, St2),
- {#k_bin_seg{anno=A,size=S,
- unit=U0,
- type=cerl:concrete(T),
- flags=Fs0,
- seg=E,next=Es},
- {Isub,Osub},St3};
+ {build_bin_seg(A, S, U0, cerl:concrete(T), Fs0, E, Es),{Isub,Osub},St3};
pattern_bin_1([], Isub, Osub, St) -> {#k_bin_end{},{Isub,Osub},St}.
+%% build_bin_seg(Anno, Size, Unit, Type, Flags, Seg, Next) -> #k_bin_seg{}.
+%% This function normalizes literal integers with size > 8 and literal
+%% utf8 segments into integers with size = 8 (and potentially an integer
+%% with size less than 8 at the end). This is so further optimizations
+%% have a normalized view of literal integers, allowing us to generate
+%% more literals and group more clauses. Those integers may be "squeezed"
+%% later into the largest integer possible.
+%%
+build_bin_seg(A, #k_int{val=Bits} = Sz, U, integer=Type, [unsigned,big]=Flags, #k_literal{val=Int}=Seg, Next) ->
+ Size = Bits * U,
+ case integer_fits_and_is_expandable(Int, Size) of
+ true -> build_bin_seg_integer_recur(A, Size, Int, Next);
+ false -> #k_bin_seg{anno=A,size=Sz,unit=U,type=Type,flags=Flags,seg=Seg,next=Next}
+ end;
+build_bin_seg(A, Sz, U, utf8=Type, [unsigned,big]=Flags, #k_literal{val=Utf8} = Seg, Next) ->
+ case utf8_fits(Utf8) of
+ {Int, Bits} -> build_bin_seg_integer_recur(A, Bits, Int, Next);
+ error -> #k_bin_seg{anno=A,size=Sz,unit=U,type=Type,flags=Flags,seg=Seg,next=Next}
+ end;
+build_bin_seg(A, Sz, U, Type, Flags, Seg, Next) ->
+ #k_bin_seg{anno=A,size=Sz,unit=U,type=Type,flags=Flags,seg=Seg,next=Next}.
+
+build_bin_seg_integer_recur(A, Bits, Val, Next) when Bits > 8 ->
+ NextBits = Bits - 8,
+ NextVal = Val band ((1 bsl NextBits) - 1),
+ Last = build_bin_seg_integer_recur(A, NextBits, NextVal, Next),
+ build_bin_seg_integer(A, 8, Val bsr NextBits, Last);
+
+build_bin_seg_integer_recur(A, Bits, Val, Next) ->
+ build_bin_seg_integer(A, Bits, Val, Next).
+
+build_bin_seg_integer(A, Bits, Val, Next) ->
+ Sz = #k_int{anno=A,val=Bits},
+ Seg = #k_literal{anno=A,val=Val},
+ #k_bin_seg{anno=A,size=Sz,unit=1,type=integer,flags=[unsigned,big],seg=Seg,next=Next}.
+
+integer_fits_and_is_expandable(Int, Size) when 0 < Size, Size =< ?EXPAND_MAX_SIZE_SEGMENT ->
+ case <<Int:Size>> of
+ <<Int:Size>> -> true;
+ _ -> false
+ end;
+integer_fits_and_is_expandable(_Int, _Size) ->
+ false.
+
+utf8_fits(Utf8) ->
+ try
+ Bin = <<Utf8/utf8>>,
+ Bits = bit_size(Bin),
+ <<Int:Bits>> = Bin,
+ {Int, Bits}
+ catch
+ _:_ -> error
+ end.
+
%% pattern_list([Cexpr], Sub, State) -> {[Kexpr],Sub,State}.
pattern_list(Ces, Sub, St) ->
@@ -1553,7 +1603,7 @@ maybe_add_warning(Ke, MatchAnno, St) ->
get_line([Line|_]) when is_integer(Line) -> Line;
get_line([_|T]) -> get_line(T);
get_line([]) -> none.
-
+
get_file([{file,File}|_]) -> File;
get_file([_|T]) -> get_file(T);
get_file([]) -> "no_file". % should not happen
@@ -1761,27 +1811,10 @@ do_combine_lit_pat(#k_tuple{anno=A,es=Es0}) ->
do_combine_lit_pat(_) ->
throw(not_possible).
-combine_bin_segs(#k_bin_seg{size=Size0,unit=Unit,type=integer,
- flags=[unsigned,big],seg=Seg,next=Next}) ->
- #k_literal{val=Size1} = do_combine_lit_pat(Size0),
- #k_literal{val=Int} = do_combine_lit_pat(Seg),
- Size = Size1 * Unit,
- if
- 0 < Size, Size < 64 ->
- Bin = <<Int:Size>>,
- case Bin of
- <<Int:Size>> ->
- NextBin = combine_bin_segs(Next),
- <<Bin/bits,NextBin/bits>>;
- _ ->
- %% The integer Int does not fit in the segment,
- %% thus it will not match.
- throw(not_possible)
- end;
- true ->
- %% Avoid creating huge binary literals.
- throw(not_possible)
- end;
+combine_bin_segs(#k_bin_seg{size=#k_int{val=8},unit=1,type=integer,
+ flags=[unsigned,big],seg=#k_literal{val=Int},next=Next})
+ when is_integer(Int), 0 =< Int, Int =< 255 ->
+ <<Int,(combine_bin_segs(Next))/bits>>;
combine_bin_segs(#k_bin_end{}) ->
<<>>;
combine_bin_segs(_) ->
@@ -1851,11 +1884,10 @@ handle_bin_con_not_possible([]) -> [].
select_bin_int([#iclause{pats=[#k_bin_seg{anno=A,type=integer,
size=#k_int{val=Bits0}=Sz,unit=U,
flags=Fl,seg=#k_literal{val=Val},
- next=N}|Ps]}=C|Cs0])
- when is_integer(Val) ->
+ next=N}|Ps]}=C|Cs0]) ->
Bits = U * Bits0,
if
- Bits > 1024 -> throw(not_possible); %Expands the code too much.
+ Bits > ?EXPAND_MAX_SIZE_SEGMENT -> throw(not_possible); %Expands the code too much.
true -> ok
end,
select_assert_match_possible(Bits, Val, Fl),
@@ -1866,16 +1898,6 @@ select_bin_int([#iclause{pats=[#k_bin_seg{anno=A,type=integer,
end,
Cs = select_bin_int_1(Cs0, Bits, Fl, Val),
[{k_bin_int,[C#iclause{pats=[P|Ps]}|Cs]}];
-select_bin_int([#iclause{pats=[#k_bin_seg{anno=A,type=utf8,
- flags=[unsigned,big]=Fl,
- seg=#k_literal{val=Val0},
- next=N}|Ps]}=C|Cs0])
- when is_integer(Val0) ->
- {Val,Bits} = select_utf8(Val0),
- P = #k_bin_int{anno=A,size=#k_int{val=Bits},unit=1,
- flags=Fl,val=Val,next=N},
- Cs = select_bin_int_1(Cs0, Bits, Fl, Val),
- [{k_bin_int,[C#iclause{pats=[P|Ps]}|Cs]}];
select_bin_int(_) -> throw(not_possible).
select_bin_int_1([#iclause{pats=[#k_bin_seg{anno=A,type=integer,
@@ -1890,18 +1912,6 @@ select_bin_int_1([#iclause{pats=[#k_bin_seg{anno=A,type=integer,
end,
P = #k_bin_int{anno=A,size=Sz,unit=U,flags=Fl,val=Val,next=N},
[C#iclause{pats=[P|Ps]}|select_bin_int_1(Cs, Bits, Fl, Val)];
-select_bin_int_1([#iclause{pats=[#k_bin_seg{anno=A,type=utf8,
- flags=Fl,
- seg=#k_literal{val=Val0},
- next=N}|Ps]}=C|Cs],
- Bits, Fl, Val) when is_integer(Val0) ->
- case select_utf8(Val0) of
- {Val,Bits} -> ok;
- {_,_} -> throw(not_possible)
- end,
- P = #k_bin_int{anno=A,size=#k_int{val=Bits},unit=1,
- flags=[unsigned,big],val=Val,next=N},
- [C#iclause{pats=[P|Ps]}|select_bin_int_1(Cs, Bits, Fl, Val)];
select_bin_int_1([], _, _, _) -> [];
select_bin_int_1(_, _, _, _) -> throw(not_possible).
@@ -1927,17 +1937,6 @@ match_fun(Val) ->
{match,Bs}
end.
-select_utf8(Val0) ->
- try
- Bin = <<Val0/utf8>>,
- Size = bit_size(Bin),
- <<Val:Size>> = Bin,
- {Val,Size}
- catch
- error:_ ->
- throw(not_possible)
- end.
-
%% match_value([Var], Con, [Clause], Default, State) -> {SelectExpr,State}.
%% At this point all the clauses have the same constructor, we must
%% now separate them according to value.
@@ -2057,7 +2056,8 @@ match_clause([U|Us], [C|_]=Cs0, Def, St0) ->
{Match0,Vs,St1} = get_match(get_con(Cs0), St0),
Match = sub_size_var(Match0, Cs0),
{Cs1,St2} = new_clauses(Cs0, U, St1),
- {B,St3} = match(Vs ++ Us, Cs1, Def, St2),
+ Cs2 = squeeze_clauses_by_bin_integer_count(Cs1, []),
+ {B,St3} = match(Vs ++ Us, Cs2, Def, St2),
{#k_val_clause{anno=Anno,val=Match,body=B},St3}.
sub_size_var(#k_bin_seg{size=#k_var{name=Name}=Kvar}=BinSeg, [#iclause{isub=Sub}|_]) ->
@@ -2127,6 +2127,102 @@ new_clauses(Cs0, U, St) ->
end, Cs0),
{Cs1,St}.
+%% group and squeeze
+%% The goal of those functions is to group subsequent integer k_bin_seg
+%% literals by count so we can leverage bs_get_integer_16 whenever possible.
+%%
+%% The priority is to create large groups. So if we have three clauses matching
+%% on 16-bits/16-bits/8-bits, we will first have a single 8-bits match for all
+%% three clauses instead of clauses (one with 16 and another with 8). But note
+%% the algorithm is recursive, so the remaining 8-bits for the first two clauses
+%% will be grouped next.
+%%
+%% We also try to not create too large groups. If we have too many clauses,
+%% it is preferrable to match on 8-bits, select a branch, then match on the
+%% next 8-bits, rather than match on 16-bits which would force us to have
+%% to select to many values at the same time, which would not be efficient.
+%%
+%% Another restriction is that we create groups only if the end of the
+%% group is a variadic clause or the end of the binary. That's because
+%% if we have 16-bits/16-bits/catch-all, breaking it into a 16-bits lookup
+%% will make the catch-all more expensive.
+%%
+%% Clauses are grouped in reverse when squeezing and then flattened and
+%% re-reversed at the end.
+squeeze_clauses_by_bin_integer_count([Clause | Clauses], Acc) ->
+ case clause_count_bin_integer_segments(Clause) of
+ {literal, N} -> squeeze_clauses_by_bin_integer_count(Clauses, N, 1, [Clause], Acc);
+ _ -> squeeze_clauses_by_bin_integer_count(Clauses, [[Clause] | Acc])
+ end;
+squeeze_clauses_by_bin_integer_count(_, Acc) ->
+ flat_reverse(Acc, []).
+
+squeeze_clauses_by_bin_integer_count([], N, Count, GroupAcc, Acc) ->
+ Squeezed = squeeze_clauses(GroupAcc, fix_count_without_variadic_segment(N), Count),
+ flat_reverse([Squeezed | Acc], []);
+squeeze_clauses_by_bin_integer_count([#iclause{pats=[#k_bin_end{} | _]} = Clause], N, Count, GroupAcc, Acc) ->
+ Squeezed = squeeze_clauses(GroupAcc, fix_count_without_variadic_segment(N), Count),
+ flat_reverse([[Clause | Squeezed] | Acc], []);
+squeeze_clauses_by_bin_integer_count([Clause | Clauses], N, Count, GroupAcc, Acc) ->
+ case clause_count_bin_integer_segments(Clause) of
+ {literal, NewN} ->
+ squeeze_clauses_by_bin_integer_count(Clauses, min(N, NewN), Count + 1, [Clause | GroupAcc], Acc);
+
+ {variadic, NewN} when NewN =< N ->
+ Squeezed = squeeze_clauses(GroupAcc, NewN, Count),
+ squeeze_clauses_by_bin_integer_count(Clauses, [[Clause | Squeezed] | Acc]);
+
+ _ ->
+ squeeze_clauses_by_bin_integer_count(Clauses, [[Clause | GroupAcc] | Acc])
+ end.
+
+clause_count_bin_integer_segments(#iclause{pats=[#k_bin_seg{seg=#k_literal{}} = BinSeg | _]}) ->
+ count_bin_integer_segments(BinSeg, 0);
+clause_count_bin_integer_segments(#iclause{pats=[#k_bin_seg{size=#k_int{val=Size},unit=Unit,
+ type=integer,flags=[unsigned,big], seg=#k_var{}} | _]})
+ when ((Size * Unit) rem 8) =:= 0 ->
+ {variadic, (Size * Unit) div 8};
+clause_count_bin_integer_segments(_) ->
+ error.
+
+count_bin_integer_segments(#k_bin_seg{size=#k_int{val=8},unit=1,type=integer,flags=[unsigned,big],
+ seg=#k_literal{val=Int},next=Next}, Count) when is_integer(Int), 0 =< Int, Int =< 255 ->
+ count_bin_integer_segments(Next, Count + 1);
+count_bin_integer_segments(_, Count) when Count > 0 ->
+ {literal, Count};
+count_bin_integer_segments(_, _Count) ->
+ error.
+
+%% Since 4 bytes in on 32-bits systems are bignums, we convert
+%% anything more than 3 into 2 bytes lookup. The goal is to convert
+%% any multi-clause segment into 2-byte lookups with a potential
+%% 3 byte lookup at the end.
+fix_count_without_variadic_segment(N) when N > 3 -> 2;
+fix_count_without_variadic_segment(N) -> N.
+
+%% If we have more than 16 clauses, then it is better
+%% to branch multiple times than getting a large integer.
+%% We also abort if we have nothing to squeeze.
+squeeze_clauses(Clauses, Size, Count) when Count >= 16; Size == 1 -> Clauses;
+squeeze_clauses(Clauses, Size, _Count) -> squeeze_clauses(Clauses, Size).
+
+squeeze_clauses([#iclause{pats=[#k_bin_seg{seg=#k_literal{}} = BinSeg | Pats]} = Clause | Clauses], Size) ->
+ [Clause#iclause{pats=[squeeze_segments(BinSeg, 0, 0, Size) | Pats]} |
+ squeeze_clauses(Clauses, Size)];
+squeeze_clauses([], _Size) ->
+ [].
+
+squeeze_segments(#k_bin_seg{size=Sz, seg=#k_literal{val=Val}=Lit} = BinSeg, Acc, Size, 1) ->
+ BinSeg#k_bin_seg{size=Sz#k_int{val=Size + 8}, seg=Lit#k_literal{val=(Acc bsl 8) bor Val}};
+squeeze_segments(#k_bin_seg{seg=#k_literal{val=Val},next=Next}, Acc, Size, Count) ->
+ squeeze_segments(Next, (Acc bsl 8) bor Val, Size + 8, Count - 1).
+
+flat_reverse([Head | Tail], Acc) -> flat_reverse(Tail, flat_reverse_1(Head, Acc));
+flat_reverse([], Acc) -> Acc.
+
+flat_reverse_1([Head | Tail], Acc) -> flat_reverse_1(Tail, [Head | Acc]);
+flat_reverse_1([], Acc) -> Acc.
+
%% build_guard([GuardClause]) -> GuardExpr.
build_guard([]) -> fail;
diff --git a/lib/compiler/test/Makefile b/lib/compiler/test/Makefile
index 2c0767b17f..44cff40128 100644
--- a/lib/compiler/test/Makefile
+++ b/lib/compiler/test/Makefile
@@ -110,6 +110,8 @@ NO_MOD_OPT = $(NO_OPT)
NO_SSA_OPT = $(NO_OPT)
+NO_TYPE_OPT = $(NO_OPT)
+
NO_OPT_MODULES= $(NO_OPT:%=%_no_opt_SUITE)
NO_OPT_ERL_FILES= $(NO_OPT_MODULES:%=%.erl)
POST_OPT_MODULES= $(NO_OPT:%=%_post_opt_SUITE)
@@ -122,6 +124,8 @@ NO_MOD_OPT_MODULES= $(NO_MOD_OPT:%=%_no_module_opt_SUITE)
NO_MOD_OPT_ERL_FILES= $(NO_MOD_OPT_MODULES:%=%.erl)
NO_SSA_OPT_MODULES= $(NO_SSA_OPT:%=%_no_ssa_opt_SUITE)
NO_SSA_OPT_ERL_FILES= $(NO_SSA_OPT_MODULES:%=%.erl)
+NO_TYPE_OPT_MODULES= $(NO_TYPE_OPT:%=%_no_type_opt_SUITE)
+NO_TYPE_OPT_ERL_FILES= $(NO_TYPE_OPT_MODULES:%=%.erl)
ERL_FILES= $(MODULES:%=%.erl)
CORE_FILES= $(CORE_MODULES:%=%.core)
@@ -151,7 +155,7 @@ EBIN = .
# ----------------------------------------------------
make_emakefile: $(NO_OPT_ERL_FILES) $(POST_OPT_ERL_FILES) $(NO_SSA_OPT_ERL_FILES) \
- $(INLINE_ERL_FILES) $(R21_ERL_FILES) $(NO_MOD_OPT_ERL_FILES)
+ $(INLINE_ERL_FILES) $(R21_ERL_FILES) $(NO_MOD_OPT_ERL_FILES) $(NO_TYPE_OPT_ERL_FILES)
$(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) $(MODULES) \
> $(EMAKEFILE)
$(ERL_TOP)/make/make_emakefile +no_copt +no_postopt \
@@ -170,6 +174,8 @@ make_emakefile: $(NO_OPT_ERL_FILES) $(POST_OPT_ERL_FILES) $(NO_SSA_OPT_ERL_FILES
-o$(EBIN) $(NO_MOD_OPT_MODULES) >> $(EMAKEFILE)
$(ERL_TOP)/make/make_emakefile +from_core $(ERL_COMPILE_FLAGS) \
-o$(EBIN) $(CORE_MODULES) >> $(EMAKEFILE)
+ $(ERL_TOP)/make/make_emakefile +no_type_opt $(ERL_COMPILE_FLAGS) \
+ -o$(EBIN) $(NO_TYPE_OPT_MODULES) >> $(EMAKEFILE)
tests debug opt: make_emakefile
erl $(ERL_MAKE_FLAGS) -make
@@ -203,6 +209,10 @@ docs:
%_no_module_opt_SUITE.erl: %_SUITE.erl
sed -e 's;-module($(basename $<));-module($(basename $@));' $< > $@
+%_no_type_opt_SUITE.erl: %_SUITE.erl
+ sed -e 's;-module($(basename $<));-module($(basename $@));' $< > $@
+
+
# ----------------------------------------------------
# Release Target
# ----------------------------------------------------
@@ -217,7 +227,8 @@ release_tests_spec: make_emakefile
$(INSTALL_DATA) $(NO_OPT_ERL_FILES) $(POST_OPT_ERL_FILES) \
$(INLINE_ERL_FILES) $(R21_ERL_FILES) \
$(NO_MOD_OPT_ERL_FILES) \
- $(NO_SSA_OPT_ERL_FILES) "$(RELSYSDIR)"
+ $(NO_SSA_OPT_ERL_FILES) \
+ $(NO_TYPE_OPT_ERL_FILES) "$(RELSYSDIR)"
$(INSTALL_DATA) $(CORE_FILES) "$(RELSYSDIR)"
for file in $(ERL_DUMMY_FILES); do \
module=`basename $$file .erl`; \
diff --git a/lib/compiler/test/beam_except_SUITE.erl b/lib/compiler/test/beam_except_SUITE.erl
index 67947dc292..f52239f2a8 100644
--- a/lib/compiler/test/beam_except_SUITE.erl
+++ b/lib/compiler/test/beam_except_SUITE.erl
@@ -72,11 +72,25 @@ bs_get_tail(Config) ->
{function_clause,
[{?MODULE,bs_get_tail_1,[<<>>,0,0,Config],_}|_]}} =
(catch bs_get_tail_1(id(<<>>), 0, 0, Config)),
+
+ ok = bs_get_tail_2(<<"W">>, <<"X">>, <<"Z">>),
+ ok = bs_get_tail_2(<<"M">>, <<"X">>, <<"Z">>),
+ {'EXIT',
+ {function_clause,
+ [{?MODULE,do_get_bs_tail_2,[<<"A">>,<<"B">>,[],<<"C">>],_}|_]}} =
+ (catch bs_get_tail_2(<<"A">>, <<"B">>, <<"C">>)),
+
ok.
bs_get_tail_1(<<_:32, Rest/binary>>, Z1, Z2, F1) ->
{Rest,Z1,Z2,F1}.
+bs_get_tail_2(A, B, C) ->
+ do_get_bs_tail_2(A, B, [], C).
+
+do_get_bs_tail_2(<<"W">>, <<"X">>, _, <<"Z">>) -> ok;
+do_get_bs_tail_2(<<"M">>, <<"X">>, _, <<"Z">>) -> ok.
+
coverage(_) ->
File = {file,"fake.erl"},
ok = fc(a),
diff --git a/lib/compiler/test/beam_ssa_SUITE.erl b/lib/compiler/test/beam_ssa_SUITE.erl
index 11bd5cf07d..054f86731a 100644
--- a/lib/compiler/test/beam_ssa_SUITE.erl
+++ b/lib/compiler/test/beam_ssa_SUITE.erl
@@ -23,7 +23,7 @@
init_per_group/2,end_per_group/2,
calls/1,tuple_matching/1,recv/1,maps/1,
cover_ssa_dead/1,combine_sw/1,share_opt/1,
- beam_ssa_dead_crash/1]).
+ beam_ssa_dead_crash/1,stack_init/1]).
suite() -> [{ct_hooks,[ts_install_cth]}].
@@ -39,7 +39,8 @@ groups() ->
cover_ssa_dead,
combine_sw,
share_opt,
- beam_ssa_dead_crash
+ beam_ssa_dead_crash,
+ stack_init
]}].
init_per_suite(Config) ->
@@ -196,6 +197,11 @@ recv(_Config) ->
self() ! 2,
b = tricky_recv_5(),
+ %% tricky_recv_6/0 is a compile-time error.
+ tricky_recv_6(),
+
+ recv_coverage(),
+
ok.
sync_wait_mon({Pid, Ref}, Timeout) ->
@@ -321,6 +327,81 @@ tricky_recv_5() ->
_:_ -> c
end.
+%% When fixing tricky_recv_5, we introduced a compiler crash when the common
+%% exit block was ?EXCEPTION_BLOCK and floats were in the picture.
+tricky_recv_6() ->
+ RefA = make_ref(),
+ RefB = make_ref(),
+ receive
+ {RefA, Number} -> Number + 1.0;
+ {RefB, Number} -> Number + 2.0
+ after 0 ->
+ ok
+ end.
+
+recv_coverage() ->
+ self() ! 1,
+ a = recv_coverage_1(),
+ self() ! 2,
+ b = recv_coverage_1(),
+
+ self() ! 1,
+ a = recv_coverage_2(),
+ self() ! 2,
+ b = recv_coverage_2(),
+
+ ok.
+
+%% Similar to tricky_recv_5/0, but provides test coverage for the #b_switch{}
+%% terminator.
+recv_coverage_1() ->
+ receive
+ X=1 ->
+ %% Jump to common exit block through #b_switch{list=L}
+ case id(0) of
+ 0 -> a;
+ 1 -> b;
+ 2 -> c;
+ 3 -> d
+ end;
+ X=2 ->
+ %% Jump to common exit block through #b_switch{fail=F}
+ case id(42) of
+ 0 -> exit(quit);
+ 1 -> exit(quit);
+ 2 -> exit(quit);
+ 3 -> exit(quit);
+ _ -> b
+ end
+ end,
+ case X of
+ 1 -> a;
+ 2 -> b
+ end.
+
+%% Similar to recv_coverage_1/0, providing test coverage for #b_br{}.
+recv_coverage_2() ->
+ receive
+ X=1 ->
+ A = id(1),
+ %% Jump to common exit block through #b_br{succ=S}.
+ if
+ A =:= 1 -> a;
+ true -> exit(quit)
+ end;
+ X=2 ->
+ A = id(2),
+ %% Jump to common exit block through #b_br{fail=F}.
+ if
+ A =:= 1 -> exit(quit);
+ true -> a
+ end
+ end,
+ case X of
+ 1 -> a;
+ 2 -> b
+ end.
+
maps(_Config) ->
{'EXIT',{{badmatch,#{}},_}} = (catch maps_1(any)),
ok.
@@ -469,9 +550,11 @@ do_comb_sw_2(X) ->
erase(?MODULE).
share_opt(_Config) ->
- ok = do_share_opt(0).
+ ok = do_share_opt_1(0),
+ ok = do_share_opt_2(),
+ ok.
-do_share_opt(A) ->
+do_share_opt_1(A) ->
%% The compiler would be stuck in an infinite loop in beam_ssa_share.
case A of
0 -> a;
@@ -480,6 +563,26 @@ do_share_opt(A) ->
end,
receive after 1 -> ok end.
+do_share_opt_2() ->
+ ok = sopt_2({[pointtopoint], [{dstaddr,any}]}, ok),
+ ok = sopt_2({[broadcast], [{broadaddr,any}]}, ok),
+ ok = sopt_2({[], []}, ok),
+ ok.
+
+sopt_2({Flags, Opts}, ok) ->
+ Broadcast = lists:member(broadcast, Flags),
+ P2P = lists:member(pointtopoint, Flags),
+ case Opts of
+ %% The following two clauses would be combined to one, silently
+ %% discarding the guard test of the P2P variable.
+ [{broadaddr,_}|Os] when Broadcast ->
+ sopt_2({Flags, Os}, ok);
+ [{dstaddr,_}|Os] when P2P ->
+ sopt_2({Flags, Os}, ok);
+ [] ->
+ ok
+ end.
+
beam_ssa_dead_crash(_Config) ->
not_A_B = do_beam_ssa_dead_crash(id(false), id(true)),
not_A_not_B = do_beam_ssa_dead_crash(false, false),
@@ -534,6 +637,30 @@ do_beam_ssa_dead_crash(A, B) ->
end
end.
+stack_init(_Config) ->
+ 6 = stack_init(a, #{a => [1,2,3]}),
+ 0 = stack_init(missing, #{}),
+ ok.
+
+stack_init(Key, Map) ->
+ %% beam_ssa_codegen would wrongly assume that y(0) would always be
+ %% initialized by the `get_map_elements` instruction that follows, and
+ %% would set up the stack frame using an `allocate` instruction and
+ %% would not generate an `init` instruction to initialize y(0).
+ Res = case Map of
+ #{Key := Elements} ->
+ %% Elements will be assigned to y(0) if the key Key exists.
+ lists:foldl(fun(El, Acc) ->
+ Acc + El
+ end, 0, Elements);
+ #{} ->
+ %% y(0) will be left uninitialized when the key is not
+ %% present in the map.
+ 0
+ end,
+ %% y(0) would be uninitialized here if the key was not present in the map
+ %% (if the second clause was executed).
+ id(Res).
%% The identity function.
id(I) -> I.
diff --git a/lib/compiler/test/beam_type_SUITE.erl b/lib/compiler/test/beam_type_SUITE.erl
index 076a604aa4..a99dee48aa 100644
--- a/lib/compiler/test/beam_type_SUITE.erl
+++ b/lib/compiler/test/beam_type_SUITE.erl
@@ -24,7 +24,8 @@
integers/1,numbers/1,coverage/1,booleans/1,setelement/1,
cons/1,tuple/1,record_float/1,binary_float/1,float_compare/1,
arity_checks/1,elixir_binaries/1,find_best/1,
- test_size/1,cover_lists_functions/1,list_append/1,bad_binary_unit/1]).
+ test_size/1,cover_lists_functions/1,list_append/1,bad_binary_unit/1,
+ none_argument/1]).
suite() -> [{ct_hooks,[ts_install_cth]}].
@@ -49,7 +50,8 @@ groups() ->
test_size,
cover_lists_functions,
list_append,
- bad_binary_unit
+ bad_binary_unit,
+ none_argument
]}].
init_per_suite(Config) ->
@@ -518,5 +520,24 @@ bad_binary_unit(_Config) ->
false = is_binary(Bitstring),
ok.
+%% ERL-1013: The compiler would crash during the type optimization pass.
+none_argument(_Config) ->
+ Binary = id(<<3:16, 42>>),
+ error = id(case Binary of
+ <<Len:16, Body/binary>> when length(Body) == Len - 2 ->
+ %% The type for Body will be none. It means
+ %% that this clause will never match and that
+ %% uncompress/1 will never be called.
+ uncompress(Body);
+ _ ->
+ error
+ end),
+ ok.
+
+uncompress(CompressedBinary) ->
+ %% The type for CompressedBinary is none, which beam_ssa_type
+ %% did not handle properly.
+ zlib:uncompress(CompressedBinary).
+
id(I) ->
I.
diff --git a/lib/compiler/test/beam_validator_SUITE.erl b/lib/compiler/test/beam_validator_SUITE.erl
index d49d5af9c3..e4e34ec0d2 100644
--- a/lib/compiler/test/beam_validator_SUITE.erl
+++ b/lib/compiler/test/beam_validator_SUITE.erl
@@ -35,7 +35,8 @@
map_field_lists/1,cover_bin_opt/1,
val_dsetel/1,bad_tuples/1,bad_try_catch_nesting/1,
receive_stacked/1,aliased_types/1,type_conflict/1,
- infer_on_eq/1,infer_dead_value/1]).
+ infer_on_eq/1,infer_dead_value/1,infer_on_ne/1,
+ branch_to_try_handler/1,call_without_stack/1]).
-include_lib("common_test/include/ct.hrl").
@@ -65,7 +66,8 @@ groups() ->
map_field_lists,cover_bin_opt,val_dsetel,
bad_tuples,bad_try_catch_nesting,
receive_stacked,aliased_types,type_conflict,
- infer_on_eq,infer_dead_value]}].
+ infer_on_eq,infer_dead_value,infer_on_ne,
+ branch_to_try_handler,call_without_stack]}].
init_per_suite(Config) ->
test_lib:recompile(?MODULE),
@@ -148,19 +150,34 @@ stack(Config) when is_list(Config) ->
call_last(Config) when is_list(Config) ->
Errors = do_val(call_last, Config),
- [{{t,a,1},{{call_last,1,{f,8},2},9,{allocated,1}}},
+ [{{t,a,1},
+ {{call_last,1,{f,8},2},9,{allocated,1}}},
{{t,b,1},
- {{call_ext_last,2,{extfunc,lists,seq,2},2},
- 10,
- {allocated,1}}}] = Errors,
+ {{call_ext_last,2,{extfunc,lists,seq,2},2},10,{allocated,1}}},
+ {{t,baz,2},
+ {{call_ext_only,2,{extfunc,erlang,put,2}},5,{allocated,0}}},
+ {{t,biz,2},
+ {{call_only,2,{f,10}},5,{allocated,0}}}] = Errors,
+ ok.
+
+call_without_stack(Config) when is_list(Config) ->
+ Errors = do_val(call_without_stack, Config),
+ [{{t,local,2},
+ {{call,2,{f,2}},4,{allocated,none}}},
+ {{t,remote,2},
+ {{call_ext,2,{extfunc,lists,seq,2}},4,{allocated,none}}}] = Errors,
ok.
merge_undefined(Config) when is_list(Config) ->
Errors = do_val(merge_undefined, Config),
- [{{t,handle_call,2},
+ [{{t,undecided,2},
{{call_ext,2,{extfunc,debug,filter,2}},
22,
- {uninitialized_reg,{y,_}}}}] = Errors,
+ {allocated,undecided}}},
+ {{t,uninitialized,2},
+ {{call_ext,2,{extfunc,io,format,2}},
+ 17,
+ {uninitialized_reg,{y,1}}}}] = Errors,
ok.
uninit(Config) when is_list(Config) ->
@@ -263,7 +280,7 @@ freg_uninit(Config) when is_list(Config) ->
{uninitialized_reg,{fr,1}}}},
{{t,sum_2,2},
{{bif,fadd,{f,0},[{fr,0},{fr,1}],{fr,0}},
- 9,
+ 10,
{uninitialized_reg,{fr,0}}}}] = Errors,
ok.
@@ -681,11 +698,16 @@ infer_on_eq_4(T) ->
%% ERIERL-348; types were inferred for dead values, causing validation to fail.
+-record(idv, {key}).
+
infer_dead_value(Config) when is_list(Config) ->
a = idv_1({a, b, c, d, e, f, g}, {0, 0, 0, 0, 0, 0, 0}),
b = idv_1({a, b, c, d, 0, 0, 0}, {a, b, c, d, 0, 0, 0}),
c = idv_1({0, 0, 0, 0, 0, f, g}, {0, 0, 0, 0, 0, f, g}),
error = idv_1(gurka, gaffel),
+
+ ok = idv_2(id(#idv{})),
+
ok.
idv_1({_A, _B, _C, _D, _E, _F, _G},
@@ -700,6 +722,53 @@ idv_1({_A, _B, _C, _D, _E, F, G},
idv_1(_A, _B) ->
error.
+%% ERL-998; type inference for select_val (#b_switch{}) was more clever than
+%% that for is_ne_exact (#b_br{}), sometimes failing validation when the type
+%% optimization pass acted on the former and the validator got the latter.
+
+-record(ion, {state}).
+
+infer_on_ne(Config) when is_list(Config) ->
+ #ion{state = closing} = ion_1(#ion{ state = id(open) }),
+ #ion{state = closing} = ion_close(#ion{ state = open }),
+ ok.
+
+ion_1(State = #ion{state = open}) -> ion_2(State);
+ion_1(State = #ion{state = closing}) -> ion_2(State).
+
+ion_2(State = #ion{state = open}) -> ion_close(State);
+ion_2(#ion{state = closing}) -> ok.
+
+ion_close(State = #ion{}) -> State#ion{state = closing}.
+
+%% ERL-995: The first solution to ERIERL-348 was incomplete and caused
+%% validation to fail when living values depended on delayed type inference on
+%% "dead" values.
+
+idv_2(State) ->
+ Flag = (State#idv.key == undefined),
+ case id(gurka) of
+ {_} -> id([Flag]);
+ _ -> ok
+ end,
+ if
+ Flag -> idv_called_once(State);
+ true -> ok
+ end.
+
+idv_called_once(_State) -> ok.
+
+%% Direct jumps to try/catch handlers crash the emulator and must fail
+%% validation. This is provoked by OTP-15945.
+
+branch_to_try_handler(Config) ->
+ Errors = do_val(branch_to_try_handler, Config),
+ [{{branch_to_try_handler,main,1},
+ {{bif,tuple_size,{f,3},[{y,0}],{x,0}},
+ 12,
+ {illegal_branch,try_handler,3}}}] = Errors,
+ ok.
+
%%%-------------------------------------------------------------------------
transform_remove(Remove, Module) ->
diff --git a/lib/compiler/test/beam_validator_SUITE_data/branch_to_try_handler.S b/lib/compiler/test/beam_validator_SUITE_data/branch_to_try_handler.S
new file mode 100644
index 0000000000..6d43ec7b54
--- /dev/null
+++ b/lib/compiler/test/beam_validator_SUITE_data/branch_to_try_handler.S
@@ -0,0 +1,48 @@
+{module, branch_to_try_handler}. %% version = 0
+
+{exports, [{main,1}]}.
+
+{attributes, []}.
+
+{labels, 11}.
+
+{function, main, 1, 2}.
+ {label,1}.
+ {line,[{location,"t.erl",4}]}.
+ {func_info,{atom,branch_to_try_handler},{atom,main},1}.
+ {label,2}.
+ {allocate,2,1}.
+ {move,{x,0},{y,0}}.
+ {'try',{y,1},{f,3}}.
+ {move,{atom,ignored},{x,0}}.
+ {line,[{location,"t.erl",6}]}.
+ {call,1,{f,6}}.
+ {'%',{type_info,{x,0},{t_atom,[ignored]}}}.
+ {line,[{location,"t.erl",7}]}.
+ %%
+ %% Fail directly to the try handler instead of throwing an exception; this
+ %% will crash the emulator.
+ %%
+ {bif,tuple_size,{f,3},[{y,0}],{x,0}}.
+ %%
+ {test,is_eq_exact,{f,4},[{x,0},{integer,1}]}.
+ {move,{atom,error},{x,0}}.
+ {try_end,{y,1}}.
+ {deallocate,2}.
+ return.
+ {label,3}.
+ {try_case,{y,1}}.
+ {move,{atom,ok},{x,0}}.
+ {deallocate,2}.
+ return.
+ {label,4}.
+ {line,[{location,"t.erl",7}]}.
+ {badmatch,{x,0}}.
+
+{function, id, 1, 6}.
+ {label,5}.
+ {line,[{location,"t.erl",13}]}.
+ {func_info,{atom,branch_to_try_handler},{atom,id},1}.
+ {label,6}.
+ {'%',{type_info,{x,0},{t_atom,[ignored]}}}.
+ return.
diff --git a/lib/compiler/test/beam_validator_SUITE_data/call_last.S b/lib/compiler/test/beam_validator_SUITE_data/call_last.S
index 827b6c0ae6..ff81da1b57 100644
--- a/lib/compiler/test/beam_validator_SUITE_data/call_last.S
+++ b/lib/compiler/test/beam_validator_SUITE_data/call_last.S
@@ -1,6 +1,6 @@
{module, call_last}. %% version = 0
-{exports, [{a,1},{b,1},{bar,1},{foo,1},{module_info,0},{module_info,1}]}.
+{exports, [{a,1},{b,1},{bar,1},{foo,1},{baz,2},{biz,2}]}.
{attributes, []}.
@@ -53,19 +53,16 @@
{'%live',1}.
return.
-
-{function, module_info, 0, 10}.
+{function, baz, 2, 10}.
{label,9}.
- {func_info,{atom,t},{atom,module_info},0}.
+ {func_info,{atom,t},{atom,baz},2}.
{label,10}.
- {move,{atom,t},{x,0}}.
- {call_ext_only,1,{extfunc,erlang,get_module_info,1}}.
-
+ {allocate,0,2}.
+ {call_ext_only,2,{extfunc,erlang,put,2}}.
-{function, module_info, 1, 12}.
+{function, biz, 2, 12}.
{label,11}.
- {func_info,{atom,t},{atom,module_info},1}.
+ {func_info,{atom,t},{atom,biz},2}.
{label,12}.
- {move,{x,0},{x,1}}.
- {move,{atom,t},{x,0}}.
- {call_ext_only,2,{extfunc,erlang,get_module_info,2}}.
+ {allocate,0,2}.
+ {call_only,2,{f,10}}.
diff --git a/lib/compiler/test/beam_validator_SUITE_data/call_without_stack.S b/lib/compiler/test/beam_validator_SUITE_data/call_without_stack.S
new file mode 100644
index 0000000000..9ccbc163e3
--- /dev/null
+++ b/lib/compiler/test/beam_validator_SUITE_data/call_without_stack.S
@@ -0,0 +1,21 @@
+{module, call_without_stack}. %% version = 0
+
+{exports, [{remote,2},{local,2}]}.
+
+{attributes, []}.
+
+{labels, 9}.
+
+{function, remote, 2, 2}.
+ {label,1}.
+ {func_info,{atom,t},{atom,remote},2}.
+ {label,2}.
+ {call_ext,2,{extfunc,lists,seq,2}}.
+ if_end.
+
+{function, local, 2, 4}.
+ {label,3}.
+ {func_info,{atom,t},{atom,local},2}.
+ {label,4}.
+ {call,2,{f,2}}.
+ if_end.
diff --git a/lib/compiler/test/beam_validator_SUITE_data/freg_uninit.S b/lib/compiler/test/beam_validator_SUITE_data/freg_uninit.S
index 71e833446a..2d4cbc9388 100644
--- a/lib/compiler/test/beam_validator_SUITE_data/freg_uninit.S
+++ b/lib/compiler/test/beam_validator_SUITE_data/freg_uninit.S
@@ -21,12 +21,14 @@
{label,3}.
{func_info,{atom,t},{atom,sum_2},2}.
{label,4}.
+ {allocate,0,2}.
{fconv,{x,0},{fr,0}}.
{fconv,{x,1},{fr,1}}.
fclearerror.
{fcheckerror,{f,0}}.
{call,2,{f,6}}.
{bif,fadd,{f,0},[{fr,0},{fr,1}],{fr,0}}.
+ {deallocate,0}.
return.
{function, foo, 2, 6}.
diff --git a/lib/compiler/test/beam_validator_SUITE_data/merge_undefined.S b/lib/compiler/test/beam_validator_SUITE_data/merge_undefined.S
index aa344807e4..3035471f04 100644
--- a/lib/compiler/test/beam_validator_SUITE_data/merge_undefined.S
+++ b/lib/compiler/test/beam_validator_SUITE_data/merge_undefined.S
@@ -1,15 +1,14 @@
{module, merge_undefined}. %% version = 0
-{exports, [{bar,2},{foo,1},{handle_call,2},{module_info,0},{module_info,1}]}.
+{exports, [{uninitialized,2},{undecided,2}]}.
{attributes, []}.
{labels, 15}.
-
-{function, handle_call, 2, 2}.
+{function, uninitialized, 2, 2}.
{label,1}.
- {func_info,{atom,t},{atom,handle_call},2}.
+ {func_info,{atom,t},{atom,uninitialized},2}.
{label,2}.
{test,is_atom,{f,1},[{x,0}]}.
{select_val,{x,0},{f,1},{list,[{atom,gurka},{f,3},{atom,delete},{f,4}]}}.
@@ -21,7 +20,7 @@
{move,{atom,nisse},{x,0}}.
{call_ext,1,{extfunc,erlang,exit,1}}.
{label,4}.
- {allocate_heap,1,6,2}.
+ {allocate_heap,2,6,2}.
{move,{x,1},{y,0}}.
{put_list,{integer,112},nil,{x,0}}.
{put_list,{integer,126},{x,0},{x,0}}.
@@ -51,37 +50,57 @@
{call_ext,1,{extfunc,erlang,exit,1}}.
{label,6}.
{move,{y,0},{x,0}}.
- {call_last,1,{f,8},1}.
+ {call_last,1,{f,14},1}.
-
-{function, foo, 1, 8}.
+{function, undecided, 2, 8}.
{label,7}.
- {func_info,{atom,t},{atom,foo},1}.
+ {func_info,{atom,t},{atom,undecided},2}.
{label,8}.
- {move,{atom,ok},{x,0}}.
- return.
-
-
-{function, bar, 2, 10}.
+ {test,is_atom,{f,7},[{x,0}]}.
+ {select_val,{x,0},{f,1},{list,[{atom,gurka},{f,9},{atom,delete},{f,10}]}}.
{label,9}.
- {func_info,{atom,t},{atom,bar},2}.
+ {allocate_heap,2,6,2}.
+ {test,is_eq_exact,{f,11},[{x,0},{atom,ok}]}.
+ %% This is unreachable since {x,0} is known not to be 'ok'. We should not
+ %% fail with "uninitialized y registers" on erlang:exit/1
+ {move,{atom,nisse},{x,0}}.
+ {call_ext,1,{extfunc,erlang,exit,1}}.
{label,10}.
- {move,{atom,ok},{x,0}}.
- return.
-
-
-{function, module_info, 0, 12}.
+ {allocate_heap,1,6,2}.
+ {move,{x,1},{y,0}}.
+ {put_list,{integer,112},nil,{x,0}}.
+ {put_list,{integer,126},{x,0},{x,0}}.
+ {put_list,{y,0},nil,{x,1}}.
+ {'%live',2}.
+ {call_ext,2,{extfunc,io,format,2}}.
+ {test,is_ne_exact,{f,12},[{x,0},{atom,ok}]}.
{label,11}.
- {func_info,{atom,t},{atom,module_info},0}.
+ %% The number of allocated Y registers are in conflict here.
+ {move,{atom,logReader},{x,1}}.
+ {move,{atom,console},{x,0}}.
+ {call_ext,2,{extfunc,debug,filter,2}}.
+ {test_heap,14,1}.
+ {put_list,{atom,logReader},nil,{x,1}}.
+ {put_list,{atom,console},{x,1},{x,1}}.
+ {put_tuple,3,{x,2}}.
+ {put,{atom,debug}}.
+ {put,{atom,filter}}.
+ {put,{x,1}}.
+ {put_tuple,2,{x,1}}.
+ {put,{x,2}}.
+ {put,{x,0}}.
+ {put_tuple,2,{x,0}}.
+ {put,{atom,badmatch}}.
+ {put,{x,1}}.
+ {'%live',1}.
+ {call_ext,1,{extfunc,erlang,exit,1}}.
{label,12}.
- {move,{atom,t},{x,0}}.
- {call_ext_only,1,{extfunc,erlang,get_module_info,1}}.
-
+ {move,{y,0},{x,0}}.
+ {call_last,1,{f,8},1}.
-{function, module_info, 1, 14}.
+{function, foo, 1, 14}.
{label,13}.
- {func_info,{atom,t},{atom,module_info},1}.
+ {func_info,{atom,t},{atom,foo},1}.
{label,14}.
- {move,{x,0},{x,1}}.
- {move,{atom,t},{x,0}}.
- {call_ext_only,2,{extfunc,erlang,get_module_info,2}}.
+ {move,{atom,ok},{x,0}}.
+ return.
diff --git a/lib/compiler/test/bs_match_SUITE.erl b/lib/compiler/test/bs_match_SUITE.erl
index 145a50f4ad..0dc1d64eeb 100644
--- a/lib/compiler/test/bs_match_SUITE.erl
+++ b/lib/compiler/test/bs_match_SUITE.erl
@@ -24,7 +24,7 @@
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2,
init_per_testcase/2,end_per_testcase/2,
- verify_highest_opcode/1,
+ verify_highest_opcode/1, expand_and_squeeze/1,
size_shadow/1,int_float/1,otp_5269/1,null_fields/1,wiger/1,
bin_tail/1,save_restore/1,
partitioned_bs_match/1,function_clause/1,
@@ -64,7 +64,7 @@ groups() ->
[{p,[],
[verify_highest_opcode,
size_shadow,int_float,otp_5269,null_fields,wiger,
- bin_tail,save_restore,
+ bin_tail,save_restore,expand_and_squeeze,
partitioned_bs_match,function_clause,unit,
shared_sub_bins,bin_and_float,dec_subidentifiers,
skip_optional_tag,decode_integer,wfbm,degenerated_match,bs_sum,
@@ -2021,3 +2021,161 @@ do_exceptions_after_match_failure(Other) ->
ok.
id(I) -> I.
+
+expand_and_squeeze(Config) when is_list(Config) ->
+ %% UTF8 literals are expanded and then squeezed into integer16
+ [
+ {test,bs_get_integer2,_,_,[_,{integer,16}|_],_}
+ | _
+ ] = binary_match_to_asm([
+ ?Q("<<$á/utf8,_/binary>>"),
+ ?Q("<<$é/utf8,_/binary>>")
+ ]),
+
+ %% Sized integers are expanded and then squeezed into integer16
+ [
+ {test,bs_get_integer2,_,_,[_,{integer,16}|_],_}
+ | _
+ ] = binary_match_to_asm([
+ ?Q("<<0:32,_/binary>>"),
+ ?Q("<<\"bbbb\",_/binary>>")
+ ]),
+
+ %% Groups of 8 bits are squeezed into integer16
+ [
+ {test,bs_get_integer2,_,_,[_,{integer,16}|_],_}
+ | _
+ ] = binary_match_to_asm([
+ ?Q("<<\"aaaa\",_/binary>>"),
+ ?Q("<<\"bbbb\",_/binary>>")
+ ]),
+
+ %% Groups of 8 bits with empty binary are also squeezed
+ [
+ {test,bs_get_integer2,_,_,[_,{integer,16}|_],_}
+ | _
+ ] = binary_match_to_asm([
+ ?Q("<<\"aaaa\",_/binary>>"),
+ ?Q("<<\"bbbb\",_/binary>>"),
+ ?Q("<<>>")
+ ]),
+
+ %% Groups of 8 bits with float lookup are not squeezed
+ [
+ {test,bs_get_integer2,_,_,[_,{integer,8}|_],_}
+ | _
+ ] = binary_match_to_asm([
+ ?Q("<<\"aaaa\",_/binary>>"),
+ ?Q("<<\"bbbb\",_/binary>>"),
+ ?Q("<<_/float>>")
+ ]),
+
+ %% Groups of diverse bits go with minimum possible
+ [
+ {test,bs_get_integer2,_,_,[_,{integer,8}|_],_}
+ | _
+ ] = binary_match_to_asm([
+ ?Q("<<\"aa\",_/binary>>"),
+ ?Q("<<\"bb\",_/binary>>"),
+ ?Q("<<\"c\",_/binary>>")
+ ]),
+
+ %% Groups of diverse bits go with minimum possible but are recursive...
+ [
+ {test,bs_get_integer2,_,_,[_,{integer,8}|_],_}
+ | RestDiverse
+ ] = binary_match_to_asm([
+ ?Q("<<\"aaa\",_/binary>>"),
+ ?Q("<<\"abb\",_/binary>>"),
+ ?Q("<<\"c\",_/binary>>")
+ ]),
+
+ %% so we still perform a 16 bits lookup for the remaining
+ true = lists:any(fun({test,bs_get_integer2,_,_,[_,{integer,16}|_],_}) -> true;
+ (_) -> false end, RestDiverse),
+
+ %% Large match is kept as is if there is a sized match later
+ [
+ {test,bs_get_integer2,_,_,[_,{integer,64}|_],_}
+ | _
+ ] = binary_match_to_asm([
+ ?Q("<<255,255,255,255,255,255,255,255>>"),
+ ?Q("<<_:64>>")
+ ]),
+
+ %% Large match is kept as is with large matches before and after
+ [
+ {test,bs_get_integer2,_,_,[_,{integer,32}|_],_}
+ | _
+ ] = binary_match_to_asm([
+ ?Q("<<A:32,_:A>>"),
+ ?Q("<<0:32>>"),
+ ?Q("<<_:32>>")
+ ]),
+
+ %% Large match is kept as is with large matches before and after
+ [
+ {test,bs_get_integer2,_,_,[_,{integer,32}|_],_}
+ | _
+ ] = binary_match_to_asm([
+ ?Q("<<A:32,_:A>>"),
+ ?Q("<<0,0,0,0>>"),
+ ?Q("<<_:32>>")
+ ]),
+
+ %% Large match is kept as is with smaller but still large matches before and after
+ [
+ {test,bs_get_integer2,_,_,[_,{integer,32}|_],_}
+ | _
+ ] = binary_match_to_asm([
+ ?Q("<<A:32, _:A>>"),
+ ?Q("<<0:64>>"),
+ ?Q("<<_:32>>")
+ ]),
+
+ %% There is no squeezing for groups with more than 16 matches
+ [
+ {test,bs_get_integer2,_,_,[_,{integer,8}|_],_}
+ | _
+ ] = binary_match_to_asm([
+ ?Q("<<\"aa\", _/binary>>"),
+ ?Q("<<\"bb\", _/binary>>"),
+ ?Q("<<\"cc\", _/binary>>"),
+ ?Q("<<\"dd\", _/binary>>"),
+ ?Q("<<\"ee\", _/binary>>"),
+ ?Q("<<\"ff\", _/binary>>"),
+ ?Q("<<\"gg\", _/binary>>"),
+ ?Q("<<\"hh\", _/binary>>"),
+ ?Q("<<\"ii\", _/binary>>"),
+ ?Q("<<\"jj\", _/binary>>"),
+ ?Q("<<\"kk\", _/binary>>"),
+ ?Q("<<\"ll\", _/binary>>"),
+ ?Q("<<\"mm\", _/binary>>"),
+ ?Q("<<\"nn\", _/binary>>"),
+ ?Q("<<\"oo\", _/binary>>"),
+ ?Q("<<\"pp\", _/binary>>")
+ ]),
+
+ ok.
+
+binary_match_to_asm(Matches) ->
+ Clauses = [
+ begin
+ Ann = element(2, Match),
+ {clause,Ann,[Match],[],[{integer,Ann,Return}]}
+ end || {Match,Return} <- lists:zip(Matches, lists:seq(1, length(Matches)))
+ ],
+
+ Module = [
+ {attribute,1,module,match_to_asm},
+ {attribute,2,export,[{example,1}]},
+ {function,3,example,1,Clauses}
+ ],
+
+ {ok,match_to_asm,{match_to_asm,_Exports,_Attrs,Funs,_},_} =
+ compile:forms(Module, [return, to_asm]),
+
+ [{function,example,1,2,AllInstructions}|_] = Funs,
+ [{label,_},{line,_},{func_info,_,_,_},{label,_},{'%',_},
+ {test,bs_start_match3,_,_,_,_},{bs_get_position,_,_,_}|Instructions] = AllInstructions,
+ Instructions.
diff --git a/lib/compiler/test/misc_SUITE.erl b/lib/compiler/test/misc_SUITE.erl
index eb60dc049d..20fadc4fdb 100644
--- a/lib/compiler/test/misc_SUITE.erl
+++ b/lib/compiler/test/misc_SUITE.erl
@@ -274,14 +274,34 @@ silly_coverage(Config) when is_list(Config) ->
bad_ssa_lint_input() ->
{b_module,#{},t,
- [{foobar,1},{module_info,0},{module_info,1}],
+ [{a,1},{b,1},{c,1},{module_info,0},{module_info,1}],
[],
[{b_function,
- #{func_info => {t,foobar,1},location => {"t.erl",4}},
+ #{func_info => {t,a,1},location => {"t.erl",4}},
[{b_var,0}],
#{0 => {b_blk,#{},[],{b_ret,#{},{b_var,'@undefined_var'}}}},
3},
{b_function,
+ #{func_info => {t,b,1},location => {"t.erl",5}},
+ [{b_var,0}],
+ #{0 =>
+ {b_blk,#{},
+ [{b_set,#{},{b_var,'@first_var'},first_op,[]},
+ {b_set,#{},{b_var,'@second_var'},second_op,[]},
+ {b_set,#{},{b_var,'@ret'},succeeded,[{b_var,'@first_var'}]}],
+ {b_ret,#{},{b_var,'@ret'}}}},
+ 3},
+ {b_function,
+ #{func_info => {t,c,1},location => {"t.erl",6}},
+ [{b_var,0}],
+ #{0 =>
+ {b_blk,#{},
+ [{b_set,#{},{b_var,'@first_var'},first_op,[]},
+ {b_set,#{},{b_var,'@ret'},succeeded,[{b_var,'@first_var'}]},
+ {b_set,#{},{b_var,'@second_var'},second_op,[]}],
+ {b_ret,#{},{b_var,'@ret'}}}},
+ 3},
+ {b_function,
#{func_info => {t,module_info,0}},
[],
#{0 =>
diff --git a/lib/compiler/test/receive_SUITE.erl b/lib/compiler/test/receive_SUITE.erl
index 752491f0f8..8cd864c59e 100644
--- a/lib/compiler/test/receive_SUITE.erl
+++ b/lib/compiler/test/receive_SUITE.erl
@@ -431,6 +431,20 @@ elusive_common_exit(_Config) ->
self() ! {1, a},
self() ! {2, b},
{[z], [{2,b},{1,a}]} = elusive_loop([x,y,z], 2, []),
+
+ CodeServer = whereis(code_server),
+ Self = self(),
+ Self ! {Self, abc},
+ Self ! {CodeServer, []},
+ Self ! {Self, other},
+ try elusive2([]) of
+ Unexpected ->
+ ct:fail("Expected an exception; got ~p\n", [Unexpected])
+ catch
+ throw:[other, CodeServer, Self] ->
+ ok
+ end,
+
ok.
elusive_loop(List, 0, Results) ->
@@ -449,4 +463,25 @@ elusive_loop(List, ToReceive, Results) ->
%% that it would not insert all necessary copy instructions.
elusive_loop(RemList, ToReceive-1, [Result | Results]).
+
+elusive2(Acc) ->
+ receive
+ {Pid, abc} ->
+ ok;
+ {Pid, []} ->
+ ok;
+ {Pid, Res} ->
+ %% beam_ssa_pre_codegen:find_loop_exit/2 attempts to find
+ %% the first block of the common code after the receive
+ %% statement. It used to only look at the two last clauses
+ %% of the receive. In this function, the last two clauses
+ %% don't have any common block, so it would be assumed
+ %% that there was no common block for any of the
+ %% clauses. That would mean that copy instructions would
+ %% not be inserted as needed.
+ throw([Res | Acc])
+ end,
+ %% Common code.
+ elusive2([Pid | Acc]).
+
id(I) -> I.
diff --git a/lib/compiler/test/test_lib.erl b/lib/compiler/test/test_lib.erl
index a468482acb..4b68e663cd 100644
--- a/lib/compiler/test/test_lib.erl
+++ b/lib/compiler/test/test_lib.erl
@@ -99,7 +99,8 @@ get_data_dir(Config) ->
Data2 = re:replace(Data1, "_post_opt_SUITE", "_SUITE", Opts),
Data3 = re:replace(Data2, "_inline_SUITE", "_SUITE", Opts),
Data4 = re:replace(Data3, "_r21_SUITE", "_SUITE", Opts),
- Data = re:replace(Data4, "_no_module_opt_SUITE", "_SUITE", Opts),
+ Data5 = re:replace(Data4, "_no_module_opt_SUITE", "_SUITE", Opts),
+ Data = re:replace(Data5, "_no_type_opt_SUITE", "_SUITE", Opts),
re:replace(Data, "_no_ssa_opt_SUITE", "_SUITE", Opts).
is_cloned_mod(Mod) ->
diff --git a/lib/compiler/vsn.mk b/lib/compiler/vsn.mk
index c76777b191..5434fdf90a 100644
--- a/lib/compiler/vsn.mk
+++ b/lib/compiler/vsn.mk
@@ -1 +1 @@
-COMPILER_VSN = 7.4.3
+COMPILER_VSN = 7.4.5
diff --git a/lib/crypto/c_src/atoms.c b/lib/crypto/c_src/atoms.c
index bbeb329fa2..fc983ec03b 100644
--- a/lib/crypto/c_src/atoms.c
+++ b/lib/crypto/c_src/atoms.c
@@ -89,6 +89,8 @@ ERL_NIF_TERM atom_ecdsa;
#ifdef HAVE_ED_CURVE_DH
ERL_NIF_TERM atom_x25519;
ERL_NIF_TERM atom_x448;
+ERL_NIF_TERM atom_ed25519;
+ERL_NIF_TERM atom_ed448;
#endif
ERL_NIF_TERM atom_eddsa;
@@ -219,6 +221,8 @@ int init_atoms(ErlNifEnv *env, const ERL_NIF_TERM fips_mode, const ERL_NIF_TERM
#ifdef HAVE_ED_CURVE_DH
atom_x25519 = enif_make_atom(env,"x25519");
atom_x448 = enif_make_atom(env,"x448");
+ atom_ed25519 = enif_make_atom(env,"ed25519");
+ atom_ed448 = enif_make_atom(env,"ed448");
#endif
atom_eddsa = enif_make_atom(env,"eddsa");
#ifdef HAVE_EDDSA
diff --git a/lib/crypto/c_src/atoms.h b/lib/crypto/c_src/atoms.h
index 0e2f1a0022..956aab93eb 100644
--- a/lib/crypto/c_src/atoms.h
+++ b/lib/crypto/c_src/atoms.h
@@ -93,6 +93,8 @@ extern ERL_NIF_TERM atom_ecdsa;
#ifdef HAVE_ED_CURVE_DH
extern ERL_NIF_TERM atom_x25519;
extern ERL_NIF_TERM atom_x448;
+extern ERL_NIF_TERM atom_ed25519;
+extern ERL_NIF_TERM atom_ed448;
#endif
extern ERL_NIF_TERM atom_eddsa;
diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c
index 802818541b..2ea7b7c329 100644
--- a/lib/crypto/c_src/crypto.c
+++ b/lib/crypto/c_src/crypto.c
@@ -95,7 +95,7 @@ static ErlNifFunc nif_funcs[] = {
{"dh_generate_key_nif", 4, dh_generate_key_nif, 0},
{"dh_compute_key_nif", 3, dh_compute_key_nif, 0},
{"evp_compute_key_nif", 3, evp_compute_key_nif, 0},
- {"evp_generate_key_nif", 1, evp_generate_key_nif, 0},
+ {"evp_generate_key_nif", 2, evp_generate_key_nif, 0},
{"privkey_to_pubkey_nif", 2, privkey_to_pubkey_nif, 0},
{"srp_value_B_nif", 5, srp_value_B_nif, 0},
{"srp_user_secret_nif", 7, srp_user_secret_nif, 0},
diff --git a/lib/crypto/c_src/evp.c b/lib/crypto/c_src/evp.c
index 3bf66bfffe..fb6495a640 100644
--- a/lib/crypto/c_src/evp.c
+++ b/lib/crypto/c_src/evp.c
@@ -106,25 +106,34 @@ ERL_NIF_TERM evp_generate_key_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM a
EVP_PKEY_CTX *ctx = NULL;
EVP_PKEY *pkey = NULL;
ERL_NIF_TERM ret_pub, ret_prv, ret;
+ ErlNifBinary prv_key;
size_t key_len;
unsigned char *out_pub = NULL, *out_priv = NULL;
- ASSERT(argc == 1);
-
if (argv[0] == atom_x25519)
type = EVP_PKEY_X25519;
else if (argv[0] == atom_x448)
type = EVP_PKEY_X448;
+ else if (argv[0] == atom_ed25519)
+ type = EVP_PKEY_ED25519;
+ else if (argv[0] == atom_ed448)
+ type = EVP_PKEY_ED448;
else
goto bad_arg;
- if ((ctx = EVP_PKEY_CTX_new_id(type, NULL)) == NULL)
- goto bad_arg;
-
- if (EVP_PKEY_keygen_init(ctx) != 1)
- goto err;
- if (EVP_PKEY_keygen(ctx, &pkey) != 1)
- goto err;
+ if (argv[1] == atom_undefined) {
+ if ((ctx = EVP_PKEY_CTX_new_id(type, NULL)) == NULL)
+ goto bad_arg;
+ if (EVP_PKEY_keygen_init(ctx) != 1)
+ goto err;
+ if (EVP_PKEY_keygen(ctx, &pkey) != 1)
+ goto err;
+ } else {
+ if (!enif_inspect_binary(env, argv[1], &prv_key))
+ goto bad_arg;
+ if ((pkey = EVP_PKEY_new_raw_private_key(type, NULL, prv_key.data, prv_key.size)) == NULL)
+ goto bad_arg;
+ }
if (EVP_PKEY_get_raw_public_key(pkey, NULL, &key_len) != 1)
goto err;
diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml
index 3973cf3f9f..5c1f6af016 100644
--- a/lib/crypto/doc/src/crypto.xml
+++ b/lib/crypto/doc/src/crypto.xml
@@ -796,7 +796,7 @@
</func>
<func>
- <name name="mac" arity="3" since="OTP @OTP-13872@"/>
+ <name name="mac" arity="3" since="OTP 22.1"/>
<fsummary></fsummary>
<desc>
<p>Short for <seealso marker="#mac-4">mac(Type, undefined, Key, Data)</seealso>.
@@ -805,7 +805,7 @@
</func>
<func>
- <name name="mac" arity="4" since="OTP @OTP-13872@"/>
+ <name name="mac" arity="4" since="OTP 22.1"/>
<fsummary></fsummary>
<desc>
<p>Computes a MAC (Message Authentication Code) of type <c>Type</c> from <c>Data</c>.
@@ -846,7 +846,7 @@
</func>
<func>
- <name name="macN" arity="4" since="OTP @OTP-13872@"/>
+ <name name="macN" arity="4" since="OTP 22.1"/>
<fsummary></fsummary>
<desc>
<p>Short for <seealso marker="#macN-5">macN(Type, undefined, Key, Data, MacLength)</seealso>.
@@ -855,7 +855,7 @@
</func>
<func>
- <name name="macN" arity="5" since="OTP @OTP-13872@"/>
+ <name name="macN" arity="5" since="OTP 22.1"/>
<fsummary></fsummary>
<desc>
<p>Computes a MAC (Message Authentication Code)
@@ -874,7 +874,7 @@
</func>
<func>
- <name name="mac_init" arity="2" since="OTP @OTP-13872@"/>
+ <name name="mac_init" arity="2" since="OTP 22.1"/>
<fsummary></fsummary>
<desc>
<p>Short for <seealso marker="#mac_init-3">mac_init(Type, undefined, Key)</seealso>.
@@ -883,7 +883,7 @@
</func>
<func>
- <name name="mac_init" arity="3" since="OTP @OTP-13872@"/>
+ <name name="mac_init" arity="3" since="OTP 22.1"/>
<fsummary></fsummary>
<desc>
<p>Initializes the context for streaming MAC operations.
@@ -929,7 +929,7 @@
</func>
<func>
- <name name="mac_update" arity="2" since="OTP @OTP-13872@"/>
+ <name name="mac_update" arity="2" since="OTP 22.1"/>
<fsummary></fsummary>
<desc>
<p>Updates the MAC represented by <c>State0</c> using the given <c>Data</c> which
@@ -945,7 +945,7 @@
</func>
<func>
- <name name="mac_final" arity="1" since="OTP @OTP-13872@"/>
+ <name name="mac_final" arity="1" since="OTP 22.1"/>
<fsummary></fsummary>
<desc>
<p>Finalizes the MAC operation referenced by <c>State</c>. The <c>Mac</c> result will have
@@ -960,7 +960,7 @@
</func>
<func>
- <name name="mac_finalN" arity="2" since="OTP @OTP-13872@"/>
+ <name name="mac_finalN" arity="2" since="OTP 22.1"/>
<fsummary></fsummary>
<desc>
<p>Finalizes the MAC operation referenced by <c>State</c>.
diff --git a/lib/crypto/doc/src/notes.xml b/lib/crypto/doc/src/notes.xml
index 5f47981855..b35dddf4ff 100644
--- a/lib/crypto/doc/src/notes.xml
+++ b/lib/crypto/doc/src/notes.xml
@@ -31,6 +31,64 @@
</header>
<p>This document describes the changes made to the Crypto application.</p>
+<section><title>Crypto 4.6</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The implementation of <c>crypto_one_time/4</c> is
+ adjusted to match the type specification. The spec and
+ the black-box behaviour of the function are unchanged.</p>
+ <p>
+ Some details: Both the spec and the implementation were
+ correct seen separately. But with both of them combined
+ simultaneously with <c>crypto_one_time/5</c> which was
+ called by the implementation of <c>crypto_one_time/4</c>,
+ an (obvious) error was detected by a Dialyzer with more
+ thorough checking than usual.</p>
+ <p>
+ Own Id: OTP-15884 Aux Id: ERL-974 </p>
+ </item>
+ <item>
+ <p>
+ When using crypto with FIPS mode enabled, the digests
+ were not correctly handled.</p>
+ <p>
+ Own Id: OTP-15911</p>
+ </item>
+ <item>
+ <p>
+ A memory leak in error handling code in
+ <c>ng_crypto_init_nif</c> is fixed.</p>
+ <p>
+ Own Id: OTP-15924</p>
+ </item>
+ <item>
+ <p>
+ Fixed the broken static build of the crypto nifs</p>
+ <p>
+ Own Id: OTP-15928 Aux Id: PR-2296 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ The Message Authentication Codes (MAC) CMAC, HMAC and
+ Poly1305 are unified into common functions in the New
+ Crypto API. See the manual for CRYPTO.</p>
+ <p>
+ Own Id: OTP-13872</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Crypto 4.5.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl
index 965697578d..1c32895dbf 100644
--- a/lib/crypto/src/crypto.erl
+++ b/lib/crypto/src/crypto.erl
@@ -1761,21 +1761,21 @@ pkey_crypt_nif(_Algorithm, _In, _Key, _Options, _IsPrivate, _IsEncrypt) -> ?nif_
-spec generate_key(Type, Params)
-> {PublicKey, PrivKeyOut}
- when Type :: dh | ecdh | rsa | srp,
+ when Type :: dh | ecdh | eddsa | rsa | srp,
PublicKey :: dh_public() | ecdh_public() | rsa_public() | srp_public(),
PrivKeyOut :: dh_private() | ecdh_private() | rsa_private() | {srp_public(),srp_private()},
- Params :: dh_params() | ecdh_params() | rsa_params() | srp_gen_params()
+ Params :: dh_params() | ecdh_params() | eddsa_params() | rsa_params() | srp_gen_params()
.
generate_key(Type, Params) ->
generate_key(Type, Params, undefined).
-spec generate_key(Type, Params, PrivKeyIn)
-> {PublicKey, PrivKeyOut}
- when Type :: dh | ecdh | rsa | srp,
+ when Type :: dh | ecdh | eddsa | rsa | srp,
PublicKey :: dh_public() | ecdh_public() | rsa_public() | srp_public(),
PrivKeyIn :: undefined | dh_private() | ecdh_private() | rsa_private() | {srp_public(),srp_private()},
PrivKeyOut :: dh_private() | ecdh_private() | rsa_private() | {srp_public(),srp_private()},
- Params :: dh_params() | ecdh_params() | rsa_params() | srp_comp_params()
+ Params :: dh_params() | ecdh_params() | eddsa_params() | rsa_params() | srp_comp_params()
.
generate_key(dh, DHParameters0, PrivateKey) ->
@@ -1813,15 +1813,17 @@ generate_key(rsa, {ModulusSize, PublicExponent}, undefined) ->
{lists:sublist(Private, 2), Private}
end;
-
-generate_key(ecdh, Curve, undefined) when Curve == x448 ;
- Curve == x25519 ->
- evp_generate_key_nif(Curve);
+generate_key(ecdh, Curve, PrivKey) when Curve == x448 ;
+ Curve == x25519 ->
+ evp_generate_key_nif(Curve, ensure_int_as_bin(PrivKey));
generate_key(ecdh, Curve, PrivKey) ->
- ec_key_generate(nif_curve_params(Curve), ensure_int_as_bin(PrivKey)).
+ ec_key_generate(nif_curve_params(Curve), ensure_int_as_bin(PrivKey));
+generate_key(eddsa, Curve, PrivKey) when Curve == ed448 ;
+ Curve == ed25519 ->
+ evp_generate_key_nif(Curve, ensure_int_as_bin(PrivKey)).
-evp_generate_key_nif(_Curve) -> ?nif_stub.
+evp_generate_key_nif(_Curve, _PrivKey) -> ?nif_stub.
-spec compute_key(Type, OthersPublicKey, MyPrivateKey, Params)
diff --git a/lib/crypto/test/crypto_SUITE.erl b/lib/crypto/test/crypto_SUITE.erl
index 0da70d5592..614d14029b 100644
--- a/lib/crypto/test/crypto_SUITE.erl
+++ b/lib/crypto/test/crypto_SUITE.erl
@@ -202,11 +202,13 @@ groups() ->
{ecdsa, [], [sign_verify
%% Does not work yet: ,public_encrypt, private_encrypt
]},
- {ed25519, [], [sign_verify
+ {ed25519, [], [sign_verify,
%% Does not work yet: ,public_encrypt, private_encrypt
+ generate
]},
- {ed448, [], [sign_verify
+ {ed448, [], [sign_verify,
%% Does not work yet: ,public_encrypt, private_encrypt
+ generate
]},
{dh, [], [generate_compute, compute_bug]},
{ecdh, [], [use_all_elliptic_curves, compute, generate]},
@@ -1429,7 +1431,7 @@ do_compute({ecdh = Type, Pub, Priv, Curve, SharedSecret}) ->
ct:fail({{crypto, compute_key, [Type, Pub, Priv, Curve]}, {expected, SharedSecret}, {got, Other}})
end.
-do_generate({ecdh = Type, Curve, Priv, Pub}) ->
+do_generate({Type, Curve, Priv, Pub}) when Type == ecdh ; Type == eddsa ->
case crypto:generate_key(Type, Curve, Priv) of
{Pub, _} ->
ok;
@@ -1854,7 +1856,10 @@ group_config(ecdsa = Type, Config) ->
[{sign_verify, SignVerify}, {pub_priv_encrypt, PubPrivEnc} | Config];
group_config(Type, Config) when Type == ed25519 ; Type == ed448 ->
TestVectors = eddsa(Type),
- [{sign_verify,TestVectors} | Config];
+ Generate = lists:map(fun({Curve, _Hash, Priv, Pub, _Msg, _Signature}) ->
+ {eddsa, Curve, Priv, Pub}
+ end, TestVectors),
+ [{sign_verify,TestVectors}, {generate, Generate} | Config];
group_config(srp, Config) ->
GenerateCompute = [srp3(), srp6(), srp6a(), srp6a_smaller_prime()],
[{generate_compute, GenerateCompute} | Config];
@@ -3351,7 +3356,7 @@ srp(ClientPrivate, Generator, Prime, Version, Verifier, ServerPublic, ServerPriv
eddsa(ed25519) ->
%% https://tools.ietf.org/html/rfc8032#section-7.1
- %% {ALGORITHM, (SHA)}, SECRET KEY, PUBLIC KEY, MESSAGE, SIGNATURE}
+ %% {ALGORITHM, (SHA), SECRET KEY, PUBLIC KEY, MESSAGE, SIGNATURE}
[
%% TEST 1
{ed25519, undefined,
@@ -3917,12 +3922,31 @@ ecc() ->
"782C37E372BA4520AA62E0FED121D49EF3B543660CFD05FD")},
{ecdh,secp192r1,4,
hexstr2point("35433907297CC378B0015703374729D7A4FE46647084E4BA",
- "A2649984F2135C301EA3ACB0776CD4F125389B311DB3BE32")}],
+ "A2649984F2135C301EA3ACB0776CD4F125389B311DB3BE32")},
+ %% RFC 7748, 6.2
+ {ecdh, x448,
+ hexstr2bin("9a8f4925d1519f5775cf46b04b5800d4ee9ee8bae8bc5565d498c28d"
+ "d9c9baf574a9419744897391006382a6f127ab1d9ac2d8c0a598726b"),
+ hexstr2bin("9b08f7cc31b7e3e67d22d5aea121074a273bd2b83de09c63faa73d2c"
+ "22c5d9bbc836647241d953d40c5b12da88120d53177f80e532c41fa0")},
+ {ecdh, x448,
+ hexstr2bin("1c306a7ac2a0e2e0990b294470cba339e6453772b075811d8fad0d1d"
+ "6927c120bb5ee8972b0d3e21374c9c921b09d1b0366f10b65173992d"),
+ hexstr2bin("3eb7a829b0cd20f5bcfc0b599b6feccf6da4627107bdb0d4f345b430"
+ "27d8b972fc3e34fb4232a13ca706dcb57aec3dae07bdc1c67bf33609")},
+ %% RFC 7748, 6.1
+ {ecdh, x25519,
+ hexstr2bin("77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a"),
+ hexstr2bin("8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a")},
+ {ecdh, x25519,
+ hexstr2bin("5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb"),
+ hexstr2bin("de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f")}],
lists:filter(fun ({_Type, Curve, _Priv, _Pub}) ->
lists:member(Curve, Curves)
end,
TestCases).
+
int_to_bin(X) when X < 0 -> int_to_bin_neg(X, []);
int_to_bin(X) -> int_to_bin_pos(X, []).
diff --git a/lib/crypto/vsn.mk b/lib/crypto/vsn.mk
index 2315cb3c48..9a5b9397f7 100644
--- a/lib/crypto/vsn.mk
+++ b/lib/crypto/vsn.mk
@@ -1 +1 @@
-CRYPTO_VSN = 4.5.1
+CRYPTO_VSN = 4.6
diff --git a/lib/debugger/Makefile b/lib/debugger/Makefile
index f91b8bfa5e..3a06d72c5d 100644
--- a/lib/debugger/Makefile
+++ b/lib/debugger/Makefile
@@ -35,4 +35,6 @@ SPECIAL_TARGETS =
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_subdir.mk
+DIA_PLT_APPS=wx
+
include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/dialyzer/Makefile b/lib/dialyzer/Makefile
index ab0b94748e..53083f267d 100644
--- a/lib/dialyzer/Makefile
+++ b/lib/dialyzer/Makefile
@@ -42,4 +42,6 @@ SPECIAL_TARGETS =
#
include $(ERL_TOP)/make/otp_subdir.mk
+DIA_PLT_APPS=compiler syntax_tools hipe wx
+
include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/dialyzer/doc/src/notes.xml b/lib/dialyzer/doc/src/notes.xml
index dad9fd18c7..f6cd2ec585 100644
--- a/lib/dialyzer/doc/src/notes.xml
+++ b/lib/dialyzer/doc/src/notes.xml
@@ -32,6 +32,23 @@
<p>This document describes the changes made to the Dialyzer
application.</p>
+<section><title>Dialyzer 4.1</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> Allow native compilation when using Dialyzer from
+ Erlang. The options <c>native</c> (defaults to
+ <c>false</c>) and <c>native_cache</c> have been added.
+ </p>
+ <p>
+ Own Id: OTP-15880 Aux Id: PR-2283 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Dialyzer 4.0.3</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/dialyzer/vsn.mk b/lib/dialyzer/vsn.mk
index a77c74c717..03155e2d24 100644
--- a/lib/dialyzer/vsn.mk
+++ b/lib/dialyzer/vsn.mk
@@ -1 +1 @@
-DIALYZER_VSN = 4.0.3
+DIALYZER_VSN = 4.1
diff --git a/lib/diameter/Makefile b/lib/diameter/Makefile
index 8c3c0ff0cc..a25baeb929 100644
--- a/lib/diameter/Makefile
+++ b/lib/diameter/Makefile
@@ -27,9 +27,6 @@ SPECIAL_TARGETS =
include $(ERL_TOP)/make/otp_subdir.mk
-info:
- @echo "APP_VSN = $(APP_VSN)"
-
-.PHONY: info
+DIA_PLT_APPS=ssl runtime_tools syntax_tools
include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/edoc/Makefile b/lib/edoc/Makefile
index 6dfc6f51c7..a8258015b1 100644
--- a/lib/edoc/Makefile
+++ b/lib/edoc/Makefile
@@ -69,7 +69,7 @@ SPECIAL_TARGETS =
include $(ERL_TOP)/make/otp_subdir.mk
-.PHONY: info version
+.PHONY: version
version:
@@ -92,9 +92,6 @@ edocs:
-pa $(XMERL_DIR)/ebin -run edoc_run application \
"'$(APPNAME)'" '"."' '$(DOC_OPTS)'
-info:
- @echo $(HTML_FILES)
-
app_release: tar
@@ -125,5 +122,6 @@ tar: $(APP_TAR_FILE)
$(APP_TAR_FILE): $(APP_DIR)
(cd $(APP_RELEASE_DIR); gtar zcf $(APP_TAR_FILE) $(DIR_NAME))
+DIA_PLT_APPS=syntax_tools xmerl inets
include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/edoc/src/edoc.app.src b/lib/edoc/src/edoc.app.src
index 43343e2ae8..834c0eb005 100644
--- a/lib/edoc/src/edoc.app.src
+++ b/lib/edoc/src/edoc.app.src
@@ -23,4 +23,4 @@
{applications, [compiler,kernel,stdlib,syntax_tools]},
{env, []},
{runtime_dependencies, ["xmerl-1.3.7","syntax_tools-1.6.14","stdlib-2.5",
- "kernel-3.0","inets-5.10","erts-6.0"]}]}.
+ "kernel-3.0","erts-6.0"]}]}.
diff --git a/lib/edoc/src/edoc.erl b/lib/edoc/src/edoc.erl
index 62483602aa..0fdc818fae 100644
--- a/lib/edoc/src/edoc.erl
+++ b/lib/edoc/src/edoc.erl
@@ -259,10 +259,9 @@ opt_negations() ->
%% </dd>
%% <dt>{@type {doc_path, [string()]@}}
%% </dt>
-%% <dd>Specifies a list of URI:s pointing to directories that contain
-%% EDoc-generated documentation. URI without a `scheme://' part are
-%% taken as relative to `file://'. (Note that such paths must use
-%% `/' as separator, regardless of the host operating system.)
+%% <dd>Specifies a list of file system paths pointing to directories that
+%% contain EDoc-generated documentation. All paths for applications
+%% in the code path are automatically added.
%% </dd>
%% <dt>{@type {doclet, Module::atom()@}}
%% </dt>
diff --git a/lib/edoc/src/edoc_lib.erl b/lib/edoc/src/edoc_lib.erl
index d00a283794..5959fa6f08 100644
--- a/lib/edoc/src/edoc_lib.erl
+++ b/lib/edoc/src/edoc_lib.erl
@@ -32,12 +32,12 @@
-export([count/2, lines/1, split_at/2, split_at_stop/1,
split_at_space/1, filename/1, transpose/1, segment/2,
get_first_sentence/1, is_space/1, strip_space/1, parse_expr/2,
- parse_contact/2, escape_uri/1, join_uri/2, is_relative_uri/1,
+ parse_contact/2, escape_uri/1, join_uri/2,
is_name/1, to_label/1, find_doc_dirs/0, find_sources/2,
find_file/2, try_subdir/2, unique/1,
write_file/3, write_file/4, write_info_file/3,
read_info_file/1, get_doc_env/1, get_doc_env/3, copy_file/2,
- uri_get/1, run_doclet/2, run_layout/2,
+ run_doclet/2, run_layout/2,
simplify_path/1, timestr/1, datestr/1, read_encoding/2]).
-import(edoc_report, [report/2, warning/2]).
@@ -438,128 +438,6 @@ join_uri("", Path) ->
join_uri(Base, Path) ->
Base ++ "/" ++ Path.
-%% Check for relative URI; "network paths" ("//...") not included!
-
-%% @private
-is_relative_uri([$: | _]) ->
- false;
-is_relative_uri([$/, $/ | _]) ->
- false;
-is_relative_uri([$/ | _]) ->
- true;
-is_relative_uri([$? | _]) ->
- true;
-is_relative_uri([$# | _]) ->
- true;
-is_relative_uri([_ | Cs]) ->
- is_relative_uri(Cs);
-is_relative_uri([]) ->
- true.
-
-%% @private
-uri_get("file:///" ++ Path) ->
- uri_get_file(Path);
-uri_get("file://localhost/" ++ Path) ->
- uri_get_file(Path);
-uri_get("file://" ++ Path) ->
- Msg = io_lib:format("cannot handle 'file:' scheme with "
- "nonlocal network-path: 'file://~ts'.",
- [Path]),
- {error, Msg};
-uri_get("file:/" ++ Path) ->
- uri_get_file(Path);
-uri_get("file:" ++ Path) ->
- Msg = io_lib:format("ignoring malformed URI: 'file:~ts'.", [Path]),
- {error, Msg};
-uri_get("http:" ++ Path) ->
- uri_get_http("http:" ++ Path);
-uri_get("ftp:" ++ Path) ->
- uri_get_ftp("ftp:" ++ Path);
-uri_get("//" ++ Path) ->
- Msg = io_lib:format("cannot access network-path: '//~ts'.", [Path]),
- {error, Msg};
-uri_get([C, $:, $/ | _]=Path) when C >= $A, C =< $Z; C >= $a, C =< $z ->
- uri_get_file(Path); % special case for Windows
-uri_get([C, $:, $\ | _]=Path) when C >= $A, C =< $Z; C >= $a, C =< $z ->
- uri_get_file(Path); % special case for Windows
-uri_get(URI) ->
- case is_relative_uri(URI) of
- true ->
- uri_get_file(URI);
- false ->
- Msg = io_lib:format("cannot handle URI: '~ts'.", [URI]),
- {error, Msg}
- end.
-
-uri_get_file(File0) ->
- File = filename:join(?FILE_BASE, File0),
- case read_file(File) of
- {ok, Text} ->
- {ok, Text};
- {error, R} ->
- {error, file:format_error(R)}
- end.
-
-uri_get_http(URI) ->
- %% Try using option full_result=false
- case catch {ok, httpc:request(get, {URI,[]}, [],
- [{full_result, false}])} of
- {'EXIT', _} ->
- uri_get_http_r10(URI);
- Result ->
- uri_get_http_1(Result, URI)
- end.
-
-uri_get_http_r10(URI) ->
- %% Try most general form of request
- Result = (catch {ok, httpc:request(get, {URI,[]}, [], [])}),
- uri_get_http_1(Result, URI).
-
-uri_get_http_1(Result, URI) ->
- case Result of
- {ok, {ok, {200, Text}}} when is_list(Text) ->
- %% new short result format
- {ok, Text};
- {ok, {ok, {Status, Text}}} when is_integer(Status), is_list(Text) ->
- %% new short result format when status /= 200
- Phrase = httpd_util:reason_phrase(Status),
- {error, http_errmsg(Phrase, URI)};
- {ok, {ok, {{_Vsn, 200, _Phrase}, _Hdrs, Text}}} when is_list(Text) ->
- %% new long result format
- {ok, Text};
- {ok, {ok, {{_Vsn, _Status, Phrase}, _Hdrs, Text}}} when is_list(Text) ->
- %% new long result format when status /= 200
- {error, http_errmsg(Phrase, URI)};
- {ok, {200,_Hdrs,Text}} when is_list(Text) ->
- %% old result format
- {ok, Text};
- {ok, {Status,_Hdrs,Text}} when is_list(Text) ->
- %% old result format when status /= 200
- Phrase = httpd_util:reason_phrase(Status),
- {error, http_errmsg(Phrase, URI)};
- {ok, {error, R}} ->
- Reason = inet:format_error(R),
- {error, http_errmsg(Reason, URI)};
- {ok, R} ->
- Reason = io_lib:format("bad return value ~tP", [R, 5]),
- {error, http_errmsg(Reason, URI)};
- {'EXIT', R} ->
- Reason = io_lib:format("crashed with reason ~tw", [R]),
- {error, http_errmsg(Reason, URI)};
- R ->
- Reason = io_lib:format("uncaught throw: ~tw", [R]),
- {error, http_errmsg(Reason, URI)}
- end.
-
-http_errmsg(Reason, URI) ->
- io_lib:format("http error: ~ts: '~ts'", [Reason, URI]).
-
-%% TODO: implement ftp access method
-
-uri_get_ftp(URI) ->
- Msg = io_lib:format("cannot access ftp scheme yet: '~ts'.", [URI]),
- {error, Msg}.
-
%% @private
to_label([$\s | Cs]) ->
to_label(Cs);
@@ -754,18 +632,6 @@ read_info_file(Dir) ->
{?NO_APP, []}
end.
-%% URI access
-
-uri_get_info_file(Base) ->
- URI = join_uri(Base, ?INFO_FILE),
- case uri_get(URI) of
- {ok, Text} ->
- parse_info_file(Text, URI);
- {error, Msg} ->
- warning("could not read '~ts': ~ts.", [URI, Msg]),
- {?NO_APP, []}
- end.
-
parse_info_file(Text, Name) ->
case parse_terms(Text) of
{ok, Vs} ->
@@ -897,7 +763,7 @@ find_doc_dirs([]) ->
get_doc_links(App, Modules, Opts) ->
Path = proplists:append_values(doc_path, Opts) ++ find_doc_dirs(),
- Ds = [{P, uri_get_info_file(P)} || P <- Path],
+ Ds = [{P, read_info_file(P)} || P <- Path],
Ds1 = [{"", {App, Modules}} | Ds],
D = dict:new(),
make_links(Ds1, D, D).
diff --git a/lib/eldap/Makefile b/lib/eldap/Makefile
index 98b5203dfd..30a8f778ab 100644
--- a/lib/eldap/Makefile
+++ b/lib/eldap/Makefile
@@ -38,4 +38,6 @@ SPECIAL_TARGETS =
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_subdir.mk
+DIA_PLT_APPS=asn ssl
+
include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/eldap/doc/src/eldap.xml b/lib/eldap/doc/src/eldap.xml
index 790a2f4e26..4a8c2d1647 100644
--- a/lib/eldap/doc/src/eldap.xml
+++ b/lib/eldap/doc/src/eldap.xml
@@ -317,7 +317,7 @@
</type>
<desc>
<p> Modify the DN of an entry. <c>DeleteOldRDN</c> indicates
- whether the current RDN should be removed from the attribute list after the after operation.
+ whether the current RDN should be removed from the attribute list after the operation.
<c>NewSupDN</c> is the new parent that the RDN shall be moved to. If the old parent should
remain as parent, <c>NewSupDN</c> shall be "".</p>
<code>
diff --git a/lib/erl_docgen/Makefile b/lib/erl_docgen/Makefile
index a13a3c4f94..7e9cc824ec 100644
--- a/lib/erl_docgen/Makefile
+++ b/lib/erl_docgen/Makefile
@@ -36,5 +36,6 @@ SPECIAL_TARGETS =
#
include $(ERL_TOP)/make/otp_subdir.mk
+DIA_PLT_APPS=edoc xmerl
include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/erl_docgen/doc/src/notes.xml b/lib/erl_docgen/doc/src/notes.xml
index f25361a202..f51cd26a7f 100644
--- a/lib/erl_docgen/doc/src/notes.xml
+++ b/lib/erl_docgen/doc/src/notes.xml
@@ -31,7 +31,22 @@
</header>
<p>This document describes the changes made to the <em>erl_docgen</em> application.</p>
- <section><title>Erl_Docgen 0.9.1</title>
+ <section><title>Erl_Docgen 0.10</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p> Update the documentation build support to handle FOP
+ 2.1 . </p>
+ <p>
+ Own Id: OTP-16051</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erl_Docgen 0.9.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/erl_docgen/priv/xsl/db_pdf.xsl b/lib/erl_docgen/priv/xsl/db_pdf.xsl
index 1b91d768e3..7080394298 100644
--- a/lib/erl_docgen/priv/xsl/db_pdf.xsl
+++ b/lib/erl_docgen/priv/xsl/db_pdf.xsl
@@ -662,7 +662,7 @@
<fo:flow flow-name="xsl-region-body">
<fo:block xsl:use-attribute-sets="cover.logo">
- <fo:external-graphic src="{$logo}"/>
+ <fo:external-graphic src="url('{$logo}')"/>
</fo:block>
<fo:block xsl:use-attribute-sets="cover.title" id="cover-page">
<xsl:apply-templates/>
@@ -1658,10 +1658,10 @@
<fo:block xsl:use-attribute-sets="image">
<xsl:choose>
<xsl:when test="@width">
- <fo:external-graphic content-width="scale-to-fit" width="{@width}" inline-progression-dimension.maximum="100%" src="{@file}"/>
+ <fo:external-graphic content-width="scale-to-fit" width="{@width}" inline-progression-dimension.maximum="100%" src="url('{@file}')"/>
</xsl:when>
<xsl:otherwise>
- <fo:external-graphic content-width="scale-down-to-fit" inline-progression-dimension.maximum="100%" src="{@file}"/>
+ <fo:external-graphic content-width="scale-down-to-fit" inline-progression-dimension.maximum="100%" src="url('{@file}')"/>
</xsl:otherwise>
</xsl:choose>
<xsl:apply-templates>
diff --git a/lib/erl_docgen/vsn.mk b/lib/erl_docgen/vsn.mk
index fece2456c1..2ac4acaf09 100644
--- a/lib/erl_docgen/vsn.mk
+++ b/lib/erl_docgen/vsn.mk
@@ -1 +1 @@
-ERL_DOCGEN_VSN = 0.9.1
+ERL_DOCGEN_VSN = 0.10
diff --git a/lib/erl_interface/doc/src/Makefile b/lib/erl_interface/doc/src/Makefile
index 507a84a453..03044a0ddd 100644
--- a/lib/erl_interface/doc/src/Makefile
+++ b/lib/erl_interface/doc/src/Makefile
@@ -96,7 +96,7 @@ man: $(MAN1_FILES) $(MAN3_FILES)
gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
-debug opt:
+debug opt lcnt:
clean clean_docs clean_tex:
rm -rf $(HTMLDIR)/*
diff --git a/lib/erl_interface/doc/src/notes.xml b/lib/erl_interface/doc/src/notes.xml
index c47f0d2bd1..08f5a40687 100644
--- a/lib/erl_interface/doc/src/notes.xml
+++ b/lib/erl_interface/doc/src/notes.xml
@@ -31,6 +31,41 @@
</header>
<p>This document describes the changes made to the Erl_interface application.</p>
+<section><title>Erl_Interface 3.13</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix bugs in <c>ei_print_term</c> for binaries and bit
+ strings causing incorrect output.</p>
+ <p>
+ Own Id: OTP-15917</p>
+ </item>
+ <item>
+ <p>
+ Fixed bug in <c>ei_decode_fun</c> for very old fun
+ encoding format. Bug exist since OTP 22.0.</p>
+ <p>
+ Own Id: OTP-15996</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p><c>ei_print_term()</c> now supports printing of maps
+ and funs.</p>
+ <p>
+ Own Id: OTP-15814</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Erl_Interface 3.12</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/erl_interface/src/Makefile b/lib/erl_interface/src/Makefile
index 00c49f1622..6f728a0a28 100644
--- a/lib/erl_interface/src/Makefile
+++ b/lib/erl_interface/src/Makefile
@@ -33,3 +33,5 @@ endif
clean depend docs release release_docs tests release_tests check xmllint:
$(make_verbose)$(MAKE) -f $(TARGET)/Makefile $@
+
+lcnt:
diff --git a/lib/erl_interface/src/decode/decode_fun.c b/lib/erl_interface/src/decode/decode_fun.c
index db71007505..76dc0e2ab8 100644
--- a/lib/erl_interface/src/decode/decode_fun.c
+++ b/lib/erl_interface/src/decode/decode_fun.c
@@ -52,7 +52,10 @@ int ei_decode_fun(const char *buf, int *index, erlang_fun *p)
switch (get8(s)) {
case ERL_FUN_EXT:
/* mark as old (R7 and older) external fun */
- if (p != NULL) p->arity = -1;
+ if (p != NULL) {
+ p->type = EI_FUN_CLOSURE;
+ p->arity = -1;
+ }
/* first number of free vars (environment) */
n = get32be(s);
/* then the pid */
diff --git a/lib/erl_interface/vsn.mk b/lib/erl_interface/vsn.mk
index cc72ed639a..2bf84bf18f 100644
--- a/lib/erl_interface/vsn.mk
+++ b/lib/erl_interface/vsn.mk
@@ -1,2 +1,2 @@
-EI_VSN = 3.12
+EI_VSN = 3.13
ERL_INTERFACE_VSN = $(EI_VSN)
diff --git a/lib/et/Makefile b/lib/et/Makefile
index 98e15dc179..1b89cff83e 100644
--- a/lib/et/Makefile
+++ b/lib/et/Makefile
@@ -35,4 +35,6 @@ SPECIAL_TARGETS =
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_subdir.mk
+DIA_PLT_APPS=runtime_tools wx
+
include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/eunit/Makefile b/lib/eunit/Makefile
index acc765faf9..d0dd447a6c 100644
--- a/lib/eunit/Makefile
+++ b/lib/eunit/Makefile
@@ -48,13 +48,7 @@ SPECIAL_TARGETS =
#
include $(ERL_TOP)/make/otp_subdir.mk
-
-.PHONY: info version
-
-info:
- @echo "APP_RELEASE_DIR: $(APP_RELEASE_DIR)"
- @echo "APP_DIR: $(APP_DIR)"
- @echo "APP_TAR_FILE: $(APP_TAR_FILE)"
+.PHONY: version
version:
@echo "$(VSN)"
diff --git a/lib/eunit/doc/src/notes.xml b/lib/eunit/doc/src/notes.xml
index 67a9ae5fcb..397a4657d3 100644
--- a/lib/eunit/doc/src/notes.xml
+++ b/lib/eunit/doc/src/notes.xml
@@ -33,6 +33,21 @@
</header>
<p>This document describes the changes made to the EUnit application.</p>
+<section><title>Eunit 2.3.8</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Handle <c>get_until</c> request with explicit
+ encoding in the implementation of the I/O protocol. </p>
+ <p>
+ Own Id: OTP-16000</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Eunit 2.3.7</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/eunit/src/eunit_proc.erl b/lib/eunit/src/eunit_proc.erl
index 96bdcf88b6..6e702543d0 100644
--- a/lib/eunit/src/eunit_proc.erl
+++ b/lib/eunit/src/eunit_proc.erl
@@ -644,6 +644,8 @@ io_request({get_line, _Enc, _Prompt}, Buf) ->
{eof, Buf};
io_request({get_until, _Prompt, _M, _F, _As}, Buf) ->
{eof, Buf};
+io_request({get_until, _Enc, _Prompt, _M, _F, _As}, Buf) ->
+ {eof, Buf};
io_request({setopts, _Opts}, Buf) ->
{ok, Buf};
io_request(getopts, Buf) ->
diff --git a/lib/eunit/src/eunit_surefire.erl b/lib/eunit/src/eunit_surefire.erl
index 2b9f82b075..002a069a92 100644
--- a/lib/eunit/src/eunit_surefire.erl
+++ b/lib/eunit/src/eunit_surefire.erl
@@ -451,6 +451,11 @@ escape_xml([$< | Tail], Acc, ForAttr) -> escape_xml(Tail, [$;, $t, $l, $& | Acc]
escape_xml([$> | Tail], Acc, ForAttr) -> escape_xml(Tail, [$;, $t, $g, $& | Acc], ForAttr);
escape_xml([$& | Tail], Acc, ForAttr) -> escape_xml(Tail, [$;, $p, $m, $a, $& | Acc], ForAttr);
escape_xml([$" | Tail], Acc, true) -> escape_xml(Tail, [$;, $t, $o, $u, $q, $& | Acc], true); % "
+escape_xml([Char | Tail], Acc, ForAttr) when
+ Char == $\n; Char == $\r; Char == $\t -> escape_xml(Tail, [Char | Acc], ForAttr);
+%% Strip C0 control codes which are not allowed in XML 1.0
+escape_xml([Char | Tail], Acc, ForAttr) when
+ 0 =< Char, Char =< 31 -> escape_xml(Tail, Acc, ForAttr);
escape_xml([Char | Tail], Acc, ForAttr) when is_integer(Char) -> escape_xml(Tail, [Char | Acc], ForAttr).
%% the input may be utf8 or latin1; the resulting list is unicode
diff --git a/lib/eunit/test/Makefile b/lib/eunit/test/Makefile
index b6ece61b43..7c1e56c867 100644
--- a/lib/eunit/test/Makefile
+++ b/lib/eunit/test/Makefile
@@ -22,6 +22,7 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk
MODULES = \
eunit_SUITE \
+ tc0 \
tlatin \
tutf8
diff --git a/lib/eunit/test/eunit_SUITE.erl b/lib/eunit/test/eunit_SUITE.erl
index 63bdc6c334..b9f4ea4557 100644
--- a/lib/eunit/test/eunit_SUITE.erl
+++ b/lib/eunit/test/eunit_SUITE.erl
@@ -21,14 +21,16 @@
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2,
- app_test/1,appup_test/1,eunit_test/1,surefire_utf8_test/1,surefire_latin_test/1]).
+ app_test/1,appup_test/1,eunit_test/1,surefire_utf8_test/1,surefire_latin_test/1,
+ surefire_c0_test/1]).
-include_lib("common_test/include/ct.hrl").
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [app_test, appup_test, eunit_test, surefire_utf8_test, surefire_latin_test].
+ [app_test, appup_test, eunit_test, surefire_utf8_test, surefire_latin_test,
+ surefire_c0_test].
groups() ->
[].
@@ -65,11 +67,24 @@ surefire_utf8_test(Config) when is_list(Config) ->
check_surefire(tutf8),
ok.
+surefire_c0_test(Config) when is_list(Config) ->
+ ok = file:set_cwd(proplists:get_value(priv_dir, Config, ".")),
+ Chars = check_surefire(tc0),
+ %% Check that these characters were not stripped
+ true = lists:member($\n, Chars),
+ true = lists:member($\r, Chars),
+ true = lists:member($\t, Chars),
+ ok.
+
check_surefire(Module) ->
File = "TEST-"++atom_to_list(Module)++".xml",
file:delete(File),
% ignore test result, some fail on purpose
eunit:test(Module, [{report,{eunit_surefire,[{dir,"."}]}}]),
{ok, Bin} = file:read_file(File),
- [_|_] = unicode:characters_to_list(Bin, unicode),
- ok. \ No newline at end of file
+ Chars = unicode:characters_to_list(Bin, unicode),
+ %% Check that unicode decoding succeeded
+ [_|_] = Chars,
+ %% Check that file is valid XML
+ xmerl_scan:file(File),
+ Chars.
diff --git a/lib/eunit/test/tc0.erl b/lib/eunit/test/tc0.erl
new file mode 100644
index 0000000000..8c90633fc8
--- /dev/null
+++ b/lib/eunit/test/tc0.erl
@@ -0,0 +1,14 @@
+-module(tc0).
+
+-include_lib("eunit/include/eunit.hrl").
+
+'c0_bad_output_test_'() ->
+ [{integer_to_list(C), fun() -> io:format("'~c'", [C]) end}
+ || C <- lists:seq(0, 31)].
+
+'c0_bad_description_test_'() ->
+ [{[C], fun() -> ok end}
+ || C <- lists:seq(0, 31)].
+
+'c0_bad_name__test'() ->
+ ok.
diff --git a/lib/eunit/vsn.mk b/lib/eunit/vsn.mk
index 46ef5eea3c..52d23698fa 100644
--- a/lib/eunit/vsn.mk
+++ b/lib/eunit/vsn.mk
@@ -1 +1 @@
-EUNIT_VSN = 2.3.7
+EUNIT_VSN = 2.3.8
diff --git a/lib/ftp/Makefile b/lib/ftp/Makefile
index e6bceebe15..a26d1de0a0 100644
--- a/lib/ftp/Makefile
+++ b/lib/ftp/Makefile
@@ -32,49 +32,11 @@ VSN = $(FTP_VSN)
SPECIAL_TARGETS =
-DIA_PLT = ./priv/plt/$(APPLICATION).plt
-DIA_ANALYSIS = $(basename $(DIA_PLT)).dialyzer_analysis
-
-
# ----------------------------------------------------
# Default Subdir Targets
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_subdir.mk
-.PHONY: info gclean dialyzer dialyzer_plt dclean
-
-info:
- @echo "OS: $(OS)"
- @echo "DOCB: $(DOCB)"
- @echo ""
- @echo "FTP_VSN: $(FTP_VSN)"
- @echo "APP_VSN: $(APP_VSN)"
- @echo ""
- @echo "DIA_PLT: $(DIA_PLT)"
- @echo "DIA_ANALYSIS: $(DIA_ANALYSIS)"
- @echo ""
-
-gclean:
- git clean -fXd
-
-dclean:
- rm -f $(DIA_PLT)
- rm -f $(DIA_ANALYSIS)
-
-dialyzer_plt: $(DIA_PLT)
-
-$(DIA_PLT):
- @echo "Building $(APPLICATION) plt file"
- @dialyzer --build_plt \
- --output_plt $@ \
- -r ../$(APPLICATION)/ebin \
- --output $(DIA_ANALYSIS) \
- --verbose
-
-dialyzer: $(DIA_PLT)
- @echo "Running dialyzer on $(APPLICATION)"
- @dialyzer --plt $< \
- ../$(APPLICATION)/ebin \
- --verbose
+DIA_PLT_APPS = runtime_tools ssl
include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/ftp/doc/src/notes.xml b/lib/ftp/doc/src/notes.xml
index 61da079900..d71f795c09 100644
--- a/lib/ftp/doc/src/notes.xml
+++ b/lib/ftp/doc/src/notes.xml
@@ -33,7 +33,23 @@
<file>notes.xml</file>
</header>
- <section><title>Ftp 1.0.2</title>
+ <section><title>Ftp 1.0.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ A possibly infinite loop when receiving messages divided
+ in parts is removed.</p>
+ <p>
+ Own Id: OTP-16056</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Ftp 1.0.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/ftp/src/ftp.erl b/lib/ftp/src/ftp.erl
index 18cd8c7524..e9be7b8ff7 100644
--- a/lib/ftp/src/ftp.erl
+++ b/lib/ftp/src/ftp.erl
@@ -1399,10 +1399,13 @@ handle_info({Transport, Socket, Data}, #state{csock = {Transport, Socket},
ctrl_data =
{NextMsgData, [], start}})
end;
- {continue, NewCtrlData} ->
+ {continue, NewCtrlData} when NewCtrlData =/= CtrlData ->
?DBG(' ...Continue... ctrl_data=~p~n',[NewCtrlData]),
State = activate_ctrl_connection(State0),
- {noreply, State#state{ctrl_data = NewCtrlData}}
+ {noreply, State#state{ctrl_data = NewCtrlData}};
+ {continue, NewCtrlData} when NewCtrlData == CtrlData ->
+ ?DBG(' ...Continue... ctrl_data=~p~n',[NewCtrlData]),
+ {noreply, State0}
end;
%% If the server closes the control channel it is
diff --git a/lib/ftp/vsn.mk b/lib/ftp/vsn.mk
index 9f14658099..397328ce27 100644
--- a/lib/ftp/vsn.mk
+++ b/lib/ftp/vsn.mk
@@ -19,6 +19,6 @@
# %CopyrightEnd%
APPLICATION = ftp
-FTP_VSN = 1.0.2
+FTP_VSN = 1.0.3
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(FTP_VSN)$(PRE_VSN)"
diff --git a/lib/hipe/Makefile b/lib/hipe/Makefile
index a1c5f9c83f..4998522943 100644
--- a/lib/hipe/Makefile
+++ b/lib/hipe/Makefile
@@ -75,4 +75,6 @@ distclean:
realclean:
$(V_at)$(MAKE) MAKETARGET="realclean" all-subdirs all-subdirs-x
+DIA_PLT_APPS=compiler syntax_tools
+
include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/inets/Makefile b/lib/inets/Makefile
index 9a03ee93df..a7723dc0d8 100644
--- a/lib/inets/Makefile
+++ b/lib/inets/Makefile
@@ -32,49 +32,11 @@ VSN = $(INETS_VSN)
SPECIAL_TARGETS =
-DIA_PLT = ./priv/plt/$(APPLICATION).plt
-DIA_ANALYSIS = $(basename $(DIA_PLT)).dialyzer_analysis
-
-
# ----------------------------------------------------
# Default Subdir Targets
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_subdir.mk
-.PHONY: info gclean dialyzer dialyzer_plt dclean
-
-info:
- @echo "OS: $(OS)"
- @echo "DOCB: $(DOCB)"
- @echo ""
- @echo "INETS_VSN: $(INETS_VSN)"
- @echo "APP_VSN: $(APP_VSN)"
- @echo ""
- @echo "DIA_PLT: $(DIA_PLT)"
- @echo "DIA_ANALYSIS: $(DIA_ANALYSIS)"
- @echo ""
-
-gclean:
- git clean -fXd
-
-dclean:
- rm -f $(DIA_PLT)
- rm -f $(DIA_ANALYSIS)
-
-dialyzer_plt: $(DIA_PLT)
-
-$(DIA_PLT):
- @echo "Building $(APPLICATION) plt file"
- @dialyzer --build_plt \
- --output_plt $@ \
- -r ../$(APPLICATION)/ebin \
- --output $(DIA_ANALYSIS) \
- --verbose
-
-dialyzer: $(DIA_PLT)
- @echo "Running dialyzer on $(APPLICATION)"
- @dialyzer --plt $< \
- ../$(APPLICATION)/ebin \
- --verbose
+DIA_PLT_APPS=runtime_tools ftp mnesia ssl tftp
include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/inets/doc/src/httpc.xml b/lib/inets/doc/src/httpc.xml
index 7451b314ec..72ac79a0b0 100644
--- a/lib/inets/doc/src/httpc.xml
+++ b/lib/inets/doc/src/httpc.xml
@@ -701,15 +701,17 @@
<type>
<v>Profile = profile() | pid()</v>
<d>When started <c>stand_alone</c> only the pid can be used.</d>
- <v>session_info() = {GoodSessions, BadSessions, NonSessions}</v>
- <v>GoodSessions = session()</v>
- <v>BadSessions = tuple()</v>
- <v>NonSessions = term()</v>
+ <v>session_info() = {[session()], [term()], [term()]}</v>
+ <v>session() = term() - Internal representation of a session</v>
</type>
<desc>
- <p>Produces a slightly processed dump of the session
- database. It is intended for debugging.
- If no profile is specified, the default profile is used.</p>
+ <p> This function is intended for debugging only. It produces
+ a slightly processed dump of the session database. The first
+ list of the session information tuple will contain session
+ information on an internal format. The last two lists of the
+ session information tuple should always be empty if the code
+ is working as intended. If no profile is specified, the default
+ profile is used.</p>
</desc>
</func>
diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml
index 45533c4f4b..da670bb1c9 100644
--- a/lib/inets/doc/src/notes.xml
+++ b/lib/inets/doc/src/notes.xml
@@ -33,7 +33,30 @@
<file>notes.xml</file>
</header>
- <section><title>Inets 7.0.9</title>
+ <section><title>Inets 7.1</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ httpd - Accept singel LF as line terminator</p>
+ <p>
+ Own Id: OTP-15893 Aux Id: PR-2206 </p>
+ </item>
+ <item>
+ <p>
+ mod_esi will now always propagate the actual HTTP status
+ code that it answered with, to later mod-modules, and not
+ in some cases hardcode 200.</p>
+ <p>
+ Own Id: OTP-16049 Aux Id: ERIERL-395 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 7.0.9</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
@@ -66,6 +89,23 @@
</section>
+ <section><title>Inets 7.0.7.1</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ mod_esi will now always propagate the actual HTTP status
+ code that it anwsered with, to later mod-modules, and not
+ in some cases hardcode 200.</p>
+ <p>
+ Own Id: OTP-16049 Aux Id: ERIERL-395 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Inets 7.0.7</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -3346,5 +3386,3 @@
-->
</chapter>
-
-
diff --git a/lib/inets/src/http_server/mod_esi.erl b/lib/inets/src/http_server/mod_esi.erl
index 8cbd9798e6..bcf392d55c 100644
--- a/lib/inets/src/http_server/mod_esi.erl
+++ b/lib/inets/src/http_server/mod_esi.erl
@@ -345,12 +345,12 @@ erl_scheme_webpage_whole(Mod, Func, Env, Input, ModData) ->
integer_to_list(Length)}| NewHeaders]),
case ModData#mod.method of
"HEAD" ->
- {proceed, [{response, {already_sent, 200, 0}} |
+ {proceed, [{response, {already_sent, StatusCode, 0}} |
ModData#mod.data]};
_ ->
httpd_response:send_body(ModData,
StatusCode, Body),
- {proceed, [{response, {already_sent, 200,
+ {proceed, [{response, {already_sent, StatusCode,
Length}} |
ModData#mod.data]}
end
@@ -415,12 +415,12 @@ deliver_webpage_chunk(#mod{config_db = Db} = ModData, Pid, Timeout) ->
[{"transfer-encoding",
"chunked"} | NewHeaders])
end,
- handle_body(Pid, ModData, Body, Timeout, length(Body),
+ handle_body(Pid, ModData, Body, Timeout, length(Body), StatusCode,
IsDisableChunkedSend);
timeout ->
send_headers(ModData, 504, [{"connection", "close"}]),
httpd_socket:close(ModData#mod.socket_type, ModData#mod.socket),
- {proceed,[{response, {already_sent, 200, 0}} | ModData#mod.data]}
+ {proceed,[{response, {already_sent, 504, 0}} | ModData#mod.data]}
end.
receive_headers(Timeout) ->
@@ -444,24 +444,24 @@ send_headers(ModData, StatusCode, HTTPHeaders) ->
httpd_response:send_header(ModData, StatusCode,
ExtraHeaders ++ HTTPHeaders).
-handle_body(_, #mod{method = "HEAD"} = ModData, _, _, Size, _) ->
- {proceed, [{response, {already_sent, 200, Size}} | ModData#mod.data]};
+handle_body(_, #mod{method = "HEAD"} = ModData, _, _, Size, StatusCode, _) ->
+ {proceed, [{response, {already_sent, StatusCode, Size}} | ModData#mod.data]};
-handle_body(Pid, ModData, Body, Timeout, Size, IsDisableChunkedSend) ->
+handle_body(Pid, ModData, Body, Timeout, Size, StatusCode, IsDisableChunkedSend) ->
httpd_response:send_chunk(ModData, Body, IsDisableChunkedSend),
receive
{esi_data, Data} when is_binary(Data) ->
- handle_body(Pid, ModData, Data, Timeout, Size + byte_size(Data),
+ handle_body(Pid, ModData, Data, Timeout, Size + byte_size(Data), StatusCode,
IsDisableChunkedSend);
{esi_data, Data} ->
- handle_body(Pid, ModData, Data, Timeout, Size + length(Data),
+ handle_body(Pid, ModData, Data, Timeout, Size + length(Data), StatusCode,
IsDisableChunkedSend);
{ok, Data} ->
- handle_body(Pid, ModData, Data, Timeout, Size + length(Data),
+ handle_body(Pid, ModData, Data, Timeout, Size + length(Data), StatusCode,
IsDisableChunkedSend);
{'EXIT', Pid, normal} when is_pid(Pid) ->
httpd_response:send_final_chunk(ModData, IsDisableChunkedSend),
- {proceed, [{response, {already_sent, 200, Size}} |
+ {proceed, [{response, {already_sent, StatusCode, Size}} |
ModData#mod.data]};
{'EXIT', Pid, Reason} when is_pid(Pid) ->
Error = lists:flatten(io_lib:format("mod_esi process failed with reason ~p", [Reason])),
diff --git a/lib/inets/test/httpd_SUITE.erl b/lib/inets/test/httpd_SUITE.erl
index fc5ca14dcd..bf926ec9c1 100644
--- a/lib/inets/test/httpd_SUITE.erl
+++ b/lib/inets/test/httpd_SUITE.erl
@@ -28,6 +28,7 @@
-include_lib("kernel/include/file.hrl").
-include_lib("common_test/include/ct.hrl").
-include_lib("public_key/include/public_key.hrl").
+-include_lib("inets/include/httpd.hrl").
-include("inets_test_lib.hrl").
%% Note: This directive should only be used in test suites.
@@ -58,6 +59,7 @@ all() ->
{group, https_limit},
{group, http_custom},
{group, https_custom},
+ {group, https_custom},
{group, http_basic_auth},
{group, https_basic_auth},
{group, http_auth_api},
@@ -139,7 +141,7 @@ groups() ->
{http_1_1, [],
[host, chunked, expect, cgi, cgi_chunked_encoding_test,
trace, range, if_modified_since, mod_esi_chunk_timeout,
- esi_put, esi_post] ++ http_head() ++ http_get() ++ load()},
+ esi_put, esi_post, esi_proagate] ++ http_head() ++ http_get() ++ load()},
{http_1_0, [], [host, cgi, trace] ++ http_head() ++ http_get() ++ load()},
{http_0_9, [], http_head() ++ http_get() ++ load()},
{http_rel_path_script_alias, [], [cgi]},
@@ -1053,6 +1055,17 @@ mod_esi_chunk_timeout(Config) when is_list(Config) ->
proplists:get_value(port, Config),
proplists:get_value(host, Config),
proplists:get_value(node, Config)).
+%%-------------------------------------------------------------------------
+esi_proagate(Config) when is_list(Config) ->
+ register(propagate_test, self()),
+ ok = http_status("GET /cgi-bin/erl/httpd_example:new_status_and_location ",
+ Config, [{statuscode, 201}]),
+ receive
+ {status, 201} ->
+ ok;
+ Err ->
+ ct:fail(Err)
+ end.
%%-------------------------------------------------------------------------
cgi() ->
@@ -2246,8 +2259,17 @@ head_status(_) ->
basic_conf() ->
[{modules, [mod_alias, mod_range, mod_responsecontrol,
- mod_trace, mod_esi, mod_cgi, mod_get, mod_head]}].
-
+ mod_trace, mod_esi, ?MODULE, mod_cgi, mod_get, mod_head]}].
+do(ModData) ->
+ case whereis(propagate_test) of
+ undefined ->
+ ok;
+ _ ->
+ {already_sent, Status, _Size} = proplists:get_value(response, ModData#mod.data),
+ propagate_test ! {status, Status}
+ end,
+ {proceed, ModData#mod.data}.
+
not_sup_conf() ->
[{modules, [mod_get]}].
diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk
index d948204618..afc02f2038 100644
--- a/lib/inets/vsn.mk
+++ b/lib/inets/vsn.mk
@@ -19,6 +19,6 @@
# %CopyrightEnd%
APPLICATION = inets
-INETS_VSN = 7.0.9
+INETS_VSN = 7.1
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)"
diff --git a/lib/jinterface/doc/src/notes.xml b/lib/jinterface/doc/src/notes.xml
index e79ada47f1..433534207a 100644
--- a/lib/jinterface/doc/src/notes.xml
+++ b/lib/jinterface/doc/src/notes.xml
@@ -31,6 +31,22 @@
</header>
<p>This document describes the changes made to the Jinterface application.</p>
+<section><title>Jinterface 1.10.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Replaced deprecated &lt;tt&gt; with &lt;code&gt; in
+ documentation.</p>
+ <p>
+ Own Id: OTP-16050</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Jinterface 1.10</title>
<section><title>Fixed Bugs and Malfunctions</title>
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 a3b089c1da..917e5baf3a 100644
--- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java
+++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java
@@ -106,9 +106,9 @@ public class OtpOutputStream extends ByteArrayOutputStream {
}
/**
- * Trims the capacity of this <tt>OtpOutputStream</tt> instance to be the
+ * Trims the capacity of this <code>OtpOutputStream</code> instance to be the
* buffer's current size. An application can use this operation to minimize
- * the storage of an <tt>OtpOutputStream</tt> instance.
+ * the storage of an <code>OtpOutputStream</code> instance.
*/
public void trimToSize() {
resize(super.count);
@@ -125,7 +125,7 @@ public class OtpOutputStream extends ByteArrayOutputStream {
}
/**
- * Increases the capacity of this <tt>OtpOutputStream</tt> instance, if
+ * Increases the capacity of this <code>OtpOutputStream</code> instance, if
* necessary, to ensure that it can hold at least the number of elements
* specified by the minimum capacity argument.
*
@@ -909,7 +909,7 @@ public class OtpOutputStream extends ByteArrayOutputStream {
* @param o
* the Erlang term to write.
* @param level
- * the compression level (<tt>0..9</tt>)
+ * the compression level (<code>0..9</code>)
*/
public void write_compressed(final OtpErlangObject o, final int level) {
@SuppressWarnings("resource")
diff --git a/lib/jinterface/vsn.mk b/lib/jinterface/vsn.mk
index 95c7c95726..f15a3f323b 100644
--- a/lib/jinterface/vsn.mk
+++ b/lib/jinterface/vsn.mk
@@ -1 +1 @@
-JINTERFACE_VSN = 1.10
+JINTERFACE_VSN = 1.10.1
diff --git a/lib/kernel/doc/src/file.xml b/lib/kernel/doc/src/file.xml
index b3e8149cc2..2c09c84b25 100644
--- a/lib/kernel/doc/src/file.xml
+++ b/lib/kernel/doc/src/file.xml
@@ -957,10 +957,9 @@ f.txt: {person, "kalle", 25}.
</item>
</taglist>
<p><c><anno>IoDevice</anno></c> is really the pid of the process that
- handles the file. This process is linked to the process
- that originally opened the file. If any process to which
- the <c><anno>IoDevice</anno></c> is linked terminates, the file is
- closed and the process itself is terminated.
+ handles the file. This process monitors the process that originally
+ opened the file (the owner process). If the owner process terminates,
+ the file is closed and the process itself terminates too.
An <c><anno>IoDevice</anno></c> returned from this call can be used
as an argument to the I/O functions (see
<seealso marker="stdlib:io"><c>io(3)</c></seealso>).</p>
@@ -1444,7 +1443,11 @@ f.txt: {person, "kalle", 25}.
break this module's atomicity guarantees as it can race with a
concurrent call to
<seealso marker="#write_file_info/2"><c>write_file_info/1,2</c>
- </seealso></p>
+ </seealso>.</p>
+ <p>This option has no effect when the function is
+ given an I/O device instead of a file name. Use
+ <seealso marker="#open/2"><c>open/2</c></seealso> with the
+ <c>raw</c> mode to obtain a file descriptor first.</p>
<note>
<p>As file times are stored in POSIX time on most OS, it is faster to
query file information with option <c>posix</c>.</p>
diff --git a/lib/kernel/doc/src/gen_udp.xml b/lib/kernel/doc/src/gen_udp.xml
index 6c0d072fed..9ecadc5498 100644
--- a/lib/kernel/doc/src/gen_udp.xml
+++ b/lib/kernel/doc/src/gen_udp.xml
@@ -213,7 +213,7 @@
</func>
<func>
- <name name="send" arity="3" since="OTP @OTP-15747@"/>
+ <name name="send" arity="3" since="OTP 22.1"/>
<fsummary>Send a packet.</fsummary>
<desc>
<p>
@@ -242,7 +242,7 @@
</func>
<func>
- <name name="send" arity="4" clause_i="2" anchor="send-4-AncData" since="OTP @OTP-15747@"/>
+ <name name="send" arity="4" clause_i="2" anchor="send-4-AncData" since="OTP 22.1"/>
<fsummary>Send a packet.</fsummary>
<desc>
<p>
@@ -265,7 +265,7 @@
</func>
<func>
- <name name="send" arity="4" clause_i="3" since="OTP @OTP-15747@"/>
+ <name name="send" arity="4" clause_i="3" since="OTP 22.1"/>
<fsummary>Send a packet.</fsummary>
<desc>
<p>
@@ -284,7 +284,7 @@
</func>
<func>
- <name name="send" arity="5" since="OTP @OTP-15747@"/>
+ <name name="send" arity="5" since="OTP 22.1"/>
<fsummary>Send a packet.</fsummary>
<desc>
<p>
@@ -305,4 +305,3 @@
</func>
</funcs>
</erlref>
-
diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml
index 1011befca0..e1a8ae567a 100644
--- a/lib/kernel/doc/src/inet.xml
+++ b/lib/kernel/doc/src/inet.xml
@@ -118,7 +118,7 @@ fe80::204:acff:fe17:bf38
<name name="port_number"/>
</datatype>
<datatype>
- <name name="family_address" since="OTP @OTP-15747@"/>
+ <name name="family_address" since="OTP 22.1"/>
<desc>
<p>
A general address format on the form <c>{Family, Destination}</c>
@@ -130,7 +130,7 @@ fe80::204:acff:fe17:bf38
</desc>
</datatype>
<datatype>
- <name name="inet_address" since="OTP @OTP-15747@"/>
+ <name name="inet_address" since="OTP 22.1"/>
<desc>
<warning>
<p>
@@ -142,7 +142,7 @@ fe80::204:acff:fe17:bf38
</desc>
</datatype>
<datatype>
- <name name="inet6_address" since="OTP @OTP-15747@"/>
+ <name name="inet6_address" since="OTP 22.1"/>
<desc>
<warning>
<p>
@@ -582,7 +582,7 @@ get_tcpi_sacked(Sock) ->
<p>Gets one or more statistic options for a socket.</p>
<p><c>getstat(<anno>Socket</anno>)</c> is equivalent to
<c>getstat(<anno>Socket</anno>, [recv_avg, recv_cnt, recv_dvi,
- recv_max, recv_oct, send_avg, send_cnt, send_dvi, send_max,
+ recv_max, recv_oct, send_avg, send_cnt, send_pend, send_max,
send_oct])</c>.</p>
<p>The following options are available:</p>
<taglist>
@@ -614,9 +614,9 @@ get_tcpi_sacked(Sock) ->
<item>
<p>Number of packets sent from the socket.</p>
</item>
- <tag><c>send_dvi</c></tag>
+ <tag><c>send_pend</c></tag>
<item>
- <p>Average packet size deviation, in bytes, sent from the socket.</p>
+ <p>Number of bytes waiting to be sent by the socket.</p>
</item>
<tag><c>send_max</c></tag>
<item>
diff --git a/lib/kernel/doc/src/logger_disk_log_h.xml b/lib/kernel/doc/src/logger_disk_log_h.xml
index aa577f3c62..3d8e82ce7c 100644
--- a/lib/kernel/doc/src/logger_disk_log_h.xml
+++ b/lib/kernel/doc/src/logger_disk_log_h.xml
@@ -133,7 +133,7 @@ logger:add_handler(my_disk_log_h, logger_disk_log_h,
#{config => #{file => "./my_disk_log",
type => wrap,
max_no_files => 4,
- max_no_bytes => 10000},
+ max_no_bytes => 10000,
filesync_repeat_interval => 1000}}).
</code>
<p>To use the disk_log handler instead of the default standard
diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml
index 4d31eeea3d..aea3787115 100644
--- a/lib/kernel/doc/src/notes.xml
+++ b/lib/kernel/doc/src/notes.xml
@@ -31,6 +31,101 @@
</header>
<p>This document describes the changes made to the Kernel application.</p>
+<section><title>Kernel 6.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ The type specification for <c>gen_sctp:connect/4,5</c>
+ has been corrected.</p>
+ <p>
+ Own Id: OTP-15344 Aux Id: ERL-947 </p>
+ </item>
+ <item>
+ <p>
+ Extra <c>-mode</c> flags given to <c>erl</c> are ignored
+ with a warning.</p>
+ <p>
+ Own Id: OTP-15852</p>
+ </item>
+ <item>
+ <p>
+ Fix type spec for <c>seq_trace:set_token/2</c>.</p>
+ <p>
+ Own Id: OTP-15858 Aux Id: ERL-700 </p>
+ </item>
+ <item>
+ <p>
+ <c>logger:compare_levels/2</c> would fail with a
+ <c>badarg</c> exception if given the values <c>all</c> or
+ <c>none</c> as any of the parameters. This is now
+ corrected.</p>
+ <p>
+ Own Id: OTP-15942 Aux Id: PR-2301 </p>
+ </item>
+ <item>
+ <p>
+ Fix bug where the log file in <c>logger_std_h</c> would
+ not be closed when the inode of the file changed. This
+ would in turn cause a file descriptor leak when tools
+ like logrotate are used.</p>
+ <p>
+ Own Id: OTP-15997 Aux Id: PR-2331 </p>
+ </item>
+ <item>
+ <p>
+ Fix a race condition in the debugging function
+ <c>net_kernel:nodes_info/0</c>.</p>
+ <p>
+ Own Id: OTP-16022</p>
+ </item>
+ <item>
+ <p>
+ Fix race condition when closing a file opened in
+ <c>compressed</c> or <c>delayed_write</c> mode.</p>
+ <p>
+ Own Id: OTP-16023</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ The possibility to send ancillary data, in particular the
+ TOS field, has been added to <c>gen_udp:send/4,5</c>.</p>
+ <p>
+ Own Id: OTP-15747 Aux Id: ERIERL-294 </p>
+ </item>
+ <item>
+ <p>
+ If the log file was given with relative path, the
+ standard logger handler (<c>logger_std_h</c>) would store
+ the file name with relative path. If the current
+ directory of the node was later changed, a new file would
+ be created relative the new current directory,
+ potentially failing with an <c>enoent</c> if the new
+ directory did not exist. This is now corrected and
+ <c>logger_std_h</c> always stores the log file name as an
+ absolute path, calculated from the current directory at
+ the time of the handler startup.</p>
+ <p>
+ Own Id: OTP-15850</p>
+ </item>
+ <item>
+ <p>
+ Support local sockets with inet:i/0.</p>
+ <p>
+ Own Id: OTP-15935 Aux Id: PR-2299 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Kernel 6.4.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -147,6 +242,24 @@
</section>
+<section><title>Kernel 6.3.1.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix bug where the log file in <c>logger_std_h</c> would
+ not be closed when the inode of the file changed. This
+ would in turn cause a file descriptor leak when tools
+ like logrotate are used.</p>
+ <p>
+ Own Id: OTP-15997 Aux Id: PR-2331 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Kernel 6.3.1.2</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/kernel/examples/gen_tcp_dist/src/gen_tcp_dist.erl b/lib/kernel/examples/gen_tcp_dist/src/gen_tcp_dist.erl
index cda4c470f9..bc2e07ddf5 100644
--- a/lib/kernel/examples/gen_tcp_dist/src/gen_tcp_dist.erl
+++ b/lib/kernel/examples/gen_tcp_dist/src/gen_tcp_dist.erl
@@ -1,8 +1,8 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2017. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2017-2019. All Rights Reserved.
+%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
@@ -14,7 +14,7 @@
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
-%%
+%%
%% %CopyrightEnd%
%%
-module(gen_tcp_dist).
@@ -30,12 +30,11 @@
%% VM.
%%
%% This code is a rewrite of the lib/kernel/src/inet_tcp_dist.erl
-%% distribution impementation for TCP used by default. That
-%% implementation use distribution ports instead of distribution
+%% distribution implementation for TCP used by default. The default
+%% implementation uses distribution ports instead of distribution
%% processes and is more efficient compared to this implementation.
-%% This since this implementation more or less gets the
-%% distribution processes in between the VM and the ports without
-%% any gain specific gain.
+%% This example more or less gets the distribution processes
+%% in between the VM and the ports without any specific gain.
%%
-export([listen/1, accept/1, accept_connection/5,
diff --git a/lib/kernel/src/Makefile b/lib/kernel/src/Makefile
index 88752431eb..2d2b84c206 100644
--- a/lib/kernel/src/Makefile
+++ b/lib/kernel/src/Makefile
@@ -67,6 +67,7 @@ MODULES = \
dist_ac \
dist_util \
erl_boot_server \
+ erl_compile_server \
erl_ddll \
erl_distribution \
erl_epmd \
diff --git a/lib/kernel/src/erl_compile_server.erl b/lib/kernel/src/erl_compile_server.erl
new file mode 100644
index 0000000000..f4b719068e
--- /dev/null
+++ b/lib/kernel/src/erl_compile_server.erl
@@ -0,0 +1,253 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2019. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(erl_compile_server).
+-behaviour(gen_server).
+-export([start_link/0, compile/1]).
+
+%% Internal exports
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2]).
+
+-define(COMPILE_SERVER, erl_compile_server).
+-define(IDLE_TIMEOUT, (10*1000)).
+-define(WRONG_TIMEOUT, 1).
+
+-type client() :: {pid(), term()}.
+-type job_map() :: #{{pid(),reference()} := client()}.
+
+-record(st, {
+ cwd=[] :: file:filename(),
+ config :: term(),
+ timeout=?IDLE_TIMEOUT :: non_neg_integer(),
+ jobs=#{} :: job_map()
+ }).
+
+-type state() :: #st{}.
+
+-spec start_link() -> {'ok', pid()} | {'error', term()}.
+
+start_link() ->
+ gen_server:start_link({local, ?COMPILE_SERVER}, ?MODULE, [], []).
+
+-spec init(Arg :: []) -> {'ok', any(), timeout()}.
+
+init([]) ->
+ %% We don't want the current directory in the code path.
+ %% Remove it.
+ Path = [D || D <- code:get_path(), D =/= "."],
+ true = code:set_path(Path),
+ Config = init_config(),
+ {ok, #st{config=Config}, ?IDLE_TIMEOUT}.
+
+-spec compile(term()) -> term().
+
+compile(Parameters) ->
+ gen_server:call(?COMPILE_SERVER, {compile, Parameters}, infinity).
+
+-spec handle_call(any(), _, state()) -> {'noreply', state()}.
+
+handle_call({compile, Parameters}, From, #st{jobs=Jobs}=St0) ->
+ {ErlcArgs, PathArgs} = parse_command_line(Parameters),
+ case verify_context(PathArgs, Parameters, St0) of
+ {ok, St1} ->
+ #{cwd := Cwd, encoding := Enc} = Parameters,
+ PidRef = spawn_monitor(fun() -> exit(do_compile(ErlcArgs, Cwd, Enc)) end),
+ St = St1#st{jobs=Jobs#{PidRef => From}},
+ {noreply, St#st{timeout=?IDLE_TIMEOUT}};
+ wrong_config ->
+ case map_size(Jobs) of
+ 0 ->
+ %% Wrong configuration and no outstanding jobs.
+ %% Terminate immediately.
+ halt();
+ _ ->
+ {reply, wrong_config, St0#st{timeout=?WRONG_TIMEOUT}, ?WRONG_TIMEOUT}
+ end
+ end.
+
+-spec handle_cast(term(), state()) -> {'noreply', state()}.
+
+handle_cast(_, St) ->
+ {noreply, St}.
+
+-spec handle_info(term(), state()) -> {'noreply', state(), timeout()}.
+
+handle_info({'DOWN',Ref,process,Pid,Reason}, #st{jobs=Jobs0}=St0) ->
+ Key = {Pid, Ref},
+ Client = map_get(Key, Jobs0),
+ Jobs = maps:remove(Key, Jobs0),
+ St = St0#st{jobs=Jobs},
+ gen_server:reply(Client, Reason),
+ case map_size(Jobs) =:= 0 of
+ true ->
+ {noreply, St, St#st.timeout};
+ false ->
+ {noreply, St}
+ end;
+handle_info(timeout, #st{jobs=Jobs}) when map_size(Jobs) =:= 0 ->
+ halt();
+handle_info(_, #st{timeout=Timeout}=St) ->
+ %% There are still outstanding jobs.
+ {noreply, St, Timeout}.
+
+%%%
+%%% Local functions.
+%%%
+
+verify_context(PathArgs, #{env := Env}=Parameters, St0) ->
+ case ensure_cwd(Parameters, St0) of
+ {ok, #st{config=Config}=St} ->
+ case make_config(PathArgs, Env) of
+ Config ->
+ {ok, St};
+ _ ->
+ wrong_config
+ end;
+ wrong_config ->
+ wrong_config
+ end.
+
+ensure_cwd(#{cwd := Cwd}, #st{cwd=Cwd}=St) ->
+ {ok, St};
+ensure_cwd(#{cwd := NewCwd}, #st{jobs=Jobs}=St) when map_size(Jobs) =:= 0 ->
+ ok = file:set_cwd(NewCwd),
+ {ok, St#st{cwd=NewCwd}};
+ensure_cwd(#{}, #st{}) ->
+ wrong_config.
+
+do_compile(ErlcArgs, Cwd, Enc) ->
+ GL = create_gl(),
+ group_leader(GL, self()),
+ Result = erl_compile:compile(ErlcArgs, Cwd),
+ StdOutput = ensure_enc(gl_get_output(GL), Enc),
+ case Result of
+ ok ->
+ {ok, StdOutput};
+ {error, StdErrorOutput0} ->
+ StdErrorOutput = ensure_enc(StdErrorOutput0, Enc),
+ {error, StdOutput, StdErrorOutput}
+ end.
+
+parse_command_line(#{command_line := CmdLine, cwd := Cwd}) ->
+ parse_command_line_1(CmdLine, Cwd, [], []).
+
+parse_command_line_1(["-pa", Pa|T], Cwd, PaAcc, PzAcc) ->
+ parse_command_line_1(T, Cwd, [Pa|PaAcc], PzAcc);
+parse_command_line_1(["-pz", Pz|T], Cwd, PaAcc, PzAcc) ->
+ parse_command_line_1(T, Cwd, PaAcc, [Pz|PzAcc]);
+parse_command_line_1(["-extra"|ErlcArgs], Cwd, PaAcc, PzAcc) ->
+ PaArgs = clean_path_args(lists:reverse(PaAcc), Cwd),
+ PzArgs = clean_path_args(lists:reverse(PzAcc), Cwd),
+ {ErlcArgs, [{pa, PaArgs}, {pz, PzArgs}]};
+parse_command_line_1([_|T], Cwd, PaAcc, PzAcc) ->
+ parse_command_line_1(T, Cwd, PaAcc, PzAcc).
+
+ensure_enc(Chars, latin1) ->
+ L = unicode:characters_to_list(Chars, unicode),
+ unicode:characters_to_binary(
+ [ case X of
+ High when High > 255 ->
+ ["\\x{", erlang:integer_to_list(X, 16), $}];
+ Low ->
+ Low
+ end || X <- L ], unicode, latin1);
+ensure_enc(Chars, _Enc) -> Chars.
+
+init_config() ->
+ EnvVars = ["ERL_AFLAGS", "ERL_FLAGS", "ERL_ZFLAGS",
+ "ERL_COMPILER_OPTIONS",
+ "ERL_LIBS",
+ "ERLC_CONFIGURATION"],
+ Env0 = [{Name, os:getenv(Name)} || Name <- EnvVars],
+ Env = [P || {_, Val}=P <- Env0, Val =/= false],
+ {ok, Cwd} = file:get_cwd(),
+ make_config([get_path_arg(pa, Cwd), get_path_arg(pz, Cwd)], Env).
+
+get_path_arg(PathArg, Cwd) ->
+ case init:get_argument(PathArg) of
+ error ->
+ {PathArg, []};
+ {ok, Paths0} ->
+ Paths1 = lists:append(Paths0),
+ Paths = clean_path_args(Paths1, Cwd),
+ {PathArg, Paths}
+ end.
+
+clean_path_args(PathArgs, Cwd) ->
+ [filename:absname(P, Cwd) || P <- PathArgs].
+
+make_config(PathArgs, Env0) ->
+ Env = lists:sort(Env0),
+ PathArgs ++ [iolist_to_binary([[Name,$=,Val,$\n] || {Name,Val} <- Env])].
+
+%%%
+%%% A group leader that will capture all output to the group leader.
+%%%
+
+create_gl() ->
+ spawn_link(fun() -> gl_loop([]) end).
+
+gl_get_output(GL) ->
+ GL ! {self(), get_output},
+ receive
+ {GL, Output} -> Output
+ end.
+
+gl_loop(State0) ->
+ receive
+ {io_request, From, ReplyAs, Request} ->
+ {_Tag, Reply, State} = gl_request(Request, State0),
+ gl_reply(From, ReplyAs, Reply),
+ gl_loop(State);
+ {From, get_output} ->
+ Output = iolist_to_binary(State0),
+ From ! {self(), Output},
+ gl_loop(State0);
+ _Unknown ->
+ gl_loop(State0)
+ end.
+
+gl_reply(From, ReplyAs, Reply) ->
+ From ! {io_reply, ReplyAs, Reply},
+ ok.
+
+gl_request({put_chars, Encoding, Chars}, State) ->
+ gl_put_chars(unicode:characters_to_binary(Chars, Encoding), State);
+gl_request({put_chars, Encoding, Module, Function, Args}, State) ->
+ try
+ gl_request({put_chars, Encoding, apply(Module, Function, Args)}, State)
+ catch
+ _:_ ->
+ {{error,Function}, State}
+ end;
+gl_request({requests, Reqs}, State) ->
+ gl_multi_request(Reqs, {ok, State});
+gl_request(_Other, State) ->
+ {error, {error, request}, State}.
+
+gl_multi_request([R|Rs], {ok, State}) ->
+ gl_multi_request(Rs, gl_request(R, State));
+gl_multi_request([_|_], Error) ->
+ Error;
+gl_multi_request([], Result) ->
+ Result.
+
+gl_put_chars(Chars, Output) ->
+ {ok, ok, [Output,Chars]}.
diff --git a/lib/kernel/src/erts_debug.erl b/lib/kernel/src/erts_debug.erl
index 42261d371d..7b9067d079 100644
--- a/lib/kernel/src/erts_debug.erl
+++ b/lib/kernel/src/erts_debug.erl
@@ -40,6 +40,15 @@
lc_graph/0, lc_graph_to_dot/2, lc_graph_merge/2,
alloc_blocks_size/1]).
+%% Reroutes calls to the given MFA to error_handler:breakpoint/3
+%%
+%% Note that this is potentially unsafe as compiled code may assume that the
+%% targeted function returns a specific type, triggering undefined behavior if
+%% this function were to return something else.
+%%
+%% For reference, the debugger avoids the issue by purging the affected module
+%% and interpreting all functions in the module, ensuring that no assumptions
+%% are made with regard to return or argument types.
-spec breakpoint(MFA, Flag) -> non_neg_integer() when
MFA :: {Module :: module(),
Function :: atom(),
diff --git a/lib/kernel/src/file.erl b/lib/kernel/src/file.erl
index a0616da670..cde03ce1c4 100644
--- a/lib/kernel/src/file.erl
+++ b/lib/kernel/src/file.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -239,20 +239,30 @@ make_dir(Name) ->
del_dir(Name) ->
check_and_call(del_dir, [file_name(Name)]).
--spec read_file_info(Filename) -> {ok, FileInfo} | {error, Reason} when
- Filename :: name_all(),
+-spec read_file_info(File) -> {ok, FileInfo} | {error, Reason} when
+ File :: name_all() | io_device(),
FileInfo :: file_info(),
Reason :: posix() | badarg.
+read_file_info(IoDevice)
+ when is_pid(IoDevice); is_record(IoDevice, file_descriptor) ->
+ read_file_info(IoDevice, []);
+
read_file_info(Name) ->
check_and_call(read_file_info, [file_name(Name)]).
--spec read_file_info(Filename, Opts) -> {ok, FileInfo} | {error, Reason} when
- Filename :: name_all(),
+-spec read_file_info(File, Opts) -> {ok, FileInfo} | {error, Reason} when
+ File :: name_all() | io_device(),
Opts :: [file_info_option()],
FileInfo :: file_info(),
Reason :: posix() | badarg.
+read_file_info(IoDevice, Opts) when is_pid(IoDevice), is_list(Opts) ->
+ file_request(IoDevice, {read_handle_info, Opts});
+
+read_file_info(#file_descriptor{module = Module} = Handle, Opts) when is_list(Opts) ->
+ Module:read_handle_info(Handle, Opts);
+
read_file_info(Name, Opts) when is_list(Opts) ->
Args = [file_name(Name), Opts],
case check_args(Args) of
@@ -545,7 +555,7 @@ allocate(#file_descriptor{module = Module} = Handle, Offset, Length) ->
| {no_translation, unicode, latin1}.
read(File, Sz) when (is_pid(File) orelse is_atom(File)), is_integer(Sz), Sz >= 0 ->
- case io:request(File, {get_chars, '', Sz}) of
+ case io:request(File, {get_chars, latin1, '', Sz}) of
Data when is_list(Data); is_binary(Data) ->
{ok, Data};
Other ->
@@ -566,7 +576,7 @@ read(_, _) ->
| {no_translation, unicode, latin1}.
read_line(File) when (is_pid(File) orelse is_atom(File)) ->
- case io:request(File, {get_line, ''}) of
+ case io:request(File, {get_line, latin1, ''}) of
Data when is_list(Data); is_binary(Data) ->
{ok, Data};
Other ->
diff --git a/lib/kernel/src/file_io_server.erl b/lib/kernel/src/file_io_server.erl
index 34d5497a4a..c03fbb548a 100644
--- a/lib/kernel/src/file_io_server.erl
+++ b/lib/kernel/src/file_io_server.erl
@@ -314,6 +314,14 @@ file_request(truncate,
Reply ->
std_reply(Reply, State)
end;
+file_request({read_handle_info, Opts},
+ #state{handle=Handle}=State) ->
+ case ?CALL_FD(Handle, read_handle_info, [Opts]) of
+ {error,Reason}=Reply ->
+ {stop,Reason,Reply,State};
+ Reply ->
+ {reply,Reply,State}
+ end;
file_request(Unknown,
#state{}=State) ->
Reason = {request, Unknown},
diff --git a/lib/kernel/src/group.erl b/lib/kernel/src/group.erl
index 5625ae6eb7..8c0ced8678 100644
--- a/lib/kernel/src/group.erl
+++ b/lib/kernel/src/group.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -795,12 +795,6 @@ save_line({stack, U, _L, D}, Line) ->
{stack, U, Line, D}.
get_lines(Ls) -> get_all_lines(Ls).
-%get_lines({stack, U, {}, []}) ->
-% U;
-%get_lines({stack, U, {}, D}) ->
-% tl(lists:reverse(D, U));
-%get_lines({stack, U, L, D}) ->
-% get_lines({stack, U, {}, [L|D]}).
%% There's a funny behaviour whenever the line stack doesn't have a "\n"
%% at its end -- get_lines() seemed to work on the assumption it *will* be
diff --git a/lib/kernel/src/group_history.erl b/lib/kernel/src/group_history.erl
index 9745848992..1fab2ba14e 100644
--- a/lib/kernel/src/group_history.erl
+++ b/lib/kernel/src/group_history.erl
@@ -44,7 +44,7 @@ load() ->
wait_for_kernel_safe_sup(),
case history_status() of
enabled ->
- case open_log() of
+ try open_log() of
{ok, ?LOG_NAME} ->
read_full_log(?LOG_NAME);
{repaired, ?LOG_NAME, {recovered, Good}, {badbytes, Bad}} ->
@@ -68,6 +68,10 @@ load() ->
handle_open_error(Reason),
disable_history(),
[]
+ catch
+ % disk_log shut down abruptly, possibly because
+ % the node is shutting down. Ignore it.
+ exit:_ -> []
end;
_ ->
[]
@@ -127,11 +131,15 @@ repair_log(Name) ->
%% Return whether the shell history is enabled or not
-spec history_status() -> enabled | disabled.
history_status() ->
- case is_user() orelse application:get_env(kernel, shell_history) of
- true -> disabled; % don't run for user proc
- {ok, enabled} -> enabled;
- undefined -> ?DEFAULT_STATUS;
- _ -> disabled
+ %% Don't run for user proc or if the emulator's tearing down
+ Skip = is_user() orelse not init_running(),
+ case application:get_env(kernel, shell_history) of
+ {ok, enabled} when not Skip ->
+ enabled;
+ undefined when not Skip ->
+ ?DEFAULT_STATUS;
+ _ ->
+ disabled
end.
%% Return whether the user process is running this
@@ -142,6 +150,14 @@ is_user() ->
_ -> false
end.
+%% Return if the system is running (not stopping)
+-spec init_running() -> boolean().
+init_running() ->
+ case init:get_status() of
+ {stopping, _} -> false;
+ _ -> true
+ end.
+
%% Open a disk_log file while ensuring the required path is there.
open_log() ->
Opts = log_options(),
@@ -322,7 +338,7 @@ show_unexpected_warning({M,F,A}, Term) ->
show_unexpected_close_warning() ->
show('$#erlang-history-unexpected-close',
- "The shell log file has mysteriousy closed. Ignoring "
+ "The shell log file has mysteriously closed. Ignoring "
"currently unread history.~n", []).
show_size_warning(_Current, _New) ->
diff --git a/lib/kernel/src/kernel.app.src b/lib/kernel/src/kernel.app.src
index c2ff6b63e9..4f5e6d782f 100644
--- a/lib/kernel/src/kernel.app.src
+++ b/lib/kernel/src/kernel.app.src
@@ -32,6 +32,7 @@
code_server,
dist_util,
erl_boot_server,
+ erl_compile_server,
erl_distribution,
erl_reply,
erl_signal_handler,
diff --git a/lib/kernel/src/kernel.appup.src b/lib/kernel/src/kernel.appup.src
index 95853a7a8f..d862b5491f 100644
--- a/lib/kernel/src/kernel.appup.src
+++ b/lib/kernel/src/kernel.appup.src
@@ -40,7 +40,8 @@
{<<"^6\\.3\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
{<<"^6\\.3\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
{<<"^6\\.4$">>,[restart_new_emulator]},
- {<<"^6\\.4\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}],
+ {<<"^6\\.4\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
+ {<<"^6\\.4\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}],
[{<<"^6\\.0$">>,[restart_new_emulator]},
{<<"^6\\.0\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
{<<"^6\\.0\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
@@ -54,4 +55,5 @@
{<<"^6\\.3\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
{<<"^6\\.3\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
{<<"^6\\.4$">>,[restart_new_emulator]},
- {<<"^6\\.4\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}]}.
+ {<<"^6\\.4\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
+ {<<"^6\\.4\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}]}.
diff --git a/lib/kernel/src/kernel.erl b/lib/kernel/src/kernel.erl
index c8c631ab23..8877ceea8e 100644
--- a/lib/kernel/src/kernel.erl
+++ b/lib/kernel/src/kernel.erl
@@ -166,10 +166,12 @@ init([]) ->
modules => dynamic},
Timer = start_timer(),
+ CompileServer = start_compile_server(),
{ok, {SupFlags,
[Code, InetDb | DistChildren] ++
- [File, SigSrv, StdError, User, Config, RefC, SafeSup, LoggerSup] ++ Timer}}
+ [File, SigSrv, StdError, User, Config, RefC, SafeSup, LoggerSup] ++
+ Timer ++ CompileServer}}
end;
init(safe) ->
SupFlags = #{strategy => one_for_one,
@@ -303,6 +305,19 @@ start_timer() ->
[]
end.
+start_compile_server() ->
+ case application:get_env(kernel, start_compile_server) of
+ {ok, true} ->
+ [#{id => erl_compile_server,
+ start => {erl_compile_server, start_link, []},
+ restart => permanent,
+ shutdown => 2000,
+ type => worker,
+ modules => [erl_compile_server]}];
+ _ ->
+ []
+ end.
+
%%-----------------------------------------------------------------
%% The change of the distributed parameter is taken care of here
%%-----------------------------------------------------------------
diff --git a/lib/kernel/src/logger.erl b/lib/kernel/src/logger.erl
index 38bd2f481c..fd02cf67bf 100644
--- a/lib/kernel/src/logger.erl
+++ b/lib/kernel/src/logger.erl
@@ -600,11 +600,11 @@ get_module_level() ->
%%%-----------------------------------------------------------------
%%% Misc
-spec compare_levels(Level1,Level2) -> eq | gt | lt when
- Level1 :: level(),
- Level2 :: level().
-compare_levels(Level,Level) when ?IS_LEVEL(Level) ->
+ Level1 :: level() | all | none,
+ Level2 :: level() | all | none.
+compare_levels(Level,Level) when ?IS_LEVEL_ALL(Level) ->
eq;
-compare_levels(Level1,Level2) when ?IS_LEVEL(Level1), ?IS_LEVEL(Level2) ->
+compare_levels(Level1,Level2) when ?IS_LEVEL_ALL(Level1), ?IS_LEVEL_ALL(Level2) ->
Int1 = logger_config:level_to_int(Level1),
Int2 = logger_config:level_to_int(Level2),
if Int1 < Int2 -> gt;
@@ -950,7 +950,7 @@ get_logger_type(Env) ->
get_logger_level() ->
case application:get_env(kernel,logger_level,info) of
- Level when ?IS_LEVEL(Level); Level=:=all; Level=:=none ->
+ Level when ?IS_LEVEL_ALL(Level) ->
Level;
Level ->
throw({logger_level, Level})
diff --git a/lib/kernel/src/logger_internal.hrl b/lib/kernel/src/logger_internal.hrl
index e53922e5d3..c2b2d419e7 100644
--- a/lib/kernel/src/logger_internal.hrl
+++ b/lib/kernel/src/logger_internal.hrl
@@ -86,7 +86,12 @@
L=:=warning orelse
L=:=notice orelse
L=:=info orelse
- L=:=debug)).
+ L=:=debug )).
+
+-define(IS_LEVEL_ALL(L),
+ ?IS_LEVEL(L) orelse
+ L=:=all orelse
+ L=:=none ).
-define(IS_MSG(Msg),
((is_tuple(Msg) andalso tuple_size(Msg)==2)
diff --git a/lib/kernel/src/logger_std_h.erl b/lib/kernel/src/logger_std_h.erl
index 2b078ef091..8477a0fc93 100644
--- a/lib/kernel/src/logger_std_h.erl
+++ b/lib/kernel/src/logger_std_h.erl
@@ -457,12 +457,12 @@ maybe_ensure_file(State) ->
%% In order to play well with tools like logrotate, we need to be able
%% to re-create the file if it has disappeared (e.g. if rotated by
%% logrotate)
-ensure_file(#{fd:=Fd0,inode:=INode0,file_name:=FileName,modes:=Modes}=State) ->
+ensure_file(#{inode:=INode0,file_name:=FileName,modes:=Modes}=State) ->
case file:read_file_info(FileName,[raw]) of
{ok,#file_info{inode=INode0}} ->
State#{last_check=>timestamp()};
_ ->
- close_log_file(Fd0),
+ close_log_file(State),
case file:open(FileName,Modes) of
{ok,Fd} ->
{ok,#file_info{inode=INode}} =
diff --git a/lib/kernel/src/net_kernel.erl b/lib/kernel/src/net_kernel.erl
index 83d3b4b5e1..e333bcd307 100644
--- a/lib/kernel/src/net_kernel.erl
+++ b/lib/kernel/src/net_kernel.erl
@@ -135,7 +135,6 @@
node %% remote node name
}).
-
-record(tick, {ticker, %% ticker : pid()
time %% Ticktime in milli seconds : integer()
}).
@@ -213,7 +212,6 @@ set_net_ticktime(T) when is_integer(T) ->
get_net_ticktime() ->
ticktime_res(request(ticktime)).
-
%% The monitor_nodes() feature has been moved into the emulator.
%% The feature is reached via (intentionally) undocumented process
%% flags (we may want to move it elsewhere later). In order to easily
@@ -274,7 +272,6 @@ connect_node(Node) when is_atom(Node) ->
hidden_connect_node(Node) when is_atom(Node) ->
request({connect, hidden, Node}).
-
passive_connect_monitor(From, Node) ->
ok = monitor_nodes(true,[{node_type,all}]),
Reply = case lists:member(Node,nodes([connected])) of
@@ -292,7 +289,6 @@ passive_connect_monitor(From, Node) ->
{Pid, Tag} = From,
erlang:send(Pid, {Tag, Reply}).
-
%% If the net_kernel isn't running we ignore all requests to the
%% kernel, thus basically accepting them :-)
request(Req) ->
@@ -367,7 +363,7 @@ do_auto_connect_1(Node, ConnId, From, State) ->
ConnLookup ->
do_auto_connect_2(Node, ConnId, From, State, ConnLookup)
end.
-
+
do_auto_connect_2(Node, passive_cnct, From, State, ConnLookup) ->
try erts_internal:new_connection(Node) of
ConnId ->
@@ -393,7 +389,7 @@ do_auto_connect_2(Node, ConnId, From, State, ConnLookup) ->
case application:get_env(kernel, dist_auto_connect) of
{ok, never} ->
?connect_failure(Node,{dist_auto_connect,never}),
- erts_internal:abort_connection(Node, ConnId),
+ erts_internal:abort_connection(Node, ConnId),
{reply, false, State};
%% This might happen due to connection close
@@ -418,14 +414,13 @@ do_auto_connect_2(Node, ConnId, From, State, ConnLookup) ->
end
end.
-
do_explicit_connect([#connection{conn_id = ConnId, state = up}], _, _, ConnId, _From, State) ->
{reply, true, State};
do_explicit_connect([#connection{conn_id = ConnId}=Conn], _, _, ConnId, From, State)
when Conn#connection.state =:= pending;
Conn#connection.state =:= up_pending ->
Waiting = Conn#connection.waiting,
- ets:insert(sys_dist, Conn#connection{waiting = [From|Waiting]}),
+ ets:insert(sys_dist, Conn#connection{waiting = [From|Waiting]}),
{noreply, State};
do_explicit_connect([#barred_connection{}], Type, Node, ConnId, From , State) ->
%% Barred connection only affects auto_connect, ignore it.
@@ -440,7 +435,6 @@ do_explicit_connect(_ConnLookup, Type, Node, ConnId, From , State) ->
{reply, false, State}
end.
-
%% ------------------------------------------------------------
%% handle_call.
%% ------------------------------------------------------------
@@ -477,15 +471,13 @@ handle_call({connect, Type, Node}, From, State) ->
erts_internal:abort_connection(Node, ConnId)
end,
R1
-
catch
_:_ ->
error_logger:error_msg("~n** Cannot get connection id for node ~w~n",
[Node]),
- {reply, false, State}
+ {reply, false, State}
end,
return_call(R, From);
-
%%
%% Close the connection to Node.
@@ -570,7 +562,6 @@ handle_call({publish_on_node, Node}, From, State) ->
end,
async_reply({reply, Publish, NewState}, From);
-
handle_call({verbose, Level}, From, State) ->
async_reply({reply, State#state.verbose, State#state{verbose = Level}},
From);
@@ -670,25 +661,16 @@ code_change(_OldVsn, State, _Extra) ->
terminate(no_network, State) ->
lists:foreach(
- fun({Node, Type}) ->
- case Type of
- normal -> ?nodedown(Node, State);
- _ -> ok
- end
- end, get_up_nodes() ++ [{node(), normal}]);
+ fun(Node) -> ?nodedown(Node, State)
+ end, get_nodes_up_normal() ++ [node()]);
terminate(_Reason, State) ->
lists:foreach(
fun(#listen {listen = Listen,module = Mod}) ->
Mod:close(Listen)
end, State#state.listen),
lists:foreach(
- fun({Node, Type}) ->
- case Type of
- normal -> ?nodedown(Node, State);
- _ -> ok
- end
- end, get_up_nodes() ++ [{node(), normal}]).
-
+ fun(Node) -> ?nodedown(Node, State)
+ end, get_nodes_up_normal() ++ [node()]).
%% ------------------------------------------------------------
%% handle_info.
@@ -818,7 +800,6 @@ handle_info({SetupPid, {is_pending, Node}}, State) ->
SetupPid ! {self(), {is_pending, Reply}},
{noreply, State};
-
%%
%% Handle different types of process terminations.
%%
@@ -1020,7 +1001,6 @@ up_pending_nodedown(Conn, Node, _Reason, _Type, State) ->
AcceptPid ! {self(), pending},
State#state{conn_owners = [{AcceptPid,Node}|Owners], pend_owners = Pend}.
-
up_nodedown(Conn, Node, _Reason, Type, State) ->
mark_sys_dist_nodedown(Conn, Node),
case Type of
@@ -1042,7 +1022,6 @@ mark_sys_dist_nodedown(Conn, Node) ->
%% End handle_exit/2 !!
%% -----------------------------------------------------------
-
%% -----------------------------------------------------------
%% monitor_nodes/[1,2] errors
%% -----------------------------------------------------------
@@ -1147,35 +1126,10 @@ disconnect_pid(Pid, State) ->
%%
%%
%%
-get_nodes(Which) ->
- get_nodes(ets:first(sys_dist), Which).
-get_nodes('$end_of_table', _) ->
- [];
-get_nodes(Key, Which) ->
- case ets:lookup(sys_dist, Key) of
- [Conn = #connection{state = up}] ->
- [Conn#connection.node | get_nodes(ets:next(sys_dist, Key),
- Which)];
- [Conn = #connection{}] when Which =:= all ->
- [Conn#connection.node | get_nodes(ets:next(sys_dist, Key),
- Which)];
- _ ->
- get_nodes(ets:next(sys_dist, Key), Which)
- end.
-
-%% Return a list of all nodes that are 'up'.
-get_up_nodes() ->
- get_up_nodes(ets:first(sys_dist)).
-
-get_up_nodes('$end_of_table') -> [];
-get_up_nodes(Key) ->
- case ets:lookup(sys_dist, Key) of
- [#connection{state=up,node=Node,type=Type}] ->
- [{Node,Type}|get_up_nodes(ets:next(sys_dist, Key))];
- _ ->
- get_up_nodes(ets:next(sys_dist, Key))
- end.
+%% Return a list of all nodes that are 'up' and not hidden.
+get_nodes_up_normal() ->
+ ets:select(sys_dist, [{#connection{node = '$1', state = up, type = normal, _ = '_'}, [], ['$1']}]).
ticker(Kernel, Tick) when is_integer(Tick) ->
process_flag(priority, max),
@@ -1312,7 +1266,7 @@ setup(Node, ConnId, Type, From, State) ->
end.
setup_check(Node, State) ->
- Allowed = State#state.allowed,
+ Allowed = State#state.allowed,
case lists:member(Node, Allowed) of
false when Allowed =/= [] ->
error_msg("** Connection attempt with "
@@ -1323,9 +1277,7 @@ setup_check(Node, State) ->
{ok, _L}=OK -> OK;
Error -> Error
end
- end.
-
-
+ end.
%%
%% Find a module that is willing to handle connection setup to Node
@@ -1339,7 +1291,6 @@ select_mod(Node, [L|Ls]) ->
select_mod(Node, []) ->
{error, {unsupported_address_type, Node}}.
-
get_proto_mod(Family,Protocol,[L|Ls]) ->
A = L#listen.address,
if A#net_address.family =:= Family,
@@ -1365,7 +1316,7 @@ init_node(Name, LongOrShortNames, CleanHalt) ->
Error
end;
Error ->
- Error
+ Error
end.
%% Create the node name
@@ -1474,7 +1425,6 @@ protocol_childspecs([H|T]) ->
protocol_childspecs(T)
end.
-
%%
%% epmd_module() -> module_name of erl_epmd or similar gen_server_module.
%%
@@ -1640,15 +1590,14 @@ get_node_info(Node, Key) ->
end.
get_nodes_info() ->
- get_nodes_info(get_nodes(all), []).
-
-get_nodes_info([Node|Nodes], InfoList) ->
- case get_node_info(Node) of
- {ok, Info} -> get_nodes_info(Nodes, [{Node, Info}|InfoList]);
- _ -> get_nodes_info(Nodes, InfoList)
- end;
-get_nodes_info([], InfoList) ->
- {ok, InfoList}.
+ Nodes = ets:select(sys_dist, [{#connection{node = '$1', _ = '_'}, [], ['$1']}]),
+ {ok, lists:filtermap(
+ fun(Node) ->
+ case get_node_info(Node) of
+ {ok, Info} -> {true, {Node, Info}};
+ _ -> false
+ end
+ end, Nodes)}.
%% ------------------------------------------------------------
%% Misc. functions
@@ -1669,7 +1618,6 @@ reply_waiting1([From|W], Rep) ->
reply_waiting1([], _) ->
ok.
-
-ifdef(UNUSED).
delete_all(From, [From |Tail]) -> delete_all(From, Tail);
@@ -1730,7 +1678,6 @@ fmt_address(A) ->
lists:flatten(io_lib:format("~p", [A#net_address.address]))
end.
-
fetch(Key, Info) ->
case lists:keysearch(Key, 1, Info) of
{value, {_, Val}} -> Val;
@@ -1802,7 +1749,6 @@ call_owner(Owner, Msg) ->
error
end.
-
-spec setopts(Node, Options) -> ok | {error, Reason} | ignored when
Node :: node() | new,
Options :: [inet:socket_setopt()],
diff --git a/lib/kernel/src/raw_file_io_compressed.erl b/lib/kernel/src/raw_file_io_compressed.erl
index d5ab042d25..1a6de3a4eb 100644
--- a/lib/kernel/src/raw_file_io_compressed.erl
+++ b/lib/kernel/src/raw_file_io_compressed.erl
@@ -21,7 +21,8 @@
-export([close/1, sync/1, datasync/1, truncate/1, advise/4, allocate/3,
position/2, write/2, pwrite/2, pwrite/3,
- read_line/1, read/2, pread/2, pread/3]).
+ read_line/1, read/2, pread/2, pread/3,
+ read_handle_info/2]).
%% OTP internal.
-export([ipread_s32bu_p32bu/3, sendfile/8]).
@@ -118,11 +119,15 @@ ipread_s32bu_p32bu(Fd, Offset, MaxSize) ->
sendfile(_,_,_,_,_,_,_,_) ->
{error, enotsup}.
+read_handle_info(Fd, Opts) ->
+ wrap_call(Fd, [Opts]).
+
wrap_call(Fd, Command) ->
{_Owner, Pid} = get_fd_data(Fd),
try gen_statem:call(Pid, Command, infinity) of
Result -> Result
catch
+ exit:{normal, _StackTrace} -> {error, einval};
exit:{noproc, _StackTrace} -> {error, einval}
end.
diff --git a/lib/kernel/src/raw_file_io_delayed.erl b/lib/kernel/src/raw_file_io_delayed.erl
index d2ad7550a1..f9b89270f1 100644
--- a/lib/kernel/src/raw_file_io_delayed.erl
+++ b/lib/kernel/src/raw_file_io_delayed.erl
@@ -23,7 +23,8 @@
-export([close/1, sync/1, datasync/1, truncate/1, advise/4, allocate/3,
position/2, write/2, pwrite/2, pwrite/3,
- read_line/1, read/2, pread/2, pread/3]).
+ read_line/1, read/2, pread/2, pread/3,
+ read_handle_info/2]).
%% OTP internal.
-export([ipread_s32bu_p32bu/3, sendfile/8]).
@@ -304,11 +305,15 @@ ipread_s32bu_p32bu(Fd, Offset, MaxSize) ->
sendfile(_,_,_,_,_,_,_,_) ->
{error, enotsup}.
+read_handle_info(Fd, Opts) ->
+ wrap_call(Fd, [Opts]).
+
wrap_call(Fd, Command) ->
#{ pid := Pid } = get_fd_data(Fd),
try gen_statem:call(Pid, Command, infinity) of
Result -> Result
catch
+ exit:{normal, _StackTrace} -> {error, einval};
exit:{noproc, _StackTrace} -> {error, einval}
end.
diff --git a/lib/kernel/src/raw_file_io_list.erl b/lib/kernel/src/raw_file_io_list.erl
index 2e16e63f0e..e4fe434e13 100644
--- a/lib/kernel/src/raw_file_io_list.erl
+++ b/lib/kernel/src/raw_file_io_list.erl
@@ -21,7 +21,8 @@
-export([close/1, sync/1, datasync/1, truncate/1, advise/4, allocate/3,
position/2, write/2, pwrite/2, pwrite/3,
- read_line/1, read/2, pread/2, pread/3]).
+ read_line/1, read/2, pread/2, pread/3,
+ read_handle_info/2]).
%% OTP internal.
-export([ipread_s32bu_p32bu/3, sendfile/8]).
@@ -126,3 +127,7 @@ sendfile(Fd, Dest, Offset, Bytes, ChunkSize, Headers, Trailers, Flags) ->
Args = [Dest, Offset, Bytes, ChunkSize, Headers, Trailers, Flags],
PrivateFd = Fd#file_descriptor.data,
?CALL_FD(PrivateFd, sendfile, Args).
+
+read_handle_info(Fd, Opts) ->
+ PrivateFd = Fd#file_descriptor.data,
+ ?CALL_FD(PrivateFd, read_handle_info, [Opts]).
diff --git a/lib/kernel/src/user.erl b/lib/kernel/src/user.erl
index 5a3487a9ba..81520dd841 100644
--- a/lib/kernel/src/user.erl
+++ b/lib/kernel/src/user.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -537,54 +537,6 @@ get_line_doit(Prompt, Port, Q, Accu, Enc) ->
binrev(L, T) ->
list_to_binary(lists:reverse(L, T)).
-%% is_cr_at(Pos,Bin) ->
-%% case Bin of
-%% <<_:Pos/binary,$\r,_/binary>> ->
-%% true;
-%% _ ->
-%% false
-%% end.
-
-%% collect_line_bin_re(Bin,_Data,Stack,_) ->
-%% case re:run(Bin,<<"\n">>) of
-%% nomatch ->
-%% X = byte_size(Bin)-1,
-%% case is_cr_at(X,Bin) of
-%% true ->
-%% <<D:X/binary,_/binary>> = Bin,
-%% [<<$\r>>,D|Stack];
-%% false ->
-%% [Bin|Stack]
-%% end;
-%% {match,[{Pos,1}]} ->
-%% PosPlus = Pos + 1,
-%% case Stack of
-%% [] ->
-%% case is_cr_at(Pos - 1,Bin) of
-%% false ->
-%% <<Head:PosPlus/binary,Tail/binary>> = Bin,
-%% {stop, Head, Tail};
-%% true ->
-%% PosMinus = Pos - 1,
-%% <<Head:PosMinus/binary,_,_,Tail/binary>> = Bin,
-%% {stop, binrev([],[Head,$\n]),Tail}
-%% end;
-%% [<<$\r>>|Stack1] when Pos =:= 0 ->
-
-%% <<_:PosPlus/binary,Tail/binary>> = Bin,
-%% {stop,binrev(Stack1, [$\n]),Tail};
-%% _ ->
-%% case is_cr_at(Pos - 1,Bin) of
-%% false ->
-%% <<Head:PosPlus/binary,Tail/binary>> = Bin,
-%% {stop,binrev(Stack, [Head]),Tail};
-%% true ->
-%% PosMinus = Pos - 1,
-%% <<Head:PosMinus/binary,_,_,Tail/binary>> = Bin,
-%% {stop, binrev(Stack,[Head,$\n]),Tail}
-%% end
-%% end
-%% end.
%% get_chars(Prompt, Module, Function, XtraArg, Port, Queue, Encoding)
%% Gets characters from the input port until the applied function
%% returns {stop,Result,RestBuf}. Does not block output until input
@@ -618,9 +570,6 @@ get_chars(Prompt, M, F, Xa, Port, Q, State, Enc) ->
{Port, eof} ->
put(eof, true),
{ok, eof, queue:new()};
- %%{io_request,From,ReplyAs,Request} when is_pid(From) ->
- %% get_chars_req(Prompt, M, F, Xa, Port, queue:new(), State,
- %% Request, From, ReplyAs);
{io_request,From,ReplyAs,{get_geometry,_}=Req} when is_pid(From) ->
do_io_request(Req, From, ReplyAs, Port,
queue:new()), %Keep Q over this call
diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl
index 21aaefa654..747f1d9e1b 100644
--- a/lib/kernel/test/file_SUITE.erl
+++ b/lib/kernel/test/file_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -58,6 +58,8 @@
-export([ file_info_basic_file/1, file_info_basic_directory/1,
file_info_bad/1, file_info_times/1, file_write_file_info/1,
file_wfi_helpers/1]).
+-export([ file_handle_info_basic_file/1, file_handle_info_basic_directory/1,
+ file_handle_info_times/1]).
-export([rename/1, access/1, truncate/1, datasync/1, sync/1,
read_write/1, pread_write/1, append/1, exclusive/1]).
-export([ e_delete/1, e_rename/1, e_make_dir/1, e_del_dir/1]).
@@ -153,7 +155,10 @@ groups() ->
{pos, [], [pos1, pos2, pos3]},
{file_info, [],
[file_info_basic_file, file_info_basic_directory,
- file_info_bad, file_info_times, file_write_file_info,
+ file_info_bad, file_info_times,
+ file_handle_info_basic_file, file_handle_info_basic_directory,
+ file_handle_info_times,
+ file_write_file_info,
file_wfi_helpers]},
{consult, [], [consult1, path_consult]},
{eval, [], [eval1, path_eval]},
@@ -273,11 +278,11 @@ mini_server(Parent) ->
Parent ! {io_request,From,To,{put_chars,Data}},
From ! {io_reply, To, ok},
mini_server(Parent);
- {io_request,From,To,{get_chars,'',N}} ->
+ {io_request,From,To,{get_chars,_Encoding,'',N}} ->
Parent ! {io_request,From,To,{get_chars,'',N}},
From ! {io_reply, To, {ok, lists:duplicate(N,$a)}},
mini_server(Parent);
- {io_request,From,To,{get_line,''}} ->
+ {io_request,From,To,{get_line,_Encoding,''}} ->
Parent ! {io_request,From,To,{get_line,''}},
From ! {io_reply, To, {ok, "hej\n"}},
mini_server(Parent)
@@ -1417,7 +1422,8 @@ file_info_basic_directory(Config) when is_list(Config) ->
{win32, _} ->
test_directory("/", read_write),
test_directory("c:/", read_write),
- test_directory("c:\\", read_write);
+ test_directory("c:\\", read_write),
+ test_directory("\\\\localhost\\c$", read_write);
_ ->
test_directory("/", read)
end,
@@ -1549,6 +1555,180 @@ filter_atime(Atime, Config) ->
Atime
end.
+%% Test read_file_info on I/O devices.
+
+file_handle_info_basic_file(Config) when is_list(Config) ->
+ RootDir = proplists:get_value(priv_dir, Config),
+
+ %% Create a short file.
+ Name = filename:join(RootDir,
+ atom_to_list(?MODULE)
+ ++"_basic_test.fil"),
+ {ok,Fd1} = ?FILE_MODULE:open(Name, write),
+ io:put_chars(Fd1, "foo bar"),
+ ok = ?FILE_MODULE:close(Fd1),
+
+ %% Test that the file has the expected attributes.
+ %% The times are tricky, so we will save them to a separate test case.
+
+ {ok, Fd} = ?FILE_MODULE:open(Name, read),
+ {ok,FileInfo} = ?FILE_MODULE:read_file_info(Fd),
+ ok = ?FILE_MODULE:close(Fd),
+
+ {ok, FdRaw} = ?FILE_MODULE:open(Name, [read, raw]),
+ {ok,FileInfoRaw} = ?FILE_MODULE:read_file_info(FdRaw),
+ ok = ?FILE_MODULE:close(FdRaw),
+
+ #file_info{size=Size,type=Type,access=Access,
+ atime=AccessTime,mtime=ModifyTime} = FileInfo = FileInfoRaw,
+ io:format("Access ~p, Modify ~p", [AccessTime, ModifyTime]),
+ Size = 7,
+ Type = regular,
+ read_write = Access,
+ true = abs(time_dist(filter_atime(AccessTime, Config),
+ filter_atime(ModifyTime,
+ Config))) < 5,
+ all_integers(tuple_to_list(AccessTime) ++ tuple_to_list(ModifyTime)),
+
+ [] = flush(),
+ ok.
+
+file_handle_info_basic_directory(Config) when is_list(Config) ->
+ %% Note: filename:join/1 removes any trailing slash,
+ %% which is essential for ?FILE_MODULE:file_info/1 to work on
+ %% platforms such as Windows95.
+ RootDir = filename:join([proplists:get_value(priv_dir, Config)]),
+
+ %% Test that the RootDir directory has the expected attributes.
+ test_directory_handle(RootDir, read_write),
+
+ %% Note that on Windows file systems,
+ %% "/" or "c:/" are *NOT* directories.
+ %% Therefore, test that ?FILE_MODULE:file_info/1 behaves as if they were
+ %% directories.
+ case os:type() of
+ {win32, _} ->
+ test_directory_handle("/", read_write),
+ test_directory_handle("c:/", read_write),
+ test_directory_handle("c:\\", read_write),
+ test_directory_handle("\\\\localhost\\c$", read_write);
+ _ ->
+ test_directory_handle("/", read)
+ end,
+ ok.
+
+test_directory_handle(Name, ExpectedAccess) ->
+ {ok, DirFd} = file:open(Name, [read, directory]),
+ try
+ {ok,FileInfo} = ?FILE_MODULE:read_file_info(DirFd),
+ {ok,FileInfo} = ?FILE_MODULE:read_file_info(DirFd, [raw]),
+ #file_info{size=Size,type=Type,access=Access,
+ atime=AccessTime,mtime=ModifyTime} = FileInfo,
+ io:format("Testing directory ~s", [Name]),
+ io:format("Directory size is ~p", [Size]),
+ io:format("Access ~p", [Access]),
+ io:format("Access time ~p; Modify time~p",
+ [AccessTime, ModifyTime]),
+ Type = directory,
+ Access = ExpectedAccess,
+ all_integers(tuple_to_list(AccessTime) ++ tuple_to_list(ModifyTime)),
+ [] = flush(),
+ ok
+ after
+ file:close(DirFd)
+ end.
+
+%% Test that the file times behave as they should.
+
+file_handle_info_times(Config) when is_list(Config) ->
+ %% We have to try this twice, since if the test runs across the change
+ %% of a month the time diff calculations will fail. But it won't happen
+ %% if you run it twice in succession.
+ test_server:m_out_of_n(
+ 1,2,
+ fun() -> file_handle_info_int(Config) end),
+ ok.
+
+file_handle_info_int(Config) ->
+ %% Note: filename:join/1 removes any trailing slash,
+ %% which is essential for ?FILE_MODULE:file_info/1 to work on
+ %% platforms such as Windows95.
+
+ RootDir = filename:join([proplists:get_value(priv_dir, Config)]),
+ io:format("RootDir = ~p", [RootDir]),
+
+ Name = filename:join(RootDir,
+ atom_to_list(?MODULE)
+ ++"_file_info.fil"),
+ {ok,Fd1} = ?FILE_MODULE:open(Name, write),
+ io:put_chars(Fd1,"foo"),
+ {ok,FileInfo1} = ?FILE_MODULE:read_file_info(Fd1),
+ ok = ?FILE_MODULE:close(Fd1),
+
+ {ok,Fd1Raw} = ?FILE_MODULE:open(Name, [read, raw]),
+ {ok,FileInfo1Raw} = ?FILE_MODULE:read_file_info(Fd1Raw),
+ ok = ?FILE_MODULE:close(Fd1Raw),
+
+ %% We assert that everything but the size is the same, on some OSs the
+ %% size may not have been flushed to disc and we do not want to do a
+ %% sync to force it.
+ FileInfo1Raw = FileInfo1#file_info{ size = FileInfo1Raw#file_info.size },
+
+ #file_info{type=regular,atime=AccTime1,mtime=ModTime1} = FileInfo1,
+
+ Now = erlang:localtime(), %???
+ io:format("Now ~p",[Now]),
+ io:format("Open file Acc ~p Mod ~p",[AccTime1,ModTime1]),
+ true = abs(time_dist(filter_atime(Now, Config),
+ filter_atime(AccTime1,
+ Config))) < 8,
+ true = abs(time_dist(Now,ModTime1)) < 8,
+
+ %% Sleep until we can be sure the seconds value has changed.
+ %% Note: FAT-based filesystem (like on Windows 95) have
+ %% a resolution of 2 seconds.
+ timer:sleep(2200),
+
+ %% close the file, and watch the modify date change
+
+ {ok,Fd2} = ?FILE_MODULE:open(Name, read),
+ {ok,FileInfo2} = ?FILE_MODULE:read_file_info(Fd2),
+ ok = ?FILE_MODULE:close(Fd2),
+
+ {ok,Fd2Raw} = ?FILE_MODULE:open(Name, [read, raw]),
+ {ok,FileInfo2Raw} = ?FILE_MODULE:read_file_info(Fd2Raw),
+ ok = ?FILE_MODULE:close(Fd2Raw),
+
+ #file_info{size=Size,type=regular,access=Access,
+ atime=AccTime2,mtime=ModTime2} = FileInfo2 = FileInfo2Raw,
+ io:format("Closed file Acc ~p Mod ~p",[AccTime2,ModTime2]),
+ true = time_dist(ModTime1,ModTime2) >= 0,
+
+ %% this file is supposed to be binary, so it'd better keep it's size
+ Size = 3,
+ Access = read_write,
+
+ %% Do some directory checking
+
+ {ok,Fd3} = ?FILE_MODULE:open(RootDir, [read, directory]),
+ {ok,FileInfo3} = ?FILE_MODULE:read_file_info(Fd3),
+ ok = ?FILE_MODULE:close(Fd3),
+
+ {ok,Fd3Raw} = ?FILE_MODULE:open(RootDir, [read, directory, raw]),
+ {ok,FileInfo3Raw} = ?FILE_MODULE:read_file_info(Fd3Raw),
+ ok = ?FILE_MODULE:close(Fd3Raw),
+
+ #file_info{size=DSize,type=directory,access=DAccess,
+ atime=AccTime3,mtime=ModTime3} = FileInfo3 = FileInfo3Raw,
+ %% this dir was modified only a few secs ago
+ io:format("Dir Acc ~p; Mod ~p; Now ~p", [AccTime3, ModTime3, Now]),
+ true = abs(time_dist(Now,ModTime3)) < 5,
+ DAccess = read_write,
+ io:format("Dir size is ~p",[DSize]),
+
+ [] = flush(),
+ ok.
+
%% Test the write_file_info/2 function.
file_write_file_info(Config) when is_list(Config) ->
diff --git a/lib/kernel/test/gen_tcp_misc_SUITE.erl b/lib/kernel/test/gen_tcp_misc_SUITE.erl
index de87bd9472..cd1bc2e0d1 100644
--- a/lib/kernel/test/gen_tcp_misc_SUITE.erl
+++ b/lib/kernel/test/gen_tcp_misc_SUITE.erl
@@ -3545,28 +3545,33 @@ wait(Mref) ->
%% OTP-15536
%% Test that send error works correctly for delay_send
delay_send_error(_Config) ->
- {ok, LS} = gen_tcp:listen(0, [{reuseaddr, true}, {packet, 1}, {active, false}]),
- {ok,{{0,0,0,0},PortNum}}=inet:sockname(LS),
- P = spawn_link(
- fun() ->
- {ok, S} = gen_tcp:accept(LS),
- receive die -> gen_tcp:close(S) end
- end),
- erlang:monitor(process, P),
- {ok, S} = gen_tcp:connect("localhost", PortNum,
- [{packet, 1}, {active, false}, {delay_send, true}]),
-
+ {ok, L} =
+ gen_tcp:listen(
+ 0, [{reuseaddr, true}, {packet, 1}, {active, false}]),
+ {ok,{{0,0,0,0},PortNum}}=inet:sockname(L),
+ {ok, C} =
+ gen_tcp:connect(
+ "localhost", PortNum,
+ [{packet, 1}, {active, false}, {delay_send, true}]),
+ {ok, S} = gen_tcp:accept(L),
%% Do a couple of sends first to see that it works
- ok = gen_tcp:send(S, "hello"),
- ok = gen_tcp:send(S, "hello"),
- ok = gen_tcp:send(S, "hello"),
-
- %% Make the receiver close
- P ! die,
- receive _Down -> ok end,
-
- ok = gen_tcp:send(S, "hello"),
- timer:sleep(500), %% Sleep in order for delay_send to have time to trigger
-
- %% This used to result in a double free
- {error, closed} = gen_tcp:send(S, "hello").
+ ok = gen_tcp:send(C, "hello"),
+ ok = gen_tcp:send(C, "hello"),
+ ok = gen_tcp:send(C, "hello"),
+ %% Close the receiver
+ ok = gen_tcp:close(S),
+ %%
+ case gen_tcp:send(C, "hello") of
+ ok ->
+ case gen_tcp:send(C, "hello") of
+ ok ->
+ timer:sleep(1000), %% Sleep in order for delay_send to have time to trigger
+ %% This used to result in a double free
+ {error, closed} = gen_tcp:send(C, "hello");
+ {error, closed} ->
+ ok
+ end;
+ {error, closed} ->
+ ok
+ end,
+ ok = gen_tcp:close(C).
diff --git a/lib/kernel/test/global_SUITE.erl b/lib/kernel/test/global_SUITE.erl
index 8eab36e308..5bff9cc292 100644
--- a/lib/kernel/test/global_SUITE.erl
+++ b/lib/kernel/test/global_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2018. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -2512,8 +2512,10 @@ re_register_name(Config) when is_list(Config) ->
Me = self(),
Pid1 = spawn(fun() -> proc(Me) end),
yes = global:register_name(name, Pid1),
+ wait_for_monitor(Pid1),
Pid2 = spawn(fun() -> proc(Me) end),
_ = global:re_register_name(name, Pid2),
+ wait_for_monitor(Pid2),
Pid2 ! die,
Pid1 ! die,
receive {Pid1, MonitoredBy1} -> [] = MonitoredBy1 end,
@@ -2522,6 +2524,15 @@ re_register_name(Config) when is_list(Config) ->
init_condition(Config),
ok.
+wait_for_monitor(Pid) ->
+ case process_info(Pid, monitored_by) of
+ {monitored_by, []} ->
+ timer:sleep(1),
+ wait_for_monitor(Pid);
+ {monitored_by, [_]} ->
+ ok
+ end.
+
proc(Parent) ->
receive die -> ok end,
{monitored_by, MonitoredBy} = process_info(self(), monitored_by),
diff --git a/lib/kernel/test/interactive_shell_SUITE.erl b/lib/kernel/test/interactive_shell_SUITE.erl
index 298a364a91..883abda067 100644
--- a/lib/kernel/test/interactive_shell_SUITE.erl
+++ b/lib/kernel/test/interactive_shell_SUITE.erl
@@ -22,8 +22,9 @@
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2,
get_columns_and_rows/1, exit_initial/1, job_control_local/1,
- job_control_remote/1,
- job_control_remote_noshell/1,ctrl_keys/1]).
+ job_control_remote/1,stop_during_init/1,
+ job_control_remote_noshell/1,ctrl_keys/1,
+ get_columns_and_rows_escript/1]).
-export([init_per_testcase/2, end_per_testcase/2]).
%% For spawn
@@ -40,9 +41,10 @@ suite() ->
{timetrap,{minutes,3}}].
all() ->
- [get_columns_and_rows, exit_initial, job_control_local,
+ [get_columns_and_rows_escript,get_columns_and_rows,
+ exit_initial, job_control_local,
job_control_remote, job_control_remote_noshell,
- ctrl_keys].
+ ctrl_keys, stop_during_init].
groups() ->
[].
@@ -72,6 +74,60 @@ end_per_group(_GroupName, Config) ->
-define(dbg(Data),noop).
-endif.
+string_to_term(Str) ->
+ {ok,Tokens,_EndLine} = erl_scan:string(Str ++ "."),
+ {ok,AbsForm} = erl_parse:parse_exprs(Tokens),
+ {value,Value,_Bs} = erl_eval:exprs(AbsForm, erl_eval:new_bindings()),
+ Value.
+
+run_unbuffer_escript(Rows, Columns, EScript, NoTermStdIn, NoTermStdOut) ->
+ DataDir = filename:join(filename:dirname(code:which(?MODULE)), "interactive_shell_SUITE_data"),
+ TmpFile = filename:join(DataDir, "tmp"),
+ ok = file:write_file(TmpFile, <<>>),
+ CommandModifier =
+ case {NoTermStdIn, NoTermStdOut} of
+ {false, false} -> "";
+ {true, false} -> io_lib:format(" < ~s", [TmpFile]);
+ {false, true} -> io_lib:format(" > ~s ; cat ~s", [TmpFile, TmpFile]);
+ {true, true} -> io_lib:format(" > ~s < ~s ; cat ~s", [TmpFile, TmpFile, TmpFile])
+ end,
+ Command = io_lib:format("unbuffer -p bash -c \"stty rows ~p; stty columns ~p; escript ~s ~s\"",
+ [Rows, Columns, EScript, CommandModifier]),
+ %% io:format("Command: ~s ~n", [Command]),
+ Out = os:cmd(Command),
+ %% io:format("Out: ~p ~n", [Out]),
+ string_to_term(Out).
+
+get_columns_and_rows_escript(Config) when is_list(Config) ->
+ ExpectUnbufferInstalled =
+ try
+ "79" = string:trim(os:cmd("unbuffer -p bash -c \"stty columns 79 ; tput cols\"")),
+ true
+ catch
+ _:_ -> false
+ end,
+ case ExpectUnbufferInstalled of
+ false ->
+ {skip,
+ "The unbuffer tool (https://core.tcl-lang.org/expect/index) does not seem to be installed.~n"
+ "On Ubuntu/Debian: \"sudo apt-get install expect\""};
+ true ->
+ DataDir = filename:join(filename:dirname(code:which(?MODULE)), "interactive_shell_SUITE_data"),
+ IoColumnsErl = filename:join(DataDir, "io_columns.erl"),
+ IoRowsErl = filename:join(DataDir, "io_rows.erl"),
+ [
+ begin
+ {ok, 42} = run_unbuffer_escript(99, 42, IoColumnsErl, NoTermStdIn, NoTermStdOut),
+ {ok, 99} = run_unbuffer_escript(99, 42, IoRowsErl, NoTermStdIn, NoTermStdOut)
+ end
+ ||
+ {NoTermStdIn, NoTermStdOut} <- [{false, false}, {true, false}, {false, true}]
+ ],
+ {error,enotsup} = run_unbuffer_escript(99, 42, IoRowsErl, true, true),
+ {error,enotsup} = run_unbuffer_escript(99, 42, IoColumnsErl, true, true),
+ ok
+ end.
+
%% Test that the shell can access columns and rows.
get_columns_and_rows(Config) when is_list(Config) ->
case proplists:get_value(default_shell,Config) of
@@ -149,6 +205,22 @@ exit_initial(Config) when is_list(Config) ->
{getline_re,"35"}],[])
end.
+stop_during_init(Config) when is_list(Config) ->
+ case get_progs() of
+ {error,_Reason} ->
+ {skip,"No runerl present"};
+ {RunErl,_ToErl,Erl} ->
+ case create_tempdir() of
+ {error, Reason2} ->
+ {skip, Reason2};
+ Tempdir ->
+ XArg = " -kernel shell_history true -s init stop",
+ start_runerl_command(RunErl, Tempdir, "\\\""++Erl++"\\\""++XArg),
+ {ok, Binary} = file:read_file(filename:join(Tempdir, "erlang.log.1")),
+ nomatch = binary:match(Binary, <<"*** ERROR: Shell process terminated! ***">>)
+ end
+ end.
+
%% Tests that local shell can be started by means of job control.
job_control_local(Config) when is_list(Config) ->
case proplists:get_value(default_shell,Config) of
@@ -600,10 +672,10 @@ start_runerl_node(RunErl,Erl,Tempdir,Nodename) ->
end)++
" -setcookie "++atom_to_list(erlang:get_cookie())
end,
- spawn(fun() ->
- os:cmd("\""++RunErl++"\" "++Tempdir++"/ "++Tempdir++" \""++
- Erl++XArg++"\"")
- end).
+ spawn(fun() -> start_runerl_command(RunErl, Tempdir, Erl++XArg) end).
+
+start_runerl_command(RunErl, Tempdir, Cmd) ->
+ os:cmd("\""++RunErl++"\" "++Tempdir++"/ "++Tempdir++" \""++Cmd++"\"").
start_toerl_server(ToErl,Tempdir) ->
Pid = spawn(?MODULE,toerl_server,[self(),ToErl,Tempdir]),
diff --git a/lib/kernel/test/interactive_shell_SUITE_data/.gitignore b/lib/kernel/test/interactive_shell_SUITE_data/.gitignore
new file mode 100644
index 0000000000..1c2f433de1
--- /dev/null
+++ b/lib/kernel/test/interactive_shell_SUITE_data/.gitignore
@@ -0,0 +1 @@
+tmp \ No newline at end of file
diff --git a/lib/kernel/test/interactive_shell_SUITE_data/io_columns.erl b/lib/kernel/test/interactive_shell_SUITE_data/io_columns.erl
new file mode 100644
index 0000000000..32d0cf25df
--- /dev/null
+++ b/lib/kernel/test/interactive_shell_SUITE_data/io_columns.erl
@@ -0,0 +1,6 @@
+-module(io_columns).
+
+-export([main/1]).
+
+main(_) ->
+ io:format("~p",[io:columns()]).
diff --git a/lib/kernel/test/interactive_shell_SUITE_data/io_rows.erl b/lib/kernel/test/interactive_shell_SUITE_data/io_rows.erl
new file mode 100644
index 0000000000..53ceb464b0
--- /dev/null
+++ b/lib/kernel/test/interactive_shell_SUITE_data/io_rows.erl
@@ -0,0 +1,6 @@
+-module(io_rows).
+
+-export([main/1]).
+
+main(_) ->
+ io:format("~p",[io:rows()]).
diff --git a/lib/kernel/test/logger_SUITE.erl b/lib/kernel/test/logger_SUITE.erl
index 035e5d8974..f8f3d27778 100644
--- a/lib/kernel/test/logger_SUITE.erl
+++ b/lib/kernel/test/logger_SUITE.erl
@@ -880,7 +880,7 @@ other_node(cleanup,_Config) ->
ok.
compare_levels(_Config) ->
- Levels = [emergency,alert,critical,error,warning,notice,info,debug],
+ Levels = [none,emergency,alert,critical,error,warning,notice,info,debug,all],
ok = compare(Levels),
{error,badarg} = ?TRY(logger:compare_levels(bad,bad)),
{error,badarg} = ?TRY(logger:compare_levels({bad},notice)),
diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk
index e5188aa9b5..508e54237b 100644
--- a/lib/kernel/vsn.mk
+++ b/lib/kernel/vsn.mk
@@ -1 +1 @@
-KERNEL_VSN = 6.4.1
+KERNEL_VSN = 6.5
diff --git a/lib/megaco/Makefile b/lib/megaco/Makefile
index ebf83bb475..624c4b0619 100644
--- a/lib/megaco/Makefile
+++ b/lib/megaco/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2019. All Rights Reserved.
+# Copyright Ericsson AB 1999-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -110,7 +110,7 @@ DIA_ANALYSIS = $(basename $(DIA_PLT)).dialyzer_analysis
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_subdir.mk
-.PHONY: reconf conf dconf econf configure setup info version \
+.PHONY: reconf conf dconf econf configure setup info_megaco version \
app_install dialyzer
reconf:
@@ -136,16 +136,15 @@ configure: configure.in
setup:
(cd src && $(MAKE) $@)
-info:
+info_megaco:
@echo "APP_RELEASE_DIR: $(APP_RELEASE_DIR)"
@echo "APP_DIR: $(APP_DIR)"
@echo "APP_TAR_FILE: $(APP_TAR_FILE)"
@echo "OTP_INSTALL_DIR: $(OTP_INSTALL_DIR)"
@echo "APP_INSTALL_DIR: $(APP_INSTALL_DIR)"
@echo ""
- @echo "DIA_PLT: $(DIA_PLT)"
- @echo "DIA_ANALYSIS: $(DIA_ANALYSIS)"
- @echo ""
+
+info: info_megaco
version:
@echo "$(VSN)"
@@ -204,27 +203,6 @@ tar: $(APP_TAR_FILE)
$(APP_TAR_FILE): $(APP_DIR)
(cd "$(APP_RELEASE_DIR)"; gtar zcf "$(subst $(space),\ ,$@)" $(DIR_NAME))
-dialyzer_plt: $(DIA_PLT)
-
-$(DIA_PLT): Makefile
- @echo "Building $(APPLICATION) plt file"
- @dialyzer --build_plt \
- --output_plt $@ \
- -r ../$(APPLICATION)/ebin \
- ../../lib/kernel/ebin \
- ../../lib/stdlib/ebin \
- ../../lib/runtime_tools/ebin \
- ../../lib/asn1/ebin \
- ../../lib/debugger/ebin \
- ../../lib/et/ebin \
- ../../erts/preloaded/ebin \
- --output $(DIA_ANALYSIS) \
- --verbose
-
-dialyzer: $(DIA_PLT)
- @echo "Running dialyzer on $(APPLICATION)"
- @dialyzer --plt $< \
- ../$(APPLICATION)/ebin \
- --verbose
+DIA_PLT_APPS=asn1 runtime_tools et debugger
include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/megaco/doc/src/notes.xml b/lib/megaco/doc/src/notes.xml
index 6f33ae390c..62d0aad77d 100644
--- a/lib/megaco/doc/src/notes.xml
+++ b/lib/megaco/doc/src/notes.xml
@@ -37,7 +37,24 @@
section is the version number of Megaco.</p>
- <section><title>Megaco 3.18.5</title>
+ <section><title>Megaco 3.18.6</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix various minor issues related to Dialyzer. Mostly
+ these are dialyzer warnings, but there was also some
+ minor bugs detected by Dialyzer.</p>
+ <p>
+ Own Id: OTP-15882</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Megaco 3.18.5</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/megaco/src/engine/megaco_monitor.erl b/lib/megaco/src/engine/megaco_monitor.erl
index 877509a776..8f4741b202 100644
--- a/lib/megaco/src/engine/megaco_monitor.erl
+++ b/lib/megaco/src/engine/megaco_monitor.erl
@@ -193,7 +193,7 @@ cancel_apply_after({apply_after, Ref}) ->
TimeLeft when is_integer(TimeLeft) ->
{ok, TimeLeft};
_ ->
- {ok, 0}
+ {error, {already_expired, Ref}}
end;
cancel_apply_after(apply_after_infinity) ->
ok;
diff --git a/lib/megaco/test/Makefile b/lib/megaco/test/Makefile
index 4ddd73eea1..b4e31765b8 100644
--- a/lib/megaco/test/Makefile
+++ b/lib/megaco/test/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1999-2016. All Rights Reserved.
+# Copyright Ericsson AB 1999-2019. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -102,6 +102,9 @@ endif
ERL_COMPILE_FLAGS += $(MEGACO_ERL_COMPILE_FLAGS)
+# We have a behaviour in the test catalog (megaco_test_generator)
+ERL_COMPILE_FLAGS += -pa ../../megaco/test
+
ERL_PATH = -pa ../../megaco/examples/simple \
-pa ../../megaco/ebin \
-pa ../../et/ebin
diff --git a/lib/megaco/test/megaco_SUITE.erl b/lib/megaco/test/megaco_SUITE.erl
index 38590f9fee..b55dc68143 100644
--- a/lib/megaco/test/megaco_SUITE.erl
+++ b/lib/megaco/test/megaco_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -25,7 +25,21 @@
-module(megaco_SUITE).
--compile(export_all).
+-export([
+ suite/0,
+ all/0,
+ groups/0,
+
+ init_per_suite/1,
+ end_per_suite/1,
+ init_per_group/2,
+ end_per_group/2,
+ init_per_testcase/2,
+ end_per_testcase/2,
+
+ t/0, t/1,
+ init/0
+ ]).
-include("megaco_test_lib.hrl").
-include_lib("megaco/include/megaco.hrl").
@@ -96,13 +110,39 @@ groups() ->
{flex, [], [{megaco_flex_test, all}]}].
init_per_suite(Config) ->
+ io:format("~w:init_per_suite -> entry with"
+ "~n Config: ~p"
+ "~n OS Type: ~p"
+ "~n OS Version: ~s"
+ "~n",
+ [?MODULE,
+ Config,
+ os:type(),
+ case os:version() of
+ {Major, Minor, Release} ->
+ ?F("~w.~w.~w", [Major, Minor, Release]);
+ Str when is_list(Str) ->
+ Str
+ end]),
Config.
end_per_suite(_Config) ->
ok.
init_per_group(_GroupName, Config) ->
- Config.
+ Skippable = [{unix, [{darwin, fun(V) when (V > {9, 8, 0}) ->
+ %% This version is OK: No Skip
+ false;
+ (_V) ->
+ %% This version is *not* ok: Skip
+ true
+ end}]}],
+ case ?OS_BASED_SKIP(Skippable) of
+ true ->
+ {skip, "***OLD*** Darwin"};
+ false ->
+ Config
+ end.
end_per_group(_GroupName, Config) ->
Config.
diff --git a/lib/megaco/test/megaco_actions_test.erl b/lib/megaco/test/megaco_actions_test.erl
index fcbe4f12fa..498e5c91cb 100644
--- a/lib/megaco/test/megaco_actions_test.erl
+++ b/lib/megaco/test/megaco_actions_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -20,13 +20,30 @@
%%
%%----------------------------------------------------------------------
-%% Purpose: Verify that it is possible to separatelly encode
+%% Purpose: Verify that it is possible to separately encode
%% the action requests list. Do this with all codec's
%% that supports partial encode.
%%----------------------------------------------------------------------
-module(megaco_actions_test).
--compile(export_all).
+-export([
+ all/0,
+ groups/0,
+
+ init_per_group/2,
+ end_per_group/2,
+ init_per_testcase/2,
+ end_per_testcase/2,
+
+ t/0, t/1,
+
+ pretty_text/1,
+ flex_pretty_text/1,
+ compact_text/1,
+ flex_compact_text/1,
+ erl_dist/1,
+ erl_dist_mc/1
+ ]).
-include("megaco_test_lib.hrl").
-include_lib("megaco/include/megaco.hrl").
@@ -364,9 +381,6 @@ sleep(X) ->
receive after X -> ok end.
-error_msg(F,A) -> error_logger:error_msg(F ++ "~n",A).
-
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
i(F) ->
@@ -376,8 +390,8 @@ i(F, A) ->
print(info, get(verbosity), "", F, A).
-d(F) ->
- d(F, []).
+%% d(F) ->
+%% d(F, []).
d(F, A) ->
print(debug, get(verbosity), "DBG: ", F, A).
@@ -391,20 +405,10 @@ print(Severity, Verbosity, P, F, A) ->
print(printable(Severity,Verbosity), P, F, A).
print(true, P, F, A) ->
- io:format("~s~p:~s: " ++ F ++ "~n", [P, self(), get(sname) | A]);
+ io:format("*** [~s] ~s ~p ~s ***"
+ "~n " ++ F ++ "~n",
+ [?FTS(), P, self(), get(sname) | A]);
print(_, _, _, _) ->
ok.
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-random_init() ->
- {A,B,C} = now(),
- random:seed(A,B,C).
-
-random() ->
- 10 * random:uniform(50).
-
-apply_load_timer() ->
- erlang:send_after(random(), self(), apply_load_timeout).
-
diff --git a/lib/megaco/test/megaco_app_test.erl b/lib/megaco/test/megaco_app_test.erl
index 981d93f5dd..fff2d8c7d4 100644
--- a/lib/megaco/test/megaco_app_test.erl
+++ b/lib/megaco/test/megaco_app_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -20,29 +20,42 @@
%%----------------------------------------------------------------------
%% Purpose: Verify the application specifics of the Megaco application
%%----------------------------------------------------------------------
+
-module(megaco_app_test).
--compile(export_all).
+-export([
+ all/0,
+
+ app/0, app/1,
+ appup/0, appup/1
+ ]).
-include_lib("common_test/include/ct.hrl").
+
%%--------------------------------------------------------------------
%% Common Test interface functions -----------------------------------
%%--------------------------------------------------------------------
+
all() ->
[
app,
appup
].
+
%%--------------------------------------------------------------------
%% Test Cases --------------------------------------------------------
%%--------------------------------------------------------------------
+
app() ->
[{doc, "Test that the megaco app file is ok"}].
app(Config) when is_list(Config) ->
ok = test_server:app_test(megaco).
+
+
%%--------------------------------------------------------------------
+
appup() ->
[{doc, "Test that the megaco appup file is ok"}].
appup(Config) when is_list(Config) ->
diff --git a/lib/megaco/test/megaco_appup_test.erl b/lib/megaco/test/megaco_appup_test.erl
index 8dc3ad51a0..a06d274844 100644
--- a/lib/megaco/test/megaco_appup_test.erl
+++ b/lib/megaco/test/megaco_appup_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2002-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2002-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -22,8 +22,23 @@
%%----------------------------------------------------------------------
-module(megaco_appup_test).
--compile(export_all).
--compile({no_auto_import,[error/1]}).
+-export([
+ all/0,
+ groups/0,
+
+ init_per_suite/1,
+ end_per_suite/1,
+
+ init_per_group/2,
+ end_per_group/2,
+
+ init_per_testcase/2,
+ end_per_testcase/2,
+
+ appup_file/1
+ ]).
+
+-compile({no_auto_import, [error/1]}).
-include_lib("common_test/include/ct.hrl").
-include("megaco_test_lib.hrl").
@@ -76,6 +91,10 @@ end_per_testcase(_Case, Config) when is_list(Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Perform a simple check of the appup file
+appup_file(suite) ->
+ [];
+appup_file(doc) ->
+ ["Perform a simple check of the appup file"];
appup_file(Config) when is_list(Config) ->
ok = ?t:appup_test(megaco).
diff --git a/lib/megaco/test/megaco_call_flow_test.erl b/lib/megaco/test/megaco_call_flow_test.erl
index eb4574862d..03caf705ba 100644
--- a/lib/megaco/test/megaco_call_flow_test.erl
+++ b/lib/megaco/test/megaco_call_flow_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -38,7 +38,85 @@
-module(megaco_call_flow_test).
--compile(export_all).
+-export([
+ all/0,
+ groups/0,
+
+ init_per_group/2,
+ end_per_group/2,
+ init_per_testcase/2,
+ end_per_testcase/2,
+
+ pretty/1,
+ compact/1,
+ pretty_flex/1,
+ compact_flex/1,
+ bin/1,
+ ber/1,
+ per/1,
+ standard_erl/1,
+ compressed_erl/1,
+
+ t/0, t/1
+
+ ]).
+
+-export([
+ msg1/0, msg1/1,
+ msg2/0, msg2/1,
+ msg3/0, msg3/1,
+ msg4/0, msg4/1,
+ msg5a/0, msg5a/1,
+ msg5b/0, msg5b/1,
+ msg6/0, msg6/1,
+ msg7/0, msg7/1,
+ msg9/0, msg9/1,
+ msg10/0, msg10/1,
+ msg11/0, msg11/1,
+ msg12/0, msg12/1,
+ msg13/0, msg13/1,
+ msg14/0, msg14/1,
+ msg15/0, msg15/1,
+ msg16/0, msg16/1,
+ msg17a/0, msg17a/1,
+ msg17b/0, msg17b/1,
+ msg18a/0, msg18a/1,
+ msg18b/0, msg18b/1,
+ msg18c/0, msg18c/1,
+ msg18d/0, msg18d/1,
+ msg19a/0, msg19a/1,
+ msg19b/0, msg19b/1,
+ msg20/0, msg20/1,
+ msg21/0, msg21/1,
+ msg22a/0, msg22a/1,
+ msg22b/0, msg22b/1,
+ msg23a/0, msg23a/1,
+ msg23b/0, msg23b/1
+
+ ]).
+
+-export([
+ encoders/0,
+ msg_sizes/0,
+ coding_times/0,
+ encoding_times/0,
+ decoding_times/0,
+ coding_times_stat/0,
+ encoding_times_stat/0,
+ decoding_times_stat/0,
+ size_stat/0,
+ gnuplot_gif/0,
+ gnuplot_size_gif/0,
+
+ gen_byte_msg/2,
+ gen_header_file_binary/1,
+ gen_ber_header/0,
+ gen_ber_bin_header/0,
+ gen_per_header/0,
+ single_meter/4,
+ count/2
+ ]).
+
-include_lib("megaco/include/megaco.hrl").
-include_lib("megaco/include/megaco_message_v1.hrl").
-include("megaco_test_lib.hrl").
@@ -53,16 +131,20 @@ init_per_testcase(Case, Config) ->
end_per_testcase(Case, Config) ->
megaco_test_lib:end_per_testcase(Case, Config).
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Top test case
all() ->
- [{group, text}, {group, binary}].
+ [{group, text}, {group, binary}, {group, erl}].
groups() ->
- [{text, [], [pretty, compact]},
- {flex, [], [pretty_flex, compact_flex]},
- {binary, [], [bin, ber, per]}].
+ [
+ {text, [], [pretty, compact]},
+ {flex, [], [pretty_flex, compact_flex]},
+ {binary, [], [bin, ber, per]},
+ {erl, [], [standard_erl, compressed_erl]}
+ ].
init_per_group(_GroupName, Config) ->
Config.
diff --git a/lib/megaco/test/megaco_codec_test.erl b/lib/megaco/test/megaco_codec_test.erl
index 007136f83e..0dfbabcc81 100644
--- a/lib/megaco/test/megaco_codec_test.erl
+++ b/lib/megaco/test/megaco_codec_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -25,7 +25,18 @@
-module(megaco_codec_test).
--compile(export_all).
+-export([
+ all/0,
+ groups/0,
+
+ init_per_group/2,
+ end_per_group/2,
+ init_per_testcase/2,
+ end_per_testcase/2,
+
+ t/0, t/1,
+ init/0
+ ]).
-include("megaco_test_lib.hrl").
-include_lib("megaco/include/megaco.hrl").
diff --git a/lib/megaco/test/megaco_codec_test_lib.erl b/lib/megaco/test/megaco_codec_test_lib.erl
index 6eee5caaaa..7a3d4e6cf8 100644
--- a/lib/megaco/test/megaco_codec_test_lib.erl
+++ b/lib/megaco/test/megaco_codec_test_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -993,15 +993,15 @@ expect_exec([#expect_instruction{description = Desc,
skip({What, Why}) when is_atom(What) andalso is_list(Why) ->
Reason = lists:flatten(io_lib:format("~p: ~s", [What, Why])),
- exit({skipped, Reason});
+ ?SKIP(Reason);
skip({What, Why}) ->
Reason = lists:flatten(io_lib:format("~p: ~p", [What, Why])),
- exit({skipped, Reason});
+ ?SKIP(Reason);
skip(Reason) when is_list(Reason) ->
- exit({skipped, Reason});
+ ?SKIP(Reason);
skip(Reason1) ->
Reason2 = lists:flatten(io_lib:format("~p", [Reason1])),
- exit({skipped, Reason2}).
+ ?SKIP(Reason2).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/megaco/test/megaco_config_test.erl b/lib/megaco/test/megaco_config_test.erl
index 02e06a722a..d46806927a 100644
--- a/lib/megaco/test/megaco_config_test.erl
+++ b/lib/megaco/test/megaco_config_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -25,7 +25,24 @@
-module(megaco_config_test).
--compile(export_all).
+-export([
+ all/0,
+ groups/0,
+
+ init_per_group/2,
+ end_per_group/2,
+ init_per_testcase/2,
+ end_per_testcase/2,
+
+ config/1,
+ transaction_id_counter_mg/1,
+ transaction_id_counter_mgc/1,
+ otp_7216/1,
+ otp_8167/1,
+ otp_8183/1,
+
+ t/0, t/1
+ ]).
-include("megaco_test_lib.hrl").
-include_lib("megaco/include/megaco.hrl").
@@ -37,6 +54,19 @@ t(Case) -> megaco_test_lib:t({?MODULE, Case}).
min(M) -> timer:minutes(M).
%% Test server callbacks
+init_per_testcase(Case, Config) when (Case =:= otp_7216) orelse
+ (Case =:= otp_8167) orelse
+ (Case =:= otp_8183) ->
+ i("try starting megaco_config"),
+ case megaco_config:start_link() of
+ {ok, _} ->
+ C = lists:keydelete(tc_timeout, 1, Config),
+ do_init_per_testcase(Case, [{tc_timeout, min(3)}|C]);
+ {error, Reason} ->
+ i("Failed starting megaco_config: "
+ "~n ~p", [Reason]),
+ {skip, ?F("Failed starting config: ~p", [Reason])}
+ end;
init_per_testcase(Case, Config) ->
C = lists:keydelete(tc_timeout, 1, Config),
do_init_per_testcase(Case, [{tc_timeout, min(3)}|C]).
@@ -45,6 +75,12 @@ do_init_per_testcase(Case, Config) ->
process_flag(trap_exit, true),
megaco_test_lib:init_per_testcase(Case, Config).
+end_per_testcase(Case, Config) when (Case =:= otp_7216) orelse
+ (Case =:= otp_8167) orelse
+ (Case =:= otp_8183) ->
+ (catch megaco_config:stop()),
+ process_flag(trap_exit, false),
+ megaco_test_lib:end_per_testcase(Case, Config);
end_per_testcase(Case, Config) ->
process_flag(trap_exit, false),
megaco_test_lib:end_per_testcase(Case, Config).
@@ -60,14 +96,17 @@ end_per_testcase(Case, Config) ->
%% Top test case
all() ->
- [config, {group, transaction_id_counter},
- {group, tickets}].
+ [
+ config,
+ {group, transaction_id_counter},
+ {group, tickets}
+ ].
groups() ->
- [{transaction_id_counter, [],
- [transaction_id_counter_mg,
- transaction_id_counter_mgc]},
- {tickets, [], [otp_7216, otp_8167, otp_8183]}].
+ [
+ {transaction_id_counter, [], transaction_id_counter_cases()},
+ {tickets, [], tickets_cases()}
+ ].
init_per_group(_GroupName, Config) ->
Config.
@@ -75,6 +114,19 @@ init_per_group(_GroupName, Config) ->
end_per_group(_GroupName, Config) ->
Config.
+transaction_id_counter_cases() ->
+ [
+ transaction_id_counter_mg,
+ transaction_id_counter_mgc
+ ].
+
+tickets_cases() ->
+ [
+ otp_7216,
+ otp_8167,
+ otp_8183
+ ].
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Config test case
@@ -736,9 +788,6 @@ otp_7216(Config) when is_list(Config) ->
put(tc, otp_7216),
p("start"),
- p("start the megaco config process"),
- megaco_config:start_link(),
-
LocalMid1 = {deviceName, "local-mid-1"},
%% LocalMid2 = {deviceName, "local-mid-2"},
RemoteMid1 = {deviceName, "remote-mid-1"},
@@ -859,9 +908,6 @@ otp_8167(Config) when is_list(Config) ->
put(tc, otp8167),
p("start"),
- p("start the megaco config process"),
- megaco_config:start_link(),
-
LocalMid1 = {deviceName, "local-mid-1"},
LocalMid2 = {deviceName, "local-mid-2"},
RemoteMid1 = {deviceName, "remote-mid-1"},
@@ -981,9 +1027,6 @@ otp_8183(Config) when is_list(Config) ->
put(tc, otp8183),
p("start"),
- p("start the megaco config process"),
- megaco_config:start_link(),
-
LocalMid1 = {deviceName, "local-mid-1"},
LocalMid2 = {deviceName, "local-mid-2"},
RemoteMid1 = {deviceName, "remote-mid-1"},
@@ -1122,28 +1165,18 @@ i(F) ->
i(F, []).
i(F, A) ->
- print(info, get(verbosity), now(), get(tc), "INF", F, A).
+ print(info, get(verbosity), get(tc), "INF", F, A).
printable(_, debug) -> true;
printable(info, info) -> true;
printable(_,_) -> false.
-print(Severity, Verbosity, Ts, Tc, P, F, A) ->
- print(printable(Severity,Verbosity), Ts, Tc, P, F, A).
+print(Severity, Verbosity, Tc, P, F, A) ->
+ print(printable(Severity,Verbosity), Tc, P, F, A).
-print(true, Ts, Tc, P, F, A) ->
+print(true, Tc, P, F, A) ->
io:format("*** [~s] ~s ~p ~s:~w ***"
"~n " ++ F ++ "~n",
- [format_timestamp(Ts), P, self(), get(sname), Tc | A]);
-print(_, _, _, _, _, _) ->
+ [?FTS(), P, self(), get(sname), Tc | A]);
+print(_, _, _, _, _) ->
ok.
-
-format_timestamp({_N1, _N2, N3} = Now) ->
- {Date, Time} = calendar:now_to_datetime(Now),
- {YYYY,MM,DD} = Date,
- {Hour,Min,Sec} = Time,
- FormatDate =
- io_lib:format("~.4w:~.2.0w:~.2.0w ~.2.0w:~.2.0w:~.2.0w 4~w",
- [YYYY,MM,DD,Hour,Min,Sec,round(N3/1000)]),
- lists:flatten(FormatDate).
-
diff --git a/lib/megaco/test/megaco_digit_map_test.erl b/lib/megaco/test/megaco_digit_map_test.erl
index 998e829b67..e03d38497c 100644
--- a/lib/megaco/test/megaco_digit_map_test.erl
+++ b/lib/megaco/test/megaco_digit_map_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -24,7 +24,27 @@
%%----------------------------------------------------------------------
-module(megaco_digit_map_test).
--compile(export_all).
+-export([
+ all/0,
+ groups/0,
+
+ init_per_group/2,
+ end_per_group/2,
+ init_per_testcase/2,
+ end_per_testcase/2,
+
+ otp_5750_01/1,
+ otp_5750_02/1,
+ otp_5799_01/1,
+ otp_5826_01/1,
+ otp_5826_02/1,
+ otp_5826_03/1,
+ otp_7449_1/1,
+ otp_7449_2/1,
+
+ t/0, t/1
+ ]).
+
-include("megaco_test_lib.hrl").
@@ -44,16 +64,18 @@ end_per_testcase(Case, Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
all() ->
- [{group, tickets}].
+ [
+ {group, tickets}
+ ].
groups() ->
- [{tickets, [],
- [{group, otp_5750}, {group, otp_5799},
- {group, otp_5826}, {group, otp_7449}]},
- {otp_5750, [], [otp_5750_01, otp_5750_02]},
- {otp_5799, [], [otp_5799_01]},
- {otp_5826, [], [otp_5826_01, otp_5826_02, otp_5826_03]},
- {otp_7449, [], [otp_7449_1, otp_7449_2]}].
+ [
+ {tickets, [], tickets_cases()},
+ {otp_5750, [], otp_5750_cases()},
+ {otp_5799, [], otp_5799_cases()},
+ {otp_5826, [], otp_5826_cases()},
+ {otp_7449, [], otp_7449_cases()}
+ ].
init_per_group(_GroupName, Config) ->
Config.
@@ -62,6 +84,38 @@ end_per_group(_GroupName, Config) ->
Config.
+tickets_cases() ->
+ [
+ {group, otp_5750},
+ {group, otp_5799},
+ {group, otp_5826},
+ {group, otp_7449}
+ ].
+
+otp_5750_cases() ->
+ [
+ otp_5750_01,
+ otp_5750_02
+ ].
+
+otp_5799_cases() ->
+ [
+ otp_5799_01
+ ].
+
+otp_5826_cases() ->
+ [
+ otp_5826_01,
+ otp_5826_02,
+ otp_5826_03
+ ].
+
+otp_7449_cases() ->
+ [
+ otp_7449_1,
+ otp_7449_2
+ ].
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/megaco/test/megaco_examples_test.erl b/lib/megaco/test/megaco_examples_test.erl
index 10ca0375f6..fdf9fe29ff 100644
--- a/lib/megaco/test/megaco_examples_test.erl
+++ b/lib/megaco/test/megaco_examples_test.erl
@@ -25,7 +25,20 @@
-module(megaco_examples_test).
--compile(export_all).
+-export([
+ all/0,
+ groups/0,
+
+ init_per_group/2,
+ end_per_group/2,
+ init_per_testcase/2,
+ end_per_testcase/2,
+
+ simple/1,
+
+ t/0, t/1
+ ]).
+
-include("megaco_test_lib.hrl").
-include_lib("megaco/include/megaco.hrl").
diff --git a/lib/megaco/test/megaco_flex_test.erl b/lib/megaco/test/megaco_flex_test.erl
index 999d1abc6c..27c46a3b47 100644
--- a/lib/megaco/test/megaco_flex_test.erl
+++ b/lib/megaco/test/megaco_flex_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -219,18 +219,5 @@ p(F, A) ->
TC = get(tc),
io:format("*** [~s] ~p ~w ***"
"~n " ++ F ++ "~n",
- [formated_timestamp(), self(), TC | A]).
-
-formated_timestamp() ->
- format_timestamp(erlang:now()).
-
-format_timestamp({_N1, _N2, N3} = Now) ->
- {Date, Time} = calendar:now_to_datetime(Now),
- {YYYY, MM, DD} = Date,
- {Hour, Min, Sec} = Time,
- FormatDate =
- io_lib:format("~.4w:~.2.0w:~.2.0w ~.2.0w:~.2.0w:~.2.0w 4~w",
- [YYYY,MM,DD,Hour,Min,Sec,round(N3/1000)]),
- lists:flatten(FormatDate).
-
+ [?FTS(), self(), TC | A]).
diff --git a/lib/megaco/test/megaco_load_test.erl b/lib/megaco/test/megaco_load_test.erl
index 511e5a2e8e..9cce9e70ce 100644
--- a/lib/megaco/test/megaco_load_test.erl
+++ b/lib/megaco/test/megaco_load_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -24,7 +24,33 @@
%%----------------------------------------------------------------------
-module(megaco_load_test).
--compile(export_all).
+-export([
+ all/0,
+ groups/0,
+
+ init_per_group/2,
+ end_per_group/2,
+ init_per_testcase/2,
+ end_per_testcase/2,
+
+ single_user_light_load/1,
+ single_user_medium_load/1,
+ single_user_heavy_load/1,
+ single_user_extreme_load/1,
+
+ multi_user_light_load/1,
+ multi_user_medium_load/1,
+ multi_user_heavy_load/1,
+ multi_user_extreme_load/1,
+
+ t/0, t/1
+ ]).
+
+-export([
+ do_multi_load/3,
+ multi_load_collector/7
+ ]).
+
-include("megaco_test_lib.hrl").
-include_lib("megaco/include/megaco.hrl").
@@ -66,8 +92,6 @@
t() -> megaco_test_lib:t(?MODULE).
t(Case) -> megaco_test_lib:t({?MODULE, Case}).
-min(M) -> timer:minutes(M).
-
%% Test server callbacks
init_per_testcase(single_user_light_load = Case, Config) ->
C = lists:keydelete(tc_timeout, 1, Config),
@@ -108,15 +132,33 @@ end_per_testcase(Case, Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
all() ->
- [single_user_light_load,
- single_user_medium_load, single_user_heavy_load,
- single_user_extreme_load, multi_user_light_load,
- multi_user_medium_load, multi_user_heavy_load,
- multi_user_extreme_load].
+ [
+ {group, single},
+ {group, multi}
+ ].
groups() ->
- [].
-
+ [
+ {single, [], single_cases()},
+ {multi, [], multi_cases()}
+ ].
+
+single_cases() ->
+ [
+ single_user_light_load,
+ single_user_medium_load,
+ single_user_heavy_load,
+ single_user_extreme_load
+ ].
+
+multi_cases() ->
+ [
+ multi_user_light_load,
+ multi_user_medium_load,
+ multi_user_heavy_load,
+ multi_user_extreme_load
+ ].
+
init_per_group(_GroupName, Config) ->
Config.
@@ -326,6 +368,17 @@ load_controller(Config, Fun) when is_list(Config) and is_function(Fun) ->
d("load_controller -> "
"loader [~p] terminated with ok~n", [Loader]),
ok;
+ {'EXIT', Loader, {skipped, {fatal, Reason, File, Line}}} ->
+ i("load_controller -> "
+ "loader [~p] terminated with fatal skip"
+ "~n Reason: ~p"
+ "~n At: ~p:~p", [Loader, Reason, File, Line]),
+ ?SKIP(Reason);
+ {'EXIT', Loader, {skipped, Reason}} ->
+ i("load_controller -> "
+ "loader [~p] terminated with skip"
+ "~n Reason: ~p", [Loader, Reason]),
+ ?SKIP(Reason);
{'EXIT', Loader, Reason} ->
i("load_controller -> "
"loader [~p] terminated with"
@@ -629,14 +682,6 @@ make_mids([MgNode|MgNodes], Mids) ->
exit("Test node must be started with '-sname'")
end.
-tim() ->
- {A,B,C} = erlang:now(),
- A*1000000000+B*1000+(C div 1000).
-
-sleep(X) -> receive after X -> ok end.
-
-error_msg(F,A) -> error_logger:error_msg(F ++ "~n",A).
-
maybe_display_system_info(NumLoaders) when NumLoaders > 50 ->
[{display_system_info, timer:seconds(2)}];
maybe_display_system_info(NumLoaders) when NumLoaders > 10 ->
@@ -647,50 +692,32 @@ maybe_display_system_info(_) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+min(M) -> timer:minutes(M).
+
i(F) ->
i(F, []).
i(F, A) ->
- print(info, get(verbosity), now(), get(tc), "INF", F, A).
+ print(info, get(verbosity), get(tc), "INF", F, A).
d(F) ->
d(F, []).
d(F, A) ->
- print(debug, get(verbosity), now(), get(tc), "DBG", F, A).
+ print(debug, get(verbosity), get(tc), "DBG", F, A).
printable(_, debug) -> true;
printable(info, info) -> true;
printable(_,_) -> false.
-print(Severity, Verbosity, Ts, Tc, P, F, A) ->
- print(printable(Severity,Verbosity), Ts, Tc, P, F, A).
+print(Severity, Verbosity, Tc, P, F, A) ->
+ print(printable(Severity,Verbosity), Tc, P, F, A).
-print(true, Ts, Tc, P, F, A) ->
+print(true, Tc, P, F, A) ->
io:format("*** [~s] ~s ~p ~s:~w ***"
"~n " ++ F ++ "~n",
- [format_timestamp(Ts), P, self(), get(sname), Tc | A]);
-print(_, _, _, _, _, _) ->
+ [?FTS(), P, self(), get(sname), Tc | A]);
+print(_, _, _, _, _) ->
ok.
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-random_init() ->
- {A,B,C} = now(),
- random:seed(A,B,C).
-
-random() ->
- 10 * random:uniform(50).
-
-apply_load_timer() ->
- erlang:send_after(random(), self(), apply_load_timeout).
-
-format_timestamp({_N1, _N2, N3} = Now) ->
- {Date, Time} = calendar:now_to_datetime(Now),
- {YYYY,MM,DD} = Date,
- {Hour,Min,Sec} = Time,
- FormatDate =
- io_lib:format("~.4w:~.2.0w:~.2.0w ~.2.0w:~.2.0w:~.2.0w 4~w",
- [YYYY,MM,DD,Hour,Min,Sec,round(N3/1000)]),
- lists:flatten(FormatDate).
diff --git a/lib/megaco/test/megaco_mess_test.erl b/lib/megaco/test/megaco_mess_test.erl
index d3216c2a6d..0486c13e5f 100644
--- a/lib/megaco/test/megaco_mess_test.erl
+++ b/lib/megaco/test/megaco_mess_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -281,6 +281,8 @@
-define(VERSION, 1).
+-define(USER_MOD, megaco_mess_user_test).
+
-define(TEST_VERBOSITY, debug).
-define(MGC_VERBOSITY, debug).
-define(MG_VERBOSITY, debug).
@@ -303,26 +305,48 @@
-define(MG_NOTIF_RAR(Pid), megaco_test_mg:notify_request_and_reply(Pid)).
-define(SEND(Expr),
- ?VERIFY(ok, megaco_mess_user_test:apply_proxy(fun() -> Expr end))).
+ ?VERIFY(ok, ?USER_MOD:apply_proxy(fun() -> Expr end))).
-define(USER(Expected, Reply),
- megaco_mess_user_test:reply(?MODULE,
- ?LINE,
- fun(Actual) ->
- case ?VERIFY(Expected, Actual) of
- Expected -> {ok, Reply};
- UnExpected -> {error, {reply_verify,
- ?MODULE,
- ?LINE,
- UnExpected}}
- end
- end)).
-
-%% t() -> megaco_test_lib:t(?MODULE).
-%% t(Case) -> megaco_test_lib:t({?MODULE, Case}).
-
-
-min(M) -> timer:minutes(M).
+ ?USER_MOD:reply(?MODULE,
+ ?LINE,
+ fun(Actual) ->
+ case ?VERIFY(Expected, Actual) of
+ Expected -> {ok, Reply};
+ UnExpected -> {error, {reply_verify,
+ ?MODULE,
+ ?LINE,
+ UnExpected}}
+ end
+ end)).
+
+%% Some generator (utility) macros
+-define(GM_START(), megaco_start).
+-define(GM_STOP(), megaco_stop).
+-define(GM_START_USER(M, RI, C), {megaco_start_user, M, RI, C}).
+-define(GM_START_USER(M, RI), ?GM_START_USER(M, RI, [])).
+-define(GM_STOP_USER(), megaco_stop_user).
+-define(GMSI(I), {megaco_system_info, I}).
+-define(GMSI_USERS(), ?GMSI(users)).
+-define(GMSI_CONNS(), ?GMSI(connections)).
+-define(GMCAST(Reqs, Opts), {megaco_cast, Reqs, Opts}).
+-define(GMCAST(Reqs), ?GMCAST(Reqs, [])).
+-define(GMCB(CB, VF), {megaco_callback, CB, VF}).
+-define(GMCB_CONNECT(VF), ?GMCB(handle_connect, VF)).
+-define(GMCB_TRANS_REP(VF), ?GMCB(handle_trans_reply, VF)).
+-define(GMT(T), {megaco_trace, T}).
+-define(GMT_ENABLE(), ?GMT(enable)).
+-define(GMT_DISABLE(), ?GMT(disable)).
+-define(GD(D), {debug, D}).
+-define(GD_ENABLE(), ?GD(true)).
+-define(GD_DISABLE(), ?GD(false)).
+-define(GS(T), {sleep, T}).
+
+-define(GSND(T, D), {send, T, D}).
+-define(GERCV(T, VF, TO), {expect_receive, T, {VF, TO}}).
+
+
+min(M) -> ?MINS(M).
%% Test server callbacks
init_per_testcase(otp_7189 = Case, Config) ->
@@ -396,8 +420,19 @@ groups() ->
init_per_suite(Config) ->
io:format("~w:init_per_suite -> entry with"
- "~n Config: ~p"
- "~n", [?MODULE, Config]),
+ "~n Config: ~p"
+ "~n OS Type: ~p"
+ "~n OS Version: ~s"
+ "~n",
+ [?MODULE,
+ Config,
+ os:type(),
+ case os:version() of
+ {Major, Minor, Release} ->
+ ?F("~w.~w.~w", [Major, Minor, Release]);
+ Str when is_list(Str) ->
+ Str
+ end]),
Config.
end_per_suite(_Config) ->
@@ -491,12 +526,12 @@ request_and_reply_plain(suite) ->
request_and_reply_plain(Config) when is_list(Config) ->
?ACQUIRE_NODES(1, Config),
d("request_and_reply_plain -> start proxy",[]),
- megaco_mess_user_test:start_proxy(),
+ ?USER_MOD:start_proxy(),
PrelMid = preliminary_mid,
MgMid = ipv4_mid(4711),
MgcMid = ipv4_mid(),
- UserMod = megaco_mess_user_test,
+ UserMod = ?USER_MOD,
d("request_and_reply_plain -> start megaco app",[]),
?VERIFY(ok, application:start(megaco)),
UserConfig = [{user_mod, UserMod}, {send_mod, UserMod},
@@ -564,6 +599,7 @@ request_and_reply_plain(Config) when is_list(Config) ->
ok.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% OTP-4760
@@ -754,8 +790,15 @@ request_and_reply_pending_ack_no_pending(Config) when is_list(Config) ->
d("[MGC] start the simulation"),
{ok, MgcId} = megaco_test_megaco_generator:exec(Mgc, MgcEvSeq),
- i("wait some time before starting the MG simulator"),
- sleep(1000),
+ %% i("wait some time before starting the MG simulator"),
+ %% sleep(1000),
+
+ i("await MGC ready announcement"),
+ receive
+ announce_mgc ->
+ i("received MGC ready announcement"),
+ ok
+ end,
d("[MG] start the simulator (generator)"),
{ok, Mg} = megaco_test_tcp_generator:start_link("MG", MgNode),
@@ -808,6 +851,7 @@ request_and_reply_pending_ack_no_pending(Config) when is_list(Config) ->
-endif.
rarpanp_mgc_event_sequence(text, tcp) ->
+ CTRL = self(),
Mid = {deviceName,"ctrl"},
RI = [
{port, 2944},
@@ -819,10 +863,6 @@ rarpanp_mgc_event_sequence(text, tcp) ->
ScrVerify = ?rarpanp_mgc_verify_service_change_req_fun(Mid),
NrVerify = ?rarpanp_mgc_verify_notify_req_fun(),
DiscoVerify = ?rarpanp_mgc_verify_handle_disconnect_fun(),
-%% ConnectVerify = fun rarpanp_mgc_verify_handle_connect/1,
-%% ScrVerify = rarpanp_mgc_verify_service_change_req_fun(Mid),
-%% NrVerify = rarpanp_mgc_verify_notify_request_fun(),
-%% DiscoVerify = fun rarpanp_mgc_verify_handle_disconnect/1,
EvSeq = [
{debug, true},
{megaco_trace, disable},
@@ -832,6 +872,10 @@ rarpanp_mgc_event_sequence(text, tcp) ->
{megaco_update_user_info, sent_pending_limit, 100},
start_transport,
listen,
+
+ %% ANNOUNCE READY
+ {trigger, fun() -> CTRL ! announce_mgc end},
+
{megaco_callback, handle_connect, ConnectVerify},
{megaco_conn_info, all},
{megaco_callback, handle_trans_request, ScrVerify},
@@ -1345,8 +1389,15 @@ request_and_reply_pending_ack_one_pending(Config) when is_list(Config) ->
d("[MGC] start the simulation"),
{ok, MgcId} = megaco_test_megaco_generator:exec(Mgc, MgcEvSeq),
- i("wait some time before starting the MG simulator"),
- sleep(1000),
+ %% i("wait some time before starting the MG simulator"),
+ %% sleep(1000),
+
+ i("await MGC ready announcement"),
+ receive
+ announce_mgc ->
+ i("received MGC ready announcement"),
+ ok
+ end,
d("[MG] start the simulator (generator)"),
{ok, Mg} = megaco_test_tcp_generator:start_link("MG", MgNode),
@@ -1417,6 +1468,7 @@ rarpaop_mgc_event_sequence(binary, tcp) ->
rarpaop_mgc_event_sequence(Port, TranspMod, EncMod, EncConf).
rarpaop_mgc_event_sequence(Port, TranspMod, EncMod, EncConf) ->
+ CTRL = self(),
Mid = {deviceName,"ctrl"},
RI = [
{port, Port},
@@ -1429,11 +1481,6 @@ rarpaop_mgc_event_sequence(Port, TranspMod, EncMod, EncConf) ->
NrVerify = ?rarpaop_mgc_verify_notify_req_fun(),
AckVerify = ?rarpaop_mgc_verify_reply_ack_fun(),
DiscoVerify = ?rarpaop_mgc_verify_handle_disconnect_fun(),
-%% ConnectVerify = fun rarpaop_mgc_verify_handle_connect/1,
-%% ScrVerify = rarpaop_mgc_verify_service_change_req_fun(Mid),
-%% NrVerify = rarpaop_mgc_verify_notify_request_fun(),
-%% AckVerify = rarpaop_mgc_verify_reply_ack_fun(),
-%% DiscoVerify = fun rarpaop_mgc_verify_handle_disconnect/1,
EvSeq = [
{debug, true},
{megaco_trace, disable},
@@ -1443,6 +1490,10 @@ rarpaop_mgc_event_sequence(Port, TranspMod, EncMod, EncConf) ->
{megaco_update_user_info, sent_pending_limit, 100},
start_transport,
listen,
+
+ %% ANNOUNCE READY
+ {trigger, fun() -> CTRL ! announce_mgc end},
+
{megaco_callback, handle_connect, ConnectVerify},
{megaco_conn_info, all},
{megaco_callback, handle_trans_request, ScrVerify},
@@ -1715,9 +1766,6 @@ rarpaop_mg_event_sequence(Port, EncMod, EncConf) ->
ScrVerifyFun = ?rarpaop_mg_verify_service_change_rep_msg_fun(),
PendVerifyFun = ?rarpaop_mg_verify_pending_msg_fun(TransId),
NrVerifyFun = ?rarpaop_mg_verify_notify_rep_msg_fun(TransId, TermId),
-%% ScrVerifyFun = rarpaop_mg_verify_service_change_rep_msg_fun(),
-%% PendVerifyFun = rarpaop_mg_verify_pending_msg_fun(TransId),
-%% NrVerifyFun = rarpaop_mg_verify_notify_rep_msg_fun(TransId, TermId),
EvSeq = [{debug, true},
{decode, DecodeFun},
{encode, EncodeFun},
@@ -2033,8 +2081,15 @@ single_trans_req_and_reply(Config) when is_list(Config) ->
d("[MGC] start the simulation"),
{ok, MgcId} = megaco_test_megaco_generator:exec(Mgc, MgcEvSeq),
- i("wait some time before starting the MG simulator"),
- sleep(1000),
+ %% i("wait some time before starting the MG simulator"),
+ %% sleep(1000),
+
+ i("await MGC ready announcement"),
+ receive
+ announce_mgc ->
+ i("received MGC ready announcement"),
+ ok
+ end,
d("[MG] start the simulator (generator)"),
{ok, Mg} = megaco_test_megaco_generator:start_link("MG", MgNode),
@@ -2087,6 +2142,7 @@ single_trans_req_and_reply(Config) when is_list(Config) ->
-endif.
strar_mgc_event_sequence(text, tcp) ->
+ CTRL = self(),
Mid = {deviceName,"ctrl"},
RI = [
{port, 2944},
@@ -2094,18 +2150,10 @@ strar_mgc_event_sequence(text, tcp) ->
{encoding_config, []},
{transport_module, megaco_tcp}
],
- %% Tid = #megaco_term_id{id = ["00000000","00000000","01101101"]},
-%% ReqTmr = #megaco_incr_timer{wait_for = 500,
-%% factor = 1,
-%% max_retries = 1},
ConnectVerify = ?strar_mgc_verify_handle_connect_fun(),
ServiceChangeReqVerify = ?strar_mgc_verify_service_change_req_fun(Mid),
NotifyReqVerify = ?strar_mgc_verify_notify_req_fun(),
DiscoVerify = ?strar_mgc_verify_handle_disconnect_fun(),
-%% ConnectVerify = fun strar_mgc_verify_handle_connect/1,
-%% ServiceChangeReqVerify = strar_mgc_verify_service_change_req_fun(Mid),
-%% NotifyReqVerify = strar_mgc_verify_notify_request_fun(),
-%% DiscoVerify = fun strar_mgc_verify_handle_disconnect/1,
EvSeq = [
{debug, true},
{megaco_trace, disable},
@@ -2113,6 +2161,10 @@ strar_mgc_event_sequence(text, tcp) ->
{megaco_start_user, Mid, RI, []},
start_transport,
listen,
+
+ %% ANNOUNCE READY
+ {trigger, fun() -> CTRL ! announce_mgc end},
+
{megaco_callback, handle_connect, ConnectVerify},
{megaco_callback, handle_trans_request, ServiceChangeReqVerify},
{megaco_callback, handle_trans_request, NotifyReqVerify},
@@ -2359,9 +2411,6 @@ strar_mg_event_sequence(text, tcp) ->
ConnectVerify = ?strar_mg_verify_handle_connect_fun(),
ServiceChangeReplyVerify = ?strar_mg_verify_service_change_reply_fun(),
NotifyReplyVerify = ?strar_mg_verify_notify_reply_fun(),
-%% ConnectVerify = strar_mg_verify_handle_connect_fun(),
-%% ServiceChangeReplyVerify = strar_mg_verify_service_change_reply_fun(),
-%% NotifyReplyVerify = fun strar_mg_verify_notify_reply/1,
EvSeq = [
{debug, true},
megaco_start,
@@ -2548,8 +2597,15 @@ single_trans_req_and_reply_sendopts(Config) when is_list(Config) ->
d("[MGC] start the simulation"),
{ok, MgcId} = megaco_test_megaco_generator:exec(Mgc, MgcEvSeq),
- i("wait some time before starting the MG simulator"),
- sleep(1000),
+ %% i("wait some time before starting the MG simulator"),
+ %% sleep(1000),
+
+ i("await MGC ready announcement"),
+ receive
+ announce_mgc ->
+ i("received MGC ready announcement"),
+ ok
+ end,
d("[MG] start the simulator (generator)"),
{ok, Mg} = megaco_test_megaco_generator:start_link("MG", MgNode),
@@ -2602,6 +2658,7 @@ single_trans_req_and_reply_sendopts(Config) when is_list(Config) ->
-endif.
straro_mgc_event_sequence(text, tcp) ->
+ CTRL = self(),
Mid = {deviceName,"ctrl"},
RI = [
{port, 2944},
@@ -2613,10 +2670,6 @@ straro_mgc_event_sequence(text, tcp) ->
ServiceChangeReqVerify = ?straro_mgc_verify_service_change_req_fun(Mid),
NotifyReqVerify = ?straro_mgc_verify_notify_req_fun(),
TransAckVerify = ?straro_mgc_verify_handle_trans_ack_fun(),
-%% ConnectVerify = fun straro_mgc_verify_handle_connect/1,
-%% ServiceChangeReqVerify = straro_mgc_verify_service_change_req_fun(Mid),
-%% NotifyReqVerify = straro_mgc_verify_notify_request_fun(),
-%% TransAckVerify = straro_mgc_verify_handle_trans_ack_fun(),
EvSeq = [
{debug, true},
{megaco_trace, disable},
@@ -2624,6 +2677,10 @@ straro_mgc_event_sequence(text, tcp) ->
{megaco_start_user, Mid, RI, []},
start_transport,
listen,
+
+ %% ANNOUNCE READY
+ {trigger, fun() -> CTRL ! announce_mgc end},
+
{megaco_callback, handle_connect, ConnectVerify},
{megaco_callback, handle_trans_request, ServiceChangeReqVerify},
{megaco_callback, handle_trans_request, NotifyReqVerify},
@@ -3095,8 +3152,15 @@ request_and_reply_and_ack(Config) when is_list(Config) ->
d("[MGC] start the simulation"),
{ok, MgcId} = megaco_test_megaco_generator:exec(Mgc, MgcEvSeq),
- i("wait some time before starting the MG simulator"),
- sleep(1000),
+ %% i("wait some time before starting the MG simulator"),
+ %% sleep(1000),
+
+ i("await MGC ready announcement"),
+ receive
+ announce_mgc ->
+ i("received MGC ready announcement"),
+ ok
+ end,
d("[MG] start the simulator (generator)"),
{ok, Mg} = megaco_test_tcp_generator:start_link("MG", MgNode),
@@ -3154,6 +3218,7 @@ request_and_reply_and_ack(Config) when is_list(Config) ->
-endif.
raraa_mgc_event_sequence(text, tcp) ->
+ CTRL = self(),
Mid = {deviceName,"ctrl"},
RI = [
{port, 2944},
@@ -3166,11 +3231,6 @@ raraa_mgc_event_sequence(text, tcp) ->
NrVerify = ?raraa_mgc_verify_notify_req_fun(),
AckVerify = ?raraa_mgc_verify_handle_trans_ack_fun(),
DiscoVerify = ?raraa_mgc_verify_handle_disconnect_fun(),
-%% ConnectVerify = fun raraa_mgc_verify_handle_connect/1,
-%% ScrVerify = raraa_mgc_verify_service_change_req_fun(Mid),
-%% NrVerify = raraa_mgc_verify_notify_request_fun(),
-%% AckVerify = raraa_mgc_verify_trans_ack_fun(),
-%% DiscoVerify = fun raraa_mgc_verify_handle_disconnect/1,
EvSeq = [
{debug, true},
{megaco_trace, disable},
@@ -3180,6 +3240,10 @@ raraa_mgc_event_sequence(text, tcp) ->
{megaco_update_user_info, sent_pending_limit, 100},
start_transport,
listen,
+
+ %% ANNOUNCE READY
+ {trigger, fun() -> CTRL ! announce_mgc end},
+
{megaco_callback, handle_connect, ConnectVerify},
{megaco_conn_info, all},
{megaco_callback, handle_trans_request, ScrVerify},
@@ -3437,9 +3501,6 @@ raraa_mg_event_sequence(text, tcp) ->
ScrVerifyFun = ?raraa_mg_verify_service_change_rep_msg_fun(),
NrVerifyFun = ?raraa_mg_verify_notify_rep_msg_fun(TermId,
TransId, ReqId, CtxId),
-%% ScrVerifyFun = raraa_mg_verify_service_change_rep_msg_fun(),
-%% NrVerifyFun = raraa_mg_verify_notify_rep_msg_fun(TermId,
-%% TransId, ReqId, CtxId),
EvSeq = [{debug, true},
{decode, DecodeFun},
{encode, EncodeFun},
@@ -3712,8 +3773,15 @@ request_and_reply_and_no_ack(Config) when is_list(Config) ->
d("[MGC] start the simulation"),
{ok, MgcId} = megaco_test_megaco_generator:exec(Mgc, MgcEvSeq),
- i("wait some time before starting the MG simulator"),
- sleep(1000),
+ %% i("wait some time before starting the MG simulator"),
+ %% sleep(1000),
+
+ i("await MGC ready announcement"),
+ receive
+ announce_mgc ->
+ i("received MGC ready announcement"),
+ ok
+ end,
d("[MG] start the simulator (generator)"),
{ok, Mg} = megaco_test_tcp_generator:start_link("MG", MgNode),
@@ -3771,6 +3839,7 @@ request_and_reply_and_no_ack(Config) when is_list(Config) ->
-endif.
rarana_mgc_event_sequence(text, tcp) ->
+ CTRL = self(),
Mid = {deviceName,"ctrl"},
RI = [
{port, 2944},
@@ -3783,11 +3852,6 @@ rarana_mgc_event_sequence(text, tcp) ->
NrVerify = ?rarana_mgc_verify_notify_req_fun(),
AckVerify = ?rarana_mgc_verify_handle_trans_ack_fun(),
DiscoVerify = ?rarana_mgc_verify_handle_disconnect_fun(),
-%% ConnectVerify = fun rarana_mgc_verify_handle_connect/1,
-%% ScrVerify = rarana_mgc_verify_service_change_req_fun(Mid),
-%% NrVerify = rarana_mgc_verify_notify_request_fun(),
-%% AckVerify = rarana_mgc_verify_trans_ack_fun(),
-%% DiscoVerify = fun rarana_mgc_verify_handle_disconnect/1,
EvSeq = [
{debug, true},
{megaco_trace, disable},
@@ -3798,6 +3862,10 @@ rarana_mgc_event_sequence(text, tcp) ->
{megaco_update_user_info, reply_timer, 9000},
start_transport,
listen,
+
+ %% ANNOUNCE READY
+ {trigger, fun() -> CTRL ! announce_mgc end},
+
{megaco_callback, handle_connect, ConnectVerify},
{megaco_conn_info, all},
{megaco_callback, handle_trans_request, ScrVerify},
@@ -4048,9 +4116,6 @@ rarana_mg_event_sequence(text, tcp) ->
ScrVerifyFun = ?rarana_mg_verify_service_change_rep_msg_fun(),
NrVerifyFun = ?rarana_mg_verify_notify_rep_msg_fun(TermId,
TransId, ReqId, CtxId),
-%% ScrVerifyFun = rarana_mg_verify_service_change_rep_msg_fun(),
-%% NrVerifyFun = rarana_mg_verify_notify_rep_msg_fun(TermId,
-%% TransId, ReqId, CtxId),
EvSeq = [{debug, true},
{decode, DecodeFun},
{encode, EncodeFun},
@@ -4315,8 +4380,15 @@ request_and_reply_and_late_ack(Config) when is_list(Config) ->
d("[MGC] start the simulation"),
{ok, MgcId} = megaco_test_megaco_generator:exec(Mgc, MgcEvSeq),
- i("wait some time before starting the MG simulator"),
- sleep(1000),
+ %% i("wait some time before starting the MG simulator"),
+ %% sleep(1000),
+
+ i("await MGC ready announcement"),
+ receive
+ announce_mgc ->
+ i("received MGC ready announcement"),
+ ok
+ end,
d("[MG] start the simulator (generator)"),
{ok, Mg} = megaco_test_tcp_generator:start_link("MG", MgNode),
@@ -4374,6 +4446,7 @@ request_and_reply_and_late_ack(Config) when is_list(Config) ->
-endif.
rarala_mgc_event_sequence(text, tcp) ->
+ CTRL = self(),
Mid = {deviceName,"ctrl"},
RI = [
{port, 2944},
@@ -4391,11 +4464,6 @@ rarala_mgc_event_sequence(text, tcp) ->
NrVerify = ?rarala_mgc_verify_notify_req_fun(),
AckVerify = ?rarala_mgc_verify_handle_trans_ack_fun(),
DiscoVerify = ?rarala_mgc_verify_handle_disconnect_fun(),
-%% ConnectVerify = fun rarala_mgc_verify_handle_connect/1,
-%% ScrVerify = rarala_mgc_verify_service_change_req_fun(Mid),
-%% NrVerify = rarala_mgc_verify_notify_request_fun(),
-%% AckVerify = rarala_mgc_verify_trans_ack_fun(),
-%% DiscoVerify = fun rarala_mgc_verify_handle_disconnect/1,
EvSeq = [
{debug, true},
{megaco_trace, disable},
@@ -4406,6 +4474,10 @@ rarala_mgc_event_sequence(text, tcp) ->
{megaco_update_user_info, reply_timer, RepTmr},
start_transport,
listen,
+
+ %% ANNOUNCE READY
+ {trigger, fun() -> CTRL ! announce_mgc end},
+
{megaco_callback, handle_connect, ConnectVerify},
{megaco_conn_info, all},
{megaco_callback, handle_trans_request, ScrVerify},
@@ -4663,9 +4735,6 @@ rarala_mg_event_sequence(text, tcp) ->
ScrVerifyFun = ?rarala_mg_verify_service_change_rep_msg_fun(),
NrVerifyFun = ?rarala_mg_verify_notify_rep_msg_fun(TermId,
TransId, ReqId, CtxId),
-%% ScrVerifyFun = rarala_mg_verify_service_change_rep_msg_fun(),
-%% NrVerifyFun = rarala_mg_verify_notify_rep_msg_fun(TermId,
-%% TransId, ReqId, CtxId),
EvSeq = [{debug, true},
{decode, DecodeFun},
{encode, EncodeFun},
@@ -4949,8 +5018,15 @@ trans_req_and_reply_and_req(Config) when is_list(Config) ->
d("[MGC] start the simulation"),
{ok, MgcId} = megaco_test_megaco_generator:exec(Mgc, MgcEvSeq),
- i("wait some time before starting the MG simulator"),
- sleep(1000),
+ %% i("wait some time before starting the MG simulator"),
+ %% sleep(1000),
+
+ i("await MGC ready announcement"),
+ receive
+ announce_mgc ->
+ i("received MGC ready announcement"),
+ ok
+ end,
d("[MG] start the simulator (generator)"),
{ok, Mg} = megaco_test_tcp_generator:start_link("MG", MgNode),
@@ -5003,6 +5079,7 @@ trans_req_and_reply_and_req(Config) when is_list(Config) ->
-endif.
trarar_mgc_event_sequence(text, tcp) ->
+ CTRL = self(),
Mid = {deviceName,"ctrl"},
RI = [
{port, 2944},
@@ -5010,10 +5087,6 @@ trarar_mgc_event_sequence(text, tcp) ->
{encoding_config, []},
{transport_module, megaco_tcp}
],
-%% Tid = #megaco_term_id{id = ["00000000","00000000","01101101"]},
-%% ReqTmr = #megaco_incr_timer{wait_for = 500,
-%% factor = 1,
-%% max_retries = 1},
ConnectVerify = ?trarar_mgc_verify_handle_connect_fun(),
ServiceChangeReqVerify = ?trarar_mgc_verify_service_change_req_fun(Mid),
NotifyReqVerify1 = ?trarar_mgc_verify_notify_req_fun(1),
@@ -5021,13 +5094,6 @@ trarar_mgc_event_sequence(text, tcp) ->
NotifyReqVerify3 = ?trarar_mgc_verify_notify_req_fun(3),
NotifyReqVerify4 = ?trarar_mgc_verify_notify_req_fun(4),
DiscoVerify = ?trarar_mgc_verify_handle_disconnect_fun(),
-%% ConnectVerify = fun trarar_mgc_verify_handle_connect/1,
-%% ServiceChangeReqVerify = trarar_mgc_verify_service_change_req_fun(Mid),
-%% NotifyReqVerify1 = trarar_mgc_verify_notify_request_fun(1),
-%% NotifyReqVerify2 = trarar_mgc_verify_notify_request_fun(2),
-%% NotifyReqVerify3 = trarar_mgc_verify_notify_request_fun(3),
-%% NotifyReqVerify4 = trarar_mgc_verify_notify_request_fun(4),
-%% DiscoVerify = fun trarar_mgc_verify_handle_disconnect/1,
EvSeq = [
{debug, true},
{megaco_trace, disable},
@@ -5036,6 +5102,10 @@ trarar_mgc_event_sequence(text, tcp) ->
{megaco_update_user_info, reply_timer, 2000},
start_transport,
listen,
+
+ %% ANNOUNCE READY
+ {trigger, fun() -> CTRL ! announce_mgc end},
+
{megaco_callback, handle_connect, ConnectVerify},
{megaco_callback, handle_trans_request, ServiceChangeReqVerify},
{megaco_callback, handle_trans_request, NotifyReqVerify1},
@@ -5298,15 +5368,6 @@ trarar_mg_event_sequence(text, tcp) ->
?trarar_mg_verify_notify_rep_msg_fun(TermId, 2, 3, 3),
NrVerifyFun4 =
?trarar_mg_verify_notify_rep_msg_fun(TermId, 2, 4, 4),
-%% ScrVerifyFun = trarar_mg_verify_service_change_rep_msg_fun(),
-%% NrVerifyFun1 =
-%% trarar_mg_verify_notify_rep_msg_fun(TermId, 2, 1, 1),
-%% NrVerifyFun2 =
-%% trarar_mg_verify_notify_rep_msg_fun(TermId, 2, 2, 2),
-%% NrVerifyFun3 =
-%% trarar_mg_verify_notify_rep_msg_fun(TermId, 2, 3, 3),
-%% NrVerifyFun4 =
-%% trarar_mg_verify_notify_rep_msg_fun(TermId, 2, 4, 4),
EvSeq = [{debug, true},
{decode, DecodeFun},
{encode, EncodeFun},
@@ -5613,8 +5674,15 @@ pending_ack_plain(Config) when is_list(Config) ->
d("[MGC] start the simulation"),
{ok, MgcId} = megaco_test_megaco_generator:exec(Mgc, MgcEvSeq),
- i("wait some time before starting the MG simulator"),
- sleep(1000),
+ %% i("wait some time before starting the MG simulator"),
+ %% sleep(1000),
+
+ i("await MGC ready announcement"),
+ receive
+ announce_mgc ->
+ i("received MGC ready announcement"),
+ ok
+ end,
d("[MG] start the simulator (generator)"),
{ok, Mg} = megaco_test_tcp_generator:start_link("MG", MgNode),
@@ -5676,6 +5744,7 @@ pending_ack_plain(Config) when is_list(Config) ->
-endif.
pap_mgc_event_sequence(text, tcp) ->
+ CTRL = self(),
Mid = {deviceName,"ctrl"},
RI = [
{port, 2944},
@@ -5689,12 +5758,6 @@ pap_mgc_event_sequence(text, tcp) ->
NrVerify2 = ?pap_mgc_verify_notify_req_long_fun(),
AckVerify = ?pap_mgc_verify_handle_trans_ack_fun(),
DiscoVerify = ?pap_mgc_verify_handle_disconnect_fun(),
-%% ConnectVerify = fun pap_mgc_verify_handle_connect/1,
-%% ScrVerify = pap_mgc_verify_service_change_req_fun(Mid),
-%% NrVerify1 = pap_mgc_verify_notify_request_fun(),
-%% NrVerify2 = pap_mgc_verify_notify_request_long_fun(),
-%% AckVerify = pap_mgc_verify_trans_ack_fun(),
-%% DiscoVerify = fun pap_mgc_verify_handle_disconnect/1,
EvSeq = [
{debug, true},
{megaco_trace, disable},
@@ -5704,6 +5767,10 @@ pap_mgc_event_sequence(text, tcp) ->
{megaco_update_user_info, sent_pending_limit, 100},
start_transport,
listen,
+
+ %% ANNOUNCE READY
+ {trigger, fun() -> CTRL ! announce_mgc end},
+
{megaco_callback, handle_connect, ConnectVerify},
{megaco_conn_info, all},
{megaco_callback, handle_trans_request, ScrVerify},
@@ -5983,11 +6050,6 @@ pap_mg_event_sequence(text, tcp) ->
?pap_mg_verify_pending_msg_fun(TransId),
NrVerifyFun =
?pap_mg_verify_notify_rep_msg_fun(TermId, TransId, ReqId, CtxId),
-%% ScrVerifyFun = pap_mg_verify_service_change_rep_msg_fun(),
-%% PendingVerifyFun =
-%% pap_mg_verify_pending_msg_fun(TransId),
-%% NrVerifyFun =
-%% pap_mg_verify_notify_rep_msg_fun(TermId, TransId, ReqId, CtxId),
EvSeq = [{debug, true},
{decode, DecodeFun},
{encode, EncodeFun},
@@ -6305,8 +6367,15 @@ request_and_pending_and_late_reply(Config) when is_list(Config) ->
d("[MGC] start the simulation"),
{ok, MgcId} = megaco_test_tcp_generator:exec(Mgc, MgcEvSeq),
- i("wait some time before starting the MG simulator"),
- sleep(1000),
+ %% i("wait some time before starting the MG simulator"),
+ %% sleep(1000),
+
+ i("await MGC ready announcement"),
+ receive
+ announce_mgc ->
+ i("received MGC ready announcement"),
+ ok
+ end,
d("[MG] start the simulator (generator)"),
{ok, Mg} = megaco_test_megaco_generator:start_link("MG", MgNode),
@@ -6364,6 +6433,7 @@ request_and_pending_and_late_reply(Config) when is_list(Config) ->
-endif.
rapalr_mgc_event_sequence(text, tcp) ->
+ CTRL = self(),
DecodeFun = ?rapalr_mgc_decode_msg_fun(megaco_pretty_text_encoder, []),
EncodeFun = ?rapalr_mgc_encode_msg_fun(megaco_pretty_text_encoder, []),
Mid = {deviceName,"mgc"},
@@ -6380,14 +6450,14 @@ rapalr_mgc_event_sequence(text, tcp) ->
NrVerifyFun =
?rapalr_mgc_verify_notify_req_msg_fun(TermId, TransId, ReqId, CtxId),
AckVerifyFun = ?rapalr_mgc_verify_trans_ack_msg_fun(TransId),
-%% ScrVerifyFun = rapalr_mgc_verify_service_change_req_msg_fun(),
-%% NrVerifyFun =
-%% rapalr_mgc_verify_notify_req_msg_fun(TermId, TransId, ReqId, CtxId),
-%% AckVerifyFun = rapalr_mgc_verify_trans_ack_msg_fun(TransId),
EvSeq = [{debug, false},
{decode, DecodeFun},
{encode, EncodeFun},
{listen, 2944},
+
+ %% ANNOUNCE READY
+ {trigger, "announce ready", fun() -> CTRL ! announce_mgc end},
+
{expect_accept, any},
{expect_receive, "service-change-request", {ScrVerifyFun, 5000}},
{send, "service-change-reply", ServiceChangeRep},
@@ -6866,12 +6936,12 @@ dist(Config) when is_list(Config) ->
?SKIP("Needs a re-write..."),
[_Local, Dist] = ?ACQUIRE_NODES(2, Config),
d("dist -> start proxy",[]),
- megaco_mess_user_test:start_proxy(),
+ ?USER_MOD:start_proxy(),
PrelMid = preliminary_mid,
MgMid = ipv4_mid(4711),
MgcMid = ipv4_mid(),
- UserMod = megaco_mess_user_test,
+ UserMod = ?USER_MOD,
d("dist -> start megaco app",[]),
?VERIFY(ok, application:start(megaco)),
UserConfig = [{user_mod, UserMod}, {send_mod, UserMod},
@@ -6977,7 +7047,7 @@ dist(Config) when is_list(Config) ->
?RECEIVE([]),
d("dist -> stop proxy",[]),
- megaco_mess_user_test:stop_proxy(),
+ ?USER_MOD:stop_proxy(),
d("dist -> done", []),
ok.
@@ -7140,6 +7210,13 @@ otp_4836(Config) when is_list(Config) ->
d("start the MGC simulation"),
{ok, MgcId} = megaco_test_tcp_generator:exec(Mgc, MgcEvSeq),
+ i("await MGC ready announcement"),
+ receive
+ announce_mgc ->
+ i("received MGC ready announcement"),
+ ok
+ end,
+
i("[MG] start"),
MgMid = {deviceName, "mg"},
ReqTmr = #megaco_incr_timer{wait_for = 3000,
@@ -7204,6 +7281,7 @@ otp_4836(Config) when is_list(Config) ->
-endif.
otp_4836_mgc_event_sequence(text, tcp) ->
+ CTRL = self(),
DecodeFun = ?otp_4836_mgc_decode_msg_fun(megaco_pretty_text_encoder, []),
EncodeFun = ?otp_4836_mgc_encode_msg_fun(megaco_pretty_text_encoder, []),
Mid = {deviceName,"ctrl"},
@@ -7217,6 +7295,10 @@ otp_4836_mgc_event_sequence(text, tcp) ->
{decode, DecodeFun},
{encode, EncodeFun},
{listen, 2944},
+
+ %% ANNOUNCE READY
+ {trigger, "announce ready", fun() -> CTRL ! announce_mgc end},
+
{expect_accept, any},
{expect_receive, "service-change-request", {ServiceChangeVerifyFun, 10000}},
{send, "service-change-reply", ServiceChangeReply},
@@ -7385,8 +7467,15 @@ otp_5805(Config) when is_list(Config) ->
{ok, MgcId} =
megaco_test_megaco_generator:exec(Mgc, MgcEvSeq, timer:minutes(1)),
- i("wait some time before starting the MG simulator"),
- sleep(1000),
+ %% i("wait some time before starting the MG simulator"),
+ %% sleep(1000),
+
+ i("await MGC ready announcement"),
+ receive
+ announce_mgc ->
+ i("received MGC ready announcement"),
+ ok
+ end,
d("start the MG simulator (generator)"),
{ok, Mg} = megaco_test_tcp_generator:start_link("MG", MgNode),
@@ -7444,9 +7533,6 @@ otp_5805_mg_event_sequence(text, tcp) ->
?otp_5805_mg_verify_service_change_rep_msg_fun(),
EDVerify =
?otp_5805_mg_verify_error_descriptor_msg_fun(),
-%% ServiceChangeReplyVerifyFun =
-%% otp_5805_mg_verify_service_change_rep_msg_fun(),
-%% EDVerify = otp_5805_mg_verify_error_descriptor_msg_fun(),
MgEvSeq = [{debug, true},
{decode, DecodeFun},
{encode, EncodeFun},
@@ -7652,6 +7738,7 @@ Transaction = 2 {
-endif.
otp_5805_mgc_event_sequence(text, tcp) ->
+ CTRL = self(),
Mid = {deviceName,"ctrl"},
RI = [
{port, 2944},
@@ -7664,11 +7751,6 @@ otp_5805_mgc_event_sequence(text, tcp) ->
SyntaxErrorVerify1 = ?otp_5805_mgc_verify_handle_syntax_error_fun(),
SyntaxErrorVerify2 = ?otp_5805_mgc_verify_handle_syntax_error_fun(),
DiscoVerify = ?otp_5805_mgc_verify_handle_disconnect_fun(),
-%% ConnectVerify = fun otp_5805_mgc_verify_handle_connect/1,
-%% ServiceChangeReqVerify = otp_5805_mgc_verify_service_change_req_fun(Mid),
-%% SyntaxErrorVerify1 = fun otp_5805_mgc_verify_handle_syntax_error/1,
-%% SyntaxErrorVerify2 = fun otp_5805_mgc_verify_handle_syntax_error/1,
-%% DiscoVerify = fun otp_5805_mgc_verify_handle_disconnect/1,
EvSeq = [
{debug, true},
{megaco_trace, disable},
@@ -7677,6 +7759,10 @@ otp_5805_mgc_event_sequence(text, tcp) ->
{megaco_start_user, Mid, RI, []},
start_transport,
listen,
+
+ %% ANNOUNCE READY
+ {trigger, fun() -> CTRL ! announce_mgc end},
+
{megaco_callback, handle_connect, ConnectVerify},
{megaco_conn_info, all},
{megaco_callback, handle_trans_request_sc, ServiceChangeReqVerify},
@@ -7849,6 +7935,13 @@ otp_5881(Config) when is_list(Config) ->
d("start the MGC simulation"),
{ok, MgcId} = megaco_test_tcp_generator:exec(Mgc, MgcEvSeq),
+ i("await MGC ready announcement"),
+ receive
+ announce_mgc ->
+ i("received MGC ready announcement"),
+ ok
+ end,
+
i("[MG] start"),
MgMid = {deviceName, "mg"},
ReqTmr = #megaco_incr_timer{wait_for = 3000,
@@ -7930,6 +8023,7 @@ otp_5881_verify_trans_id(Mg, Expected) ->
-endif.
otp_5881_mgc_event_sequence(text, tcp) ->
+ CTRL = self(),
DecodeFun = ?otp_5881_mgc_decode_msg_fun(megaco_pretty_text_encoder, []),
EncodeFun = ?otp_5881_mgc_encode_msg_fun(megaco_pretty_text_encoder, []),
Mid = {deviceName,"ctrl"},
@@ -7939,12 +8033,14 @@ otp_5881_mgc_event_sequence(text, tcp) ->
%% Pending = otp_5881_pending_msg(Mid,2),
ServiceChangeVerifyFun = ?otp_5881_mgc_verify_service_change_req_msg_fun(),
NotifyReqVerifyFun = ?otp_5881_mgc_verify_notify_req_msg_fun(),
-%% ServiceChangeVerifyFun = otp_5881_verify_service_change_req_msg_fun(),
-%% NotifyReqVerifyFun = otp_5881_verify_notify_request_fun(),
MgcEvSeq = [{debug, true},
{decode, DecodeFun},
{encode, EncodeFun},
{listen, 2944},
+
+ %% ANNOUNCE READY
+ {trigger, "announce ready", fun() -> CTRL ! announce_mgc end},
+
{expect_accept, any},
{expect_receive, "service-change-request", {ServiceChangeVerifyFun, 10000}},
{send, "service-change-reply", ServiceChangeReply},
@@ -8092,6 +8188,13 @@ otp_5887(Config) when is_list(Config) ->
d("start the MGC simulation"),
{ok, MgcId} = megaco_test_tcp_generator:exec(Mgc, MgcEvSeq),
+ i("await MGC ready announcement"),
+ receive
+ announce_mgc ->
+ i("received MGC ready announcement"),
+ ok
+ end,
+
i("[MG] start"),
MgMid = {deviceName, "mg"},
ReqTmr = #megaco_incr_timer{wait_for = 3000,
@@ -8203,6 +8306,7 @@ otp_5887_verify_trans_id(F, Expected) ->
-endif.
otp_5887_mgc_event_sequence(text, tcp) ->
+ CTRL = self(),
DecodeFun = ?otp_5887_mgc_decode_msg_fun(megaco_pretty_text_encoder, []),
EncodeFun = ?otp_5887_mgc_encode_msg_fun(megaco_pretty_text_encoder, []),
Mid = {deviceName,"ctrl"},
@@ -8211,12 +8315,14 @@ otp_5887_mgc_event_sequence(text, tcp) ->
NotifyReply = otp_5887_notify_reply_msg(Mid, 2, 0, TermId),
ServiceChangeVerifyFun = ?otp_5887_mgc_verify_service_change_req_msg_fun(),
NotifyReqVerifyFun = ?otp_5887_mgc_verify_notify_req_msg_fun(),
-%% ServiceChangeVerifyFun = otp_5887_verify_service_change_req_msg_fun(),
-%% NotifyReqVerifyFun = otp_5887_verify_notify_request_fun(),
MgcEvSeq = [{debug, true},
{decode, DecodeFun},
{encode, EncodeFun},
{listen, 2944},
+
+ %% ANNOUNCE READY
+ {trigger, "announce ready", fun() -> CTRL ! announce_mgc end},
+
{expect_accept, any},
{expect_receive, "service-change-request", {ServiceChangeVerifyFun, 10000}},
{send, "service-change-reply", ServiceChangeReply},
@@ -8362,7 +8468,7 @@ otp_6253(Config) when is_list(Config) ->
MgMid = ipv4_mid(4711),
?VERIFY(ok, application:start(megaco)),
- ?VERIFY(ok, megaco:start_user(MgMid, [{send_mod, megaco_mess_user_test},
+ ?VERIFY(ok, megaco:start_user(MgMid, [{send_mod, ?USER_MOD},
{request_timer, infinity},
{reply_timer, infinity}])),
@@ -8425,6 +8531,13 @@ otp_6275(Config) when is_list(Config) ->
d("start the MGC simulation"),
{ok, MgcId} = megaco_test_tcp_generator:exec(Mgc, MgcEvSeq),
+ i("await MGC ready announcement"),
+ receive
+ announce_mgc ->
+ i("received MGC ready announcement"),
+ ok
+ end,
+
d("[MG] start the simulator (generator)"),
{ok, Mg} = megaco_test_megaco_generator:start_link("MG", MgNode),
@@ -8487,9 +8600,6 @@ otp_6275_mg_event_sequence2(Mid, RI) ->
ConnectVerify = ?otp_6275_mg_verify_handle_connect_fun(),
NotifyReqVerify = ?otp_6275_mg_verify_notify_req_fun(),
TransReplyVerify = ?otp_6275_mg_verify_handle_trans_rep_fun(),
-%% ConnectVerify = otp_6275_mg_verify_handle_connect_fun(),
-%% NotifyReqVerify = otp_6275_mg_verify_notify_request_fun(),
-%% TransReplyVerify = otp_6275_mg_verify_trans_reply_fun(),
EvSeq = [
{debug, true},
megaco_start,
@@ -8661,6 +8771,7 @@ otp_6275_mg_service_change_request_ar(_Mid, Cid) ->
-endif.
otp_6275_mgc_event_sequence(text, tcp) ->
+ CTRL = self(),
DecodeFun = ?otp_6275_mgc_decode_msg_fun(megaco_pretty_text_encoder, []),
EncodeFun = ?otp_6275_mgc_encode_msg_fun(megaco_pretty_text_encoder, []),
Mid = {deviceName,"ctrl"},
@@ -8668,14 +8779,15 @@ otp_6275_mgc_event_sequence(text, tcp) ->
NotifyReq = otp_6275_mgc_notify_request_msg(Mid, 2, 1, TermId, 1),
SCRVerifyFun = ?otp_6275_mgc_verify_service_change_req_msg_fun(),
NotifyReplyVerifyFun = ?otp_6275_mgc_verify_notify_rep_msg_fun(),
-%% SCRVerifyFun = otp_6275_mgc_verify_service_change_req_fun(),
-%% NotifyReplyVerifyFun = otp_6275_mgc_verify_notify_reply_fun(),
MgcEvSeq =
[{debug, true},
{decode, DecodeFun},
{encode, EncodeFun},
{listen, 2944},
+ %% ANNOUNCE READY
+ {trigger, "announce ready", fun() -> CTRL ! announce_mgc end},
+
{expect_accept, any},
{expect_receive, "service-change-request", {SCRVerifyFun, 5000}},
{sleep, 1000}, %% Do _not_ send SC reply
@@ -8918,8 +9030,15 @@ otp_6276(Config) when is_list(Config) ->
d("[MGC] start the tcp-simulation"),
{ok, MgcId} = megaco_test_tcp_generator:exec(Mgc, MgcEvSeq),
- i("wait some time before starting the MG simulator"),
- sleep(1000),
+ %% i("wait some time before starting the MG simulator"),
+ %% sleep(1000),
+
+ i("await MGC ready announcement"),
+ receive
+ announce_mgc ->
+ i("received MGC ready announcement"),
+ ok
+ end,
d("[MG] start the simulator (generator)"),
{ok, Mg} = megaco_test_megaco_generator:start_link("MG", MgNode),
@@ -9112,6 +9231,7 @@ otp_6276_mgc_event_sequence(text, tcp, Ctrl) ->
otp_6276_mgc_event_sequence2(Mid, EM, EC, Ctrl).
otp_6276_mgc_event_sequence2(Mid, EM, EC, Ctrl) ->
+ CTRL = self(),
DecodeFun = otp_6276_mgc_decode_msg_fun(EM, EC),
EncodeFun = otp_6276_mgc_encode_msg_fun(EM, EC),
AnnounceMe = otp_6276_mgc_announce_fun(Ctrl),
@@ -9131,6 +9251,10 @@ otp_6276_mgc_event_sequence2(Mid, EM, EC, Ctrl) ->
{decode, DecodeFun},
{encode, EncodeFun},
{listen, 2944},
+
+ %% ANNOUNCE READY
+ {trigger, fun() -> CTRL ! announce_mgc end},
+
{expect_accept, any},
{expect_receive, "service-change-req",
{ServiceChangeReqVerify, 10000}},
@@ -11015,12 +11139,12 @@ otp_6865_request_and_reply_plain_extra1(Config) when is_list(Config) ->
ok = megaco_tc_controller:insert(extra_transport_info, ExtraInfo),
d("start proxy",[]),
- megaco_mess_user_test:start_proxy(),
+ ?USER_MOD:start_proxy(),
PrelMid = preliminary_mid,
MgMid = ipv4_mid(4711),
MgcMid = ipv4_mid(),
- UserMod = megaco_mess_user_test,
+ UserMod = ?USER_MOD,
d("start megaco app",[]),
?VERIFY(ok, application:start(megaco)),
UserConfig = [{user_mod, UserMod}, {send_mod, UserMod},
@@ -11133,8 +11257,12 @@ otp_6865_request_and_reply_plain_extra2(Config) when is_list(Config) ->
d("[MGC] start the simulation"),
{ok, MgcId} = megaco_test_megaco_generator:exec(Mgc, MgcEvSeq),
- i("wait some time before starting the MG simulator"),
- sleep(1000),
+ i("await MGC ready announcement"),
+ receive
+ announce_mgc ->
+ i("received MGC ready announcement"),
+ ok
+ end,
d("[MG] start the simulator (generator)"),
{ok, Mg} = megaco_test_tcp_generator:start_link("MG", MgNode),
@@ -11198,13 +11326,14 @@ otp_6865_request_and_reply_plain_extra2(Config) when is_list(Config) ->
-endif.
otp6865e2_mgc_event_sequence(ExtraInfo, text, tcp) ->
- Mid = {deviceName,"ctrl"},
- RI = [
- {port, 2944},
- {encoding_module, megaco_pretty_text_encoder},
- {encoding_config, []},
- {transport_module, megaco_tcp}
- ],
+ Mid = {deviceName, "ctrl"},
+ CTRL = self(),
+ RI = [
+ {port, 2944},
+ {encoding_module, megaco_pretty_text_encoder},
+ {encoding_config, []},
+ {transport_module, megaco_tcp}
+ ],
ConnectVerify =
?otp6865e2_mgc_verify_handle_connect_fun(ExtraInfo),
ServiceChangeReqVerify =
@@ -11226,6 +11355,10 @@ otp6865e2_mgc_event_sequence(ExtraInfo, text, tcp) ->
{megaco_start_user, Mid, RI, []},
start_transport,
listen,
+
+ %% ANNOUNCE READY
+ {trigger, fun() -> CTRL ! announce_mgc end},
+
{megaco_callback, handle_connect, ConnectVerify},
{megaco_callback, handle_trans_request, ServiceChangeReqVerify},
{megaco_callback, handle_trans_request, NotifyReqVerify1},
@@ -11998,8 +12131,15 @@ otp_7189(Config) when is_list(Config) ->
d("[MGC] start the simulation"),
{ok, MgcId} = megaco_test_megaco_generator:exec(Mgc, MgcEvSeq),
- i("wait some time before starting the MG simulator"),
- sleep(1000),
+ %% i("wait some time before starting the MG simulator"),
+ %% sleep(1000),
+
+ i("await MGC ready announcement"),
+ receive
+ announce_mgc ->
+ i("received MGC ready announcement"),
+ ok
+ end,
d("[MG] start the simulator (generator)"),
{ok, Mg} = megaco_test_tcp_generator:start_link("MG", MgNode),
@@ -12052,6 +12192,7 @@ otp_7189(Config) when is_list(Config) ->
-endif.
otp_7189_mgc_event_sequence(text, tcp) ->
+ CTRL = self(),
Mid = {deviceName,"ctrl"},
RI = [
{port, 2944},
@@ -12105,6 +12246,10 @@ otp_7189_mgc_event_sequence(text, tcp) ->
{megaco_user_info, all},
start_transport,
listen,
+
+ %% ANNOUNCE READY
+ {trigger, fun() -> CTRL ! announce_mgc end},
+
{megaco_callback, handle_connect, ConnectVerify},
{megaco_conn_info, all},
{megaco_callback, handle_trans_request, ScrVerify},
@@ -12286,15 +12431,15 @@ otp_7189_mg_event_sequence(text, tcp) ->
?otp_7189_mg_verify_service_change_rep_msg_fun(),
NotifyReqVerify = ?otp_7189_mg_verify_notify_req_msg_fun(TermId, TransId, ReqId, CtxId),
EvSeq = [
- {debug, true},
+ ?GD_ENABLE(),
{decode, DecodeFun},
{encode, EncodeFun},
{connect, 2944},
- {send, "service-change-request", ServiceChangeReq},
- {expect_receive, "service-change-reply", {ServiceChangeReplyVerifyFun, 2000}},
- {expect_receive, "notify request", {NotifyReqVerify, 2000}},
- {sleep, 100},
- {send, "pending", Pending},
+ ?GSND("service-change-request", ServiceChangeReq),
+ ?GERCV("service-change-reply", ServiceChangeReplyVerifyFun, ?SECS(5)),
+ ?GERCV("notify request", NotifyReqVerify, ?SECS(5)),
+ ?GS(100),
+ ?GSND("pending", Pending),
{expect_closed, timer:seconds(120)},
disconnect
],
@@ -12532,7 +12677,7 @@ otp_7259(Config) when is_list(Config) ->
megaco_test_generic_transport:incomming_message(Pid, NotifyReply),
d("[MG] await the generator reply"),
- await_completion([MgId], 5000),
+ await_completion([MgId], 7000),
%% Tell Mg to stop
i("[MG] stop generator"),
@@ -12832,13 +12977,13 @@ otp_7713(Config) when is_list(Config) ->
i("starting"),
d("start proxy",[]),
- megaco_mess_user_test:start_proxy(),
+ ?USER_MOD:start_proxy(),
Extra = otp7713_extra,
PrelMid = preliminary_mid,
MgMid = ipv4_mid(4711),
MgcMid = ipv4_mid(),
- UserMod = megaco_mess_user_test,
+ UserMod = ?USER_MOD,
d("start megaco app",[]),
?VERIFY(ok, application:start(megaco)),
UserConfig = [{user_mod, UserMod}, {send_mod, UserMod},
@@ -12954,10 +13099,11 @@ otp_8183_request1(Config) when is_list(Config) ->
i("wait some before issuing the notify reply (twice)"),
sleep(500),
- i("send the notify reply, twice times"),
+ i("send the notify reply - twice"),
NotifyReply =
otp_8183_r1_mgc_notify_reply_msg(MgcMid, TransId2, Cid2, TermId2),
megaco_test_generic_transport:incomming_message(Pid, NotifyReply),
+ sleep(100), %% This is to "make sure" the events come in the "right" order
megaco_test_generic_transport:incomming_message(Pid, NotifyReply),
d("await the generator reply"),
@@ -13081,8 +13227,8 @@ otp_8183_r1_mgc_reply_msg(Mid, TransId, CR, Cid) ->
{?MODULE, otp_8183_r1_mg_verify_handle_connect, []}).
-define(otp_8183_r1_mg_verify_service_change_rep_fun(),
{?MODULE, otp_8183_r1_mg_verify_service_change_rep, []}).
--define(otp_8183_r1_mg_verify_notify_rep_fun(Nr
- {?MODULE, otp_8183_r1_mg_verify_notify_rep, [Nr).
+-define(otp_8183_r1_mg_verify_notify_rep_fun(Nr),
+ {?MODULE, otp_8183_r1_mg_verify_notify_rep, [Nr]}).
-else.
-define(otp_8183_r1_mg_verify_handle_connect_fun(),
otp_8183_r1_mg_verify_handle_connect_fun()).
@@ -13228,13 +13374,33 @@ otp_8183_r1_mg_verify_notify_rep_fun(Nr) ->
end.
-endif.
-otp_8183_r1_mg_verify_notify_rep(Nr,
+otp_8183_r1_mg_verify_notify_rep(
+ Nr,
{handle_trans_reply, _CH, ?VERSION, {ok, Nr, [AR]}, _}) ->
io:format("otp_8183_r1_mg_verify_notify_rep -> ok"
"~n Nr: ~p"
"~n AR: ~p"
"~n", [Nr, AR]),
{ok, AR, ok};
+otp_8183_r1_mg_verify_notify_rep(
+ ExpNr,
+ {handle_trans_reply, _CH, ?VERSION, {ok, ActNr, [AR]}, _}) ->
+ io:format("otp_8183_r1_mg_verify_notify_rep -> error"
+ "~n Expected Nr: ~p"
+ "~n Actual Nr: ~p"
+ "~n AR: ~p"
+ "~n", [ExpNr, ActNr, AR]),
+ Error = {unexpected_nr, ExpNr, ActNr},
+ {error, Error, ok};
+otp_8183_r1_mg_verify_notify_rep(
+ Nr,
+ {handle_trans_reply, _CH, ?VERSION, Res, _}) ->
+ io:format("otp_8183_r1_mg_verify_notify_rep -> error"
+ "~n Nr: ~p"
+ "~n Res: ~p"
+ "~n", [Nr, Res]),
+ Error = {unexpected_result, Nr, Res},
+ {error, Error, ok};
otp_8183_r1_mg_verify_notify_rep(Nr, Else) ->
io:format("otp_8183_r1_mg_verify_notify_rep -> unknown"
"~n Nr: ~p"
diff --git a/lib/megaco/test/megaco_mess_user_test.erl b/lib/megaco/test/megaco_mess_user_test.erl
index b5a554112e..d2a9c0f290 100644
--- a/lib/megaco/test/megaco_mess_user_test.erl
+++ b/lib/megaco/test/megaco_mess_user_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -86,13 +86,13 @@ reply(Mod, Line, Fun) when is_function(Fun) ->
{?MODULE, Pid, UserCallback} ->
UserReply = Fun(UserCallback),
Pid ! {?MODULE, self(), UserReply},
- UserReply;
- Other ->
- megaco_test_lib:error(Other, Mod, Line),
- {error, Other}
-%% after 1000 ->
-%% megaco_test_lib:error(timeout, Mod, Line),
-%% {error, timeout}
+ UserReply%% ;
+ %% Other ->
+ %% megaco_test_lib:error(Other, Mod, Line),
+ %% {error, Other}
+ after 10000 ->
+ megaco_test_lib:error(timeout, Mod, Line),
+ {error, timeout}
end.
call(UserCallback) ->
diff --git a/lib/megaco/test/megaco_mib_test.erl b/lib/megaco/test/megaco_mib_test.erl
index 06ac7c08ed..9d1d408f18 100644
--- a/lib/megaco/test/megaco_mib_test.erl
+++ b/lib/megaco/test/megaco_mib_test.erl
@@ -264,7 +264,7 @@ connect(Config) when is_list(Config) ->
d("connect -> (Mg1) service change result: ~p", [Res1]),
%% Collect the statistics
- progress("collect MG1 statustics (after service change)"),
+ progress("collect MG1 statistics (after service change)"),
{ok, Mg1Stats1} = get_stats(Mg1, 1),
d("connect -> stats for Mg1: ~n~p", [Mg1Stats1]),
progress("collect MGC statistics (after MG1 service change)"),
@@ -279,7 +279,7 @@ connect(Config) when is_list(Config) ->
d("connect -> (Mg2) service change result: ~p", [Res2]),
%% Collect the statistics
- progress("collect MG2 statustics (after service change)"),
+ progress("collect MG2 statistics (after service change)"),
{ok, Mg2Stats1} = get_stats(Mg2, 1),
d("connect -> stats for Mg1: ~n~p", [Mg2Stats1]),
progress("collect MGC statistics (after MG2 service change)"),
@@ -751,7 +751,7 @@ mgc_init(Config) ->
d("mgc_init -> entry"),
Mid = get_conf(local_mid, Config),
RI = get_conf(receive_info, Config),
- d("mgc_init -> start megaco"),
+ i("mgc_init -> start megaco"),
application:start(megaco),
d("mgc_init -> start megaco user"),
megaco:start_user(Mid, []),
@@ -763,7 +763,7 @@ mgc_init(Config) ->
RH = megaco:user_info(Mid,receive_handle),
d("mgc_init -> parse receive info"),
ListenTo = mgc_parse_receive_info(RI, RH),
- d("mgc_init -> start transports"),
+ i("mgc_init -> start transport(s)"),
{Tcp, Udp} = mgc_start_transports(ListenTo),
{Mid, Tcp, Udp}.
@@ -948,7 +948,7 @@ mgc_start_transports([{_Port, RH}|_ListenTo], _TcpSup, _UdpSup) ->
mgc_start_tcp(RH, Port, undefined) ->
- d("start tcp transport"),
+ i("start tcp transport"),
case megaco_tcp:start_transport() of
{ok, Sup} ->
mgc_start_tcp(RH, Port, Sup);
@@ -956,7 +956,7 @@ mgc_start_tcp(RH, Port, undefined) ->
throw({error, {failed_starting_tcp_transport, Else}})
end;
mgc_start_tcp(RH, Port, Sup) when is_pid(Sup) ->
- d("tcp listen on ~p", [Port]),
+ i("tcp listen on ~p", [Port]),
Opts = [{port, Port},
{receive_handle, RH},
{tcp_options, [{nodelay, true}]}],
@@ -986,7 +986,7 @@ mgc_tcp_create_listen(Sup, Opts, MaxN, N, _InitialReason)
mgc_start_udp(RH, Port, undefined) ->
- d("start udp transport"),
+ i("start udp transport"),
case megaco_udp:start_transport() of
{ok, Sup} ->
mgc_start_udp(RH, Port, Sup);
@@ -994,7 +994,7 @@ mgc_start_udp(RH, Port, undefined) ->
throw({error, {failed_starting_udp_transport, Else}})
end;
mgc_start_udp(RH, Port, Sup) ->
- d("open udp ~p", [Port]),
+ i("open udp ~p", [Port]),
Opts = [{port, Port}, {receive_handle, RH}],
case megaco_udp:open(Sup, Opts) of
{ok, _SendHandle, _ControlPid} ->
@@ -1119,7 +1119,7 @@ mg_init(Config) ->
d("mg_init -> entry"),
Mid = get_conf(local_mid, Config),
RI = get_conf(receive_info, Config),
- d("mg_init -> start megaco"),
+ i("mg_init -> start megaco"),
application:start(megaco),
d("mg_init -> start megaco user"),
megaco:start_user(Mid, []),
@@ -1131,7 +1131,7 @@ mg_init(Config) ->
RH = megaco:user_info(Mid,receive_handle),
d("mg_init -> parse receive info"),
{MgcPort,RH1} = mg_parse_receive_info(RI, RH),
- d("mg_init -> start transport with"),
+ i("mg_init -> start transport(s)"),
ConnHandle = mg_start_transport(MgcPort, RH1),
{Mid, ConnHandle}.
@@ -1255,16 +1255,6 @@ mg_close_conn(CH) ->
SendMod -> exit(Pid, Reason)
end.
-% display_tuple(T) when tuple(T), size(T) > 0 ->
-% i("size(T): ~p", [size(T)]),
-% display_tuple(T,1).
-
-% display_tuple(T,P) when P > size(T) ->
-% ok;
-% display_tuple(T,P) ->
-% i("T[~p]: ~p", [P,element(P,T)]),
-% display_tuple(T,P+1).
-
mg_parse_receive_info(RI, RH) ->
d("mg_parse_receive_info -> get encoding module"),
@@ -1292,7 +1282,7 @@ mg_start_transport(_, #megaco_receive_handle{send_mod = Mod}) ->
mg_start_tcp(MgcPort, RH) ->
- d("start tcp transport"),
+ i("start tcp transport"),
case megaco_tcp:start_transport() of
{ok, Sup} ->
{ok, LocalHost} = inet:gethostname(),
@@ -1316,7 +1306,7 @@ mg_start_tcp(MgcPort, RH) ->
mg_start_udp(MgcPort, RH) ->
- d("start udp transport"),
+ i("start udp transport"),
case megaco_udp:start_transport() of
{ok, Sup} ->
%% Some linux (Ubuntu) has "crap" in their /etc/hosts, that
@@ -1723,6 +1713,7 @@ progress(F) ->
progress(F, []).
progress(F, A) ->
+ io:format("~s " ++ F ++ "~n", [?FTS()|A]),
io:format(user, "~s " ++ F ++ "~n", [?FTS()|A]).
diff --git a/lib/megaco/test/megaco_mreq_test.erl b/lib/megaco/test/megaco_mreq_test.erl
index e6a5ed3181..ba20329ab3 100644
--- a/lib/megaco/test/megaco_mreq_test.erl
+++ b/lib/megaco/test/megaco_mreq_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -24,7 +24,21 @@
%%----------------------------------------------------------------------
-module(megaco_mreq_test).
--compile(export_all).
+-export([
+ all/0,
+ groups/0,
+
+ init_per_group/2,
+ end_per_group/2,
+ init_per_testcase/2,
+ end_per_testcase/2,
+
+ req_and_rep/1,
+ req_and_pending/1,
+ req_and_cancel/1,
+
+ t/0, t/1
+ ]).
-include("megaco_test_lib.hrl").
-include_lib("megaco/include/megaco.hrl").
@@ -51,16 +65,19 @@
-define(MG_START(Pid, Mid, Enc, Transp, Verb),
megaco_test_mg:start(Pid, Mid, Enc, Transp, Verb)).
--define(MG_STOP(Pid), megaco_test_mg:stop(Pid)).
--define(MG_GET_STATS(Pid, No), megaco_test_mg:get_stats(Pid, No)).
--define(MG_RESET_STATS(Pid), megaco_test_mg:reset_stats(Pid)).
--define(MG_SERV_CHANGE(Pid), megaco_test_mg:service_change(Pid)).
--define(MG_NOTIF_RAR(Pid), megaco_test_mg:notify_request_and_reply(Pid)).
--define(MG_NOTIF_REQ(Pid), megaco_test_mg:notify_request(Pid)).
--define(MG_NOTIF_AR(Pid), megaco_test_mg:await_notify_reply(Pid)).
--define(MG_CANCEL(Pid,R), megaco_test_mg:cancel_request(Pid,R)).
+-define(MG_STOP(Pid), megaco_test_mg:stop(Pid)).
+-define(MG_GET_STATS(Pid), megaco_test_mg:get_stats(Pid)).
+-define(MG_RESET_STATS(Pid), megaco_test_mg:reset_stats(Pid)).
+-define(MG_SERV_CHANGE(Pid), megaco_test_mg:service_change(Pid)).
+-define(MG_NOTIF_RAR(Pid), megaco_test_mg:notify_request_and_reply(Pid)).
+-define(MG_NOTIF_REQ(Pid), megaco_test_mg:notify_request(Pid)).
+-define(MG_NOTIF_AR(Pid), megaco_test_mg:await_notify_reply(Pid)).
+-define(MG_CANCEL(Pid,R), megaco_test_mg:cancel_request(Pid,R)).
-define(MG_APPLY_LOAD(Pid,CntStart), megaco_test_mg:apply_load(Pid,CntStart)).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
t() -> megaco_test_lib:t(?MODULE).
t(Case) -> megaco_test_lib:t({?MODULE, Case}).
@@ -74,6 +91,7 @@ end_per_testcase(Case, Config) ->
process_flag(trap_exit, false),
megaco_test_lib:end_per_testcase(Case, Config).
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
all() ->
@@ -228,7 +246,7 @@ req_and_rep_stop_mg(MGs) ->
req_and_rep_get_mg_stats([], Acc) ->
lists:reverse(Acc);
req_and_rep_get_mg_stats([{Name, Pid}|Mgs], Acc) ->
- {ok, Stats} = ?MG_GET_STATS(Pid, 1),
+ {ok, Stats} = ?MG_GET_STATS(Pid),
d("req_and_rep_get_mg_stats -> stats for ~s: ~n~p~n", [Name, Stats]),
req_and_rep_get_mg_stats(Mgs, [{Name, Stats}|Acc]).
@@ -399,11 +417,13 @@ req_and_cancel(Config) when is_list(Config) ->
req_and_cancel_analyze_result({ok,{_PV,Res}}) ->
- d("req_and_cancel -> notify request result: ~n ~p", [Res]),
+ i("req_and_cancel -> notify request result: ~n ~p", [Res]),
req_and_cancel_analyze_result2(Res);
req_and_cancel_analyze_result(Unexpected) ->
exit({unexpected_result,Unexpected}).
+req_and_cancel_analyze_result2({error,{user_cancel,req_and_cancel}}) ->
+ ok;
req_and_cancel_analyze_result2([]) ->
ok;
req_and_cancel_analyze_result2([{error,{user_cancel,req_and_cancel}}|Res]) ->
@@ -429,9 +449,6 @@ sleep(X) ->
receive after X -> ok end.
-error_msg(F,A) -> error_logger:error_msg(F ++ "~n",A).
-
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
i(F) ->
@@ -441,8 +458,8 @@ i(F, A) ->
print(info, get(verbosity), "", F, A).
-d(F) ->
- d(F, []).
+%% d(F) ->
+%% d(F, []).
d(F, A) ->
print(debug, get(verbosity), "DBG: ", F, A).
@@ -456,20 +473,9 @@ print(Severity, Verbosity, P, F, A) ->
print(printable(Severity,Verbosity), P, F, A).
print(true, P, F, A) ->
- io:format("~s~p:~s: " ++ F ++ "~n", [P, self(), get(sname) | A]);
+ io:format("*** [~s] ~s ~p ~s ***"
+ "~n " ++ F ++ "~n",
+ [?FTS(), P, self(), get(sname) | A]);
print(_, _, _, _) ->
ok.
-
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-random_init() ->
- {A,B,C} = now(),
- random:seed(A,B,C).
-
-random() ->
- 10 * random:uniform(50).
-
-apply_load_timer() ->
- erlang:send_after(random(), self(), apply_load_timeout).
-
diff --git a/lib/megaco/test/megaco_pending_limit_test.erl b/lib/megaco/test/megaco_pending_limit_test.erl
index ef3b7e51cf..e0c0468d99 100644
--- a/lib/megaco/test/megaco_pending_limit_test.erl
+++ b/lib/megaco/test/megaco_pending_limit_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -2065,23 +2065,12 @@ await_completion(Ids) ->
?ERROR({failed, Reply})
end.
-%% await_completion(Ids, Timeout) ->
-%% case megaco_test_generator_lib:await_completion(Ids, Timeout) of
-%% {ok, Reply} ->
-%% d("OK => Reply: ~n~p", [Reply]),
-%% ok;
-%% {error, Reply} ->
-%% d("ERROR => Reply: ~n~p", [Reply]),
-%% ?ERROR({failed, Reply})
-%% end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
sleep(X) -> receive after X -> ok end.
-%% error_msg(F,A) -> error_logger:error_msg(F ++ "~n",A).
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -2089,49 +2078,30 @@ i(F) ->
i(F, []).
i(F, A) ->
- print(info, get(verbosity), now(), get(tc), "INF", F, A).
+ print(info, get(verbosity), get(tc), "INF", F, A).
d(F) ->
d(F, []).
d(F, A) ->
- print(debug, get(verbosity), now(), get(tc), "DBG", F, A).
+ print(debug, get(verbosity), get(tc), "DBG", F, A).
printable(_, debug) -> true;
printable(info, info) -> true;
printable(_,_) -> false.
-print(Severity, Verbosity, Ts, Tc, P, F, A) ->
- print(printable(Severity,Verbosity), Ts, Tc, P, F, A).
+print(Severity, Verbosity, Tc, P, F, A) ->
+ print(printable(Severity,Verbosity), Tc, P, F, A).
-print(true, Ts, Tc, P, F, A) ->
+print(true, Tc, P, F, A) ->
io:format("*** [~s] ~s ~p ~s:~w ***"
"~n " ++ F ++ "~n",
- [format_timestamp(Ts), P, self(), get(sname), Tc | A]);
-print(_, _, _, _, _, _) ->
+ [?FTS(), P, self(), get(sname), Tc | A]);
+print(_, _, _, _, _) ->
ok.
-%% print(F, A) ->
-%% io:format("*** [~s] ***"
-%% "~n " ++ F ++ "~n",
-%% [format_timestamp(now()) | A]).
-
-format_timestamp(Now) -> megaco:format_timestamp(Now).
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% random_init() ->
-%% {A,B,C} = now(),
-%% random:seed(A,B,C).
-
-%% random() ->
-%% 10 * random:uniform(50).
-
-%% apply_load_timer() ->
-%% erlang:send_after(random(), self(), apply_load_timeout).
-
-
-
diff --git a/lib/megaco/test/megaco_segment_test.erl b/lib/megaco/test/megaco_segment_test.erl
index ddb8b9f06b..47f708a14b 100644
--- a/lib/megaco/test/megaco_segment_test.erl
+++ b/lib/megaco/test/megaco_segment_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2006-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2006-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -7718,29 +7718,9 @@ await_completion(Ids) ->
?ERROR({failed, Reply})
end.
-%% await_completion(Ids, Timeout) ->
-%% case megaco_test_generator_lib:await_completion(Ids, Timeout) of
-%% {ok, Reply} ->
-%% d("OK => Reply: ~n~p", [Reply]),
-%% ok;
-%% {error, {OK, ERROR}} ->
-%% d("ERROR => "
-%% "~n OK: ~p"
-%% "~n ERROR: ~p", [OK, ERROR]),
-%% ?ERROR({failed, ERROR});
-%% {error, Reply} ->
-%% d("ERROR => Reply: ~n~p", [Reply]),
-%% ?ERROR({failed, Reply})
-%% end.
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% tim() ->
-%% {A,B,C} = erlang:now(),
-%% A*1000000000+B*1000+(C div 1000).
-
-
make_node_name(Name) ->
case string:tokens(atom_to_list(node()), [$@]) of
[_,Host] ->
@@ -7754,8 +7734,6 @@ make_node_name(Name) ->
sleep(X) -> receive after X -> ok end.
-%% error_msg(F,A) -> error_logger:error_msg(F ++ "~n",A).
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -7763,44 +7741,31 @@ i(F) ->
i(F, []).
i(F, A) ->
- print(info, get(verbosity), now(), get(tc), "INF", F, A).
+ print(info, get(verbosity), get(tc), "INF", F, A).
d(F) ->
d(F, []).
d(F, A) ->
- print(debug, get(verbosity), now(), get(tc), "DBG", F, A).
+ print(debug, get(verbosity), get(tc), "DBG", F, A).
printable(_, debug) -> true;
printable(info, info) -> true;
printable(_,_) -> false.
-print(Severity, Verbosity, Ts, Tc, P, F, A) ->
- print(printable(Severity,Verbosity), Ts, Tc, P, F, A).
+print(Severity, Verbosity, Tc, P, F, A) ->
+ print(printable(Severity, Verbosity), Tc, P, F, A).
-print(true, Ts, Tc, P, F, A) ->
+print(true, Tc, P, F, A) ->
io:format("*** [~s] ~s ~p ~s:~w ***"
"~n " ++ F ++ "~n",
- [format_timestamp(Ts), P, self(), get(sname), Tc | A]);
-print(_, _, _, _, _, _) ->
+ [?FTS(), P, self(), get(sname), Tc | A]);
+print(_, _, _, _, _) ->
ok.
-format_timestamp(Now) -> megaco:format_timestamp(Now).
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% random_init() ->
-%% {A,B,C} = now(),
-%% random:seed(A,B,C).
-
-%% random() ->
-%% 10 * random:uniform(50).
-
-%% apply_load_timer() ->
-%% erlang:send_after(random(), self(), apply_load_timeout).
-
-
diff --git a/lib/megaco/test/megaco_tcp_test.erl b/lib/megaco/test/megaco_tcp_test.erl
index a1865ad690..cc66a40f43 100644
--- a/lib/megaco/test/megaco_tcp_test.erl
+++ b/lib/megaco/test/megaco_tcp_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -117,15 +117,39 @@ end_per_testcase(Case, Config) ->
%% Test case definitions
%%======================================================================
all() ->
- [{group, start}, {group, sending}, {group, errors}].
+ [
+ {group, start},
+ {group, sending},
+ {group, errors}
+ ].
groups() ->
- [{start, [],
- [start_normal, start_invalid_opt, start_and_stop]},
- {sending, [], [sendreceive, block_unblock]},
- {errors, [],
- [socket_failure, accept_process, accept_supervisor,
- connection_supervisor, tcp_server]}].
+ [{start, [], start_cases()},
+ {sending, [], sending_cases()},
+ {errors, [], errors_cases()}].
+
+start_cases() ->
+ [
+ start_normal,
+ start_invalid_opt,
+ start_and_stop
+ ].
+
+sending_cases() ->
+ [
+ sendreceive,
+ block_unblock
+ ].
+
+errors_cases() ->
+ [
+ socket_failure,
+ accept_process,
+ accept_supervisor,
+ connection_supervisor,
+ tcp_server
+ ].
+
init_per_group(_GroupName, Config) ->
Config.
@@ -196,17 +220,8 @@ start_and_stop(Config) when is_list(Config) ->
Client = client_start_command_handler(ClientNode, ClientCmds),
p("client command handler started: ~p", [Client]),
- ok =
- receive
- {listening, Server} ->
- p("received listening message from server [~p] => "
- "send continue to client [~p]~n", [Server, Client]),
- Client ! {continue, self()},
- ok
- after 5000 ->
- {error, server_timeout}
- end,
-
+ await_server_listening(Server, Client),
+
await_command_handler_completion([Server, Client], timer:seconds(20)),
p("done"),
ok.
@@ -335,16 +350,7 @@ sendreceive(Config) when is_list(Config) ->
Client = client_start_command_handler(ClientNode, ClientCmds),
p("client command handler started: ~p", [Client]),
- ok =
- receive
- {listening, Server} ->
- p("received listening message from server [~p] => "
- "send continue to client [~p]~n", [Server, Client]),
- Client ! {continue, self()},
- ok
- after 5000 ->
- {error, server_timeout}
- end,
+ await_server_listening(Server, Client),
await_command_handler_completion([Server, Client], timer:seconds(20)),
p("done"),
@@ -544,27 +550,9 @@ block_unblock(Config) when is_list(Config) ->
Client = client_start_command_handler(ClientNode, ClientCmds),
p("client command handler started: ~p", [Client]),
- ok =
- receive
- {listening, Server} ->
- p("received listening message from server [~p] => "
- "send continue to client [~p]~n", [Server, Client]),
- Client ! {continue, self()},
- ok
- after 5000 ->
- {error, server_timeout}
- end,
-
- ok =
- receive
- {blocked, Client} ->
- p("received blocked message from client [~p] => "
- "send continue to server [~p]~n", [Client, Server]),
- Server ! {continue, self()},
- ok
- after 5000 ->
- {error, timeout}
- end,
+ await_server_listening(Server, Client),
+
+ await_client_blocked(Server, Client),
await_command_handler_completion([Server, Client], timer:seconds(30)),
p("done"),
@@ -908,6 +896,42 @@ process_received_message(ReceiveHandle, ControlPid, SendHandle, BinMsg)
%% Internal functions
%%======================================================================
+await_server_listening(Server, Client) ->
+ receive
+ {listening, Server} ->
+ p("received listening message from server [~p] => "
+ "send continue to client [~p]"
+ "~n", [Server, Client]),
+ Client ! {continue, self()},
+ ok
+ after 5000 ->
+ %% There is no normal reason why this should take any time.
+ %% Normally, this takes a few milli seconds. So, if we are not
+ %% up and running after 5 seconds, we give up and skip!!
+ exit(Server, kill),
+ exit(Client, kill),
+ ?SKIP("Server timeout (listen)")
+ end.
+
+
+await_client_blocked(Server, Client) ->
+ receive
+ {blocked, Client} ->
+ p("received blocked message from client [~p] => "
+ "send continue to server [~p]~n", [Client, Server]),
+ Server ! {continue, self()},
+ ok
+ after 5000 ->
+ %% There is no normal reason why this should take any time.
+ %% Normally, this takes a few milli seconds. So, if we are not
+ %% up and running after 5 seconds, we give up and skip!!
+ exit(Client, kill),
+ exit(Server, kill),
+ ?SKIP("Client timeout (blocked)")
+ end.
+
+
+
%% ------- Server command handler and utility functions ----------
server_start_command_handler(Node, Commands) ->
@@ -1214,23 +1238,14 @@ p(F, A) ->
p(S, F, A) when is_list(S) ->
io:format("*** [~s] ~p ~s ***"
"~n " ++ F ++ "~n",
- [format_timestamp(now()), self(), S | A]);
+ [?FTS(), self(), S | A]);
p(_S, F, A) ->
io:format("*** [~s] ~p ~s *** "
"~n " ++ F ++ "~n",
- [format_timestamp(now()), self(), "undefined" | A]).
+ [?FTS(), self(), "undefined" | A]).
ms() ->
- {A,B,C} = erlang:now(),
- A*1000000000+B*1000+(C div 1000).
-
+ erlang:monotonic_time(milli_seconds).
+
-format_timestamp({_N1, _N2, N3} = Now) ->
- {Date, Time} = calendar:now_to_datetime(Now),
- {YYYY,MM,DD} = Date,
- {Hour,Min,Sec} = Time,
- FormatDate =
- io_lib:format("~.4w:~.2.0w:~.2.0w ~.2.0w:~.2.0w:~.2.0w 4~w",
- [YYYY,MM,DD,Hour,Min,Sec,round(N3/1000)]),
- lists:flatten(FormatDate).
diff --git a/lib/megaco/test/megaco_test_deliver.erl b/lib/megaco/test/megaco_test_deliver.erl
index 78033f0e36..c042d9c9a7 100644
--- a/lib/megaco/test/megaco_test_deliver.erl
+++ b/lib/megaco/test/megaco_test_deliver.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -186,4 +186,4 @@ i(F) ->
i(F, []).
i(F, A) ->
- io:format("~p ~w:" ++ F ++ "~n", [self(), ?MODULE | A]).
+ io:format("*** [~s] ~p ~w:" ++ F ++ "~n", [?FTS(), self(), ?MODULE | A]).
diff --git a/lib/megaco/test/megaco_test_generator.erl b/lib/megaco/test/megaco_test_generator.erl
index 63f66bda07..8ea7f5ddf7 100644
--- a/lib/megaco/test/megaco_test_generator.erl
+++ b/lib/megaco/test/megaco_test_generator.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -47,7 +47,6 @@
print/3, print/4
]).
--export([behaviour_info/1]).
%% Internal exports
-export([start/4]).
@@ -65,6 +64,7 @@
-include_lib("megaco/include/megaco.hrl").
+-include("megaco_test_lib.hrl").
%%----------------------------------------------------------------------
@@ -90,15 +90,32 @@
%%% API
%%%=========================================================================
-behaviour_info(callbacks) ->
- [
- {init, 1},
- {handle_parse, 2},
- {handle_exec, 2},
- {terminate, 2}
- ];
-behaviour_info(_Other) ->
- undefined.
+-callback init(Args) -> {ok, State} | {error, Reason} when
+ Args :: term(),
+ State :: term(),
+ Reason :: term().
+
+-callback handle_parse(Instruction, State) ->
+ {ok, NewInstruction, NewState} |
+ {error, Reason} when
+ Instruction :: term(),
+ State :: term(),
+ NewInstruction :: term(),
+ NewState :: term(),
+ Reason :: term().
+
+-callback handle_exec(Instruction, State) ->
+ {ok, NewState} |
+ {error, Reason} when
+ Instruction :: term(),
+ State :: term(),
+ NewState :: term(),
+ Reason :: term().
+
+-callback terminate(Reason, State) ->
+ megaco:void() when
+ Reason :: term(),
+ State :: term().
%%----------------------------------------------------------------------
@@ -533,22 +550,13 @@ print(P, F, A) ->
print([], undefined, F, A) ->
io:format("*** [~s] ~p *** " ++
"~n " ++ F ++ "~n",
- [format_timestamp(now()),self()|A]);
+ [?FTS(), self() | A]);
print(P, undefined, F, A) ->
io:format("*** [~s] ~p ~s *** " ++
"~n " ++ F ++ "~n",
- [format_timestamp(now()),self(),P|A]);
+ [?FTS(), self(), P | A]);
print(P, N, F, A) ->
io:format("*** [~s] ~p ~s~s *** " ++
"~n " ++ F ++ "~n",
- [format_timestamp(now()),self(),N,P|A]).
-
+ [?FTS(), self(), N, P | A]).
-format_timestamp({_N1, _N2, N3} = Now) ->
- {Date, Time} = calendar:now_to_datetime(Now),
- {YYYY, MM, DD} = Date,
- {Hour, Min, Sec} = Time,
- FormatDate =
- io_lib:format("~.4w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w 4~w",
- [YYYY, MM, DD, Hour, Min, Sec, round(N3/1000)]),
- lists:flatten(FormatDate).
diff --git a/lib/megaco/test/megaco_test_generic_transport.erl b/lib/megaco/test/megaco_test_generic_transport.erl
index 3185e4c6b6..cd387f748a 100644
--- a/lib/megaco/test/megaco_test_generic_transport.erl
+++ b/lib/megaco/test/megaco_test_generic_transport.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -59,7 +59,7 @@
receive_handle}).
-include_lib("megaco/include/megaco.hrl").
-%% -include("megaco_test_lib.hrl").
+-include("megaco_test_lib.hrl").
-define(SERVER, ?MODULE).
@@ -335,21 +335,11 @@ d(F) ->
d(F, []).
d(F, A) ->
- print(now(), F, A).
+ print(F, A).
-print(Ts, F, A) ->
+print(F, A) ->
io:format("*** [~s] GENERIC TRANSPORT [~p] ***"
"~n " ++ F ++ "~n",
- [format_timestamp(Ts), self() | A]).
-
-format_timestamp({_N1, _N2, N3} = Now) ->
- {Date, Time} = calendar:now_to_datetime(Now),
- {YYYY,MM,DD} = Date,
- {Hour,Min,Sec} = Time,
- FormatDate =
- io_lib:format("~.4w:~.2.0w:~.2.0w ~.2.0w:~.2.0w:~.2.0w 4~w",
- [YYYY,MM,DD,Hour,Min,Sec,round(N3/1000)]),
- lists:flatten(FormatDate).
-
+ [?FTS(), self() | A]).
diff --git a/lib/megaco/test/megaco_test_lib.erl b/lib/megaco/test/megaco_test_lib.erl
index 0617b96456..3d26a8585f 100644
--- a/lib/megaco/test/megaco_test_lib.erl
+++ b/lib/megaco/test/megaco_test_lib.erl
@@ -127,31 +127,72 @@ non_pc_tc_maybe_skip(Config, Condition, File, Line)
end.
+%% The type and spec'ing is just to increase readability
+-type os_family() :: win32 | unix.
+-type os_name() :: atom().
+-type os_version() :: string() | {non_neg_integer(),
+ non_neg_integer(),
+ non_neg_integer()}.
+-type os_skip_check() :: fun(() -> boolean()) |
+ fun((os_version()) -> boolean()).
+-type skippable() :: any | [os_family() |
+ {os_family(), os_name() |
+ [os_name() | {os_name(),
+ os_skip_check()}]}].
+
+-spec os_based_skip(skippable()) -> boolean().
+
os_based_skip(any) ->
true;
os_based_skip(Skippable) when is_list(Skippable) ->
- {OsFam, OsName} =
- case os:type() of
- {_Fam, _Name} = FamAndName ->
- FamAndName;
- Fam ->
- {Fam, undefined}
- end,
- case lists:member(OsFam, Skippable) of
- true ->
- true;
- false ->
- case lists:keysearch(OsFam, 1, Skippable) of
- {value, {OsFam, OsName}} ->
- true;
- {value, {OsFam, OsNames}} when is_list(OsNames) ->
- lists:member(OsName, OsNames);
- _ ->
- false
- end
- end;
-os_based_skip(_) ->
+ os_base_skip(Skippable, os:type());
+os_based_skip(_Crap) ->
false.
+
+os_base_skip(Skippable, {OsFam, OsName}) ->
+ os_base_skip(Skippable, OsFam, OsName);
+os_base_skip(Skippable, OsFam) ->
+ os_base_skip(Skippable, OsFam, undefined).
+
+os_base_skip(Skippable, OsFam, OsName) ->
+ %% Check if the entire family is to be skipped
+ %% Example: [win32, unix]
+ case lists:member(OsFam, Skippable) of
+ true ->
+ true;
+ false ->
+ %% Example: [{unix, freebsd}] | [{unix, [freebsd, darwin]}]
+ case lists:keysearch(OsFam, 1, Skippable) of
+ {value, {OsFam, OsName}} ->
+ true;
+ {value, {OsFam, OsNames}} when is_list(OsNames) ->
+ %% OsNames is a list of:
+ %% [atom()|{atom(), function/0 | function/1}]
+ case lists:member(OsName, OsNames) of
+ true ->
+ true;
+ false ->
+ os_based_skip_check(OsName, OsNames)
+ end;
+ _ ->
+ false
+ end
+ end.
+
+
+
+%% Performs a check via a provided fun with arity 0 or 1.
+%% The argument is the result of os:version().
+os_based_skip_check(OsName, OsNames) ->
+ case lists:keysearch(OsName, 1, OsNames) of
+ {value, {OsName, Check}} when is_function(Check, 0) ->
+ Check();
+ {value, {OsName, Check}} when is_function(Check, 1) ->
+ Check(os:version());
+ _ ->
+ false
+ end.
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -318,9 +359,9 @@ t({Mod, {group, Name} = Group, Groups}, Config)
[Name, Error]),
[{failed, {Mod, Group}, Error}]
catch
- exit:{skipped, SkipReason} ->
+ exit:{skip, SkipReason} ->
io:format(" => skipping group: ~p~n", [SkipReason]),
- [{skipped, {Mod, Group}, SkipReason, 0}];
+ [{skip, {Mod, Group}, SkipReason, 0}];
error:undef ->
[t({Mod, Case, Groups}, Config) ||
Case <- GroupsAndCases];
@@ -383,9 +424,9 @@ t(Mod, Config) when is_atom(Mod) ->
io:format(" => suite init failed: ~p~n", [Error]),
[{failed, {Mod, init_per_suite}, Error}]
catch
- exit:{skipped, SkipReason} ->
+ exit:{skip, SkipReason} ->
io:format(" => skipping suite: ~p~n", [SkipReason]),
- [{skipped, {Mod, init_per_suite}, SkipReason, 0}];
+ [{skip, {Mod, init_per_suite}, SkipReason, 0}];
error:undef ->
[t({Mod, Case, Groups}, Config) || Case <- Cases];
T:E ->
@@ -458,10 +499,10 @@ wait_for_evaluator(Pid, Mod, Fun, Config, Errors, AccTime) ->
megaco:report_event(20, Mod, ?MODULE, Label ++ " failed",
[TestCase, Config, {return, Fail}, Errors]),
{failed, {Mod,Fun}, Fail, Time};
- {'EXIT', Pid, {skipped, Reason}, Time} ->
+ {'EXIT', Pid, {skip, Reason}, Time} ->
megaco:report_event(20, Mod, ?MODULE, Label ++ " skipped",
- [TestCase, Config, {skipped, Reason}]),
- {skipped, {Mod, Fun}, Errors, Time};
+ [TestCase, Config, {skip, Reason}]),
+ {skip, {Mod, Fun}, Errors, Time};
{'EXIT', Pid, Reason, Time} ->
megaco:report_event(20, Mod, ?MODULE, Label ++ " crashed",
[TestCase, Config, {'EXIT', Reason}]),
@@ -492,14 +533,14 @@ do_eval(ReplyTo, Mod, Fun, Config) ->
error:undef ->
%% p("do_eval -> error - undef", []),
ReplyTo ! {'EXIT', self(), undef, 0};
- exit:{skipped, Reason} ->
+ exit:{skip, Reason} ->
%% p("do_eval -> exit - skipped"
%% "~n Reason: ~p", [Reason]),
T2 = os:timestamp(),
Time = timer:now_diff(T2, T1),
display_tc_time(Time),
display_system_info("after (skipped)", Mod, Fun),
- ReplyTo ! {'EXIT', self(), {skipped, Reason}, Time};
+ ReplyTo ! {'EXIT', self(), {skip, Reason}, Time};
exit:{suite_failed, Reason} ->
%% p("do_eval -> exit - suite-failed"
%% "~n Reason: ~p", [Reason]),
@@ -616,9 +657,9 @@ display_result(Res) when is_list(Res) ->
Ok = [{MF, Time} || {ok, MF, _, Time} <- Res],
Nyi = [MF || {nyi, MF, _, _Time} <- Res],
SkippedGrps = [{{M,G}, Reason} ||
- {skipped, {M, {group, G}}, Reason, _Time} <- Res],
+ {skip, {M, {group, G}}, Reason, _Time} <- Res],
SkippedCases = [{MF, Reason} ||
- {skipped, {_M, F} = MF, Reason, _Time} <- Res,
+ {skip, {_M, F} = MF, Reason, _Time} <- Res,
is_atom(F)],
FailedGrps = [{{M,G}, Reason} ||
{failed, {M, {group, G}}, Reason, _Time} <- Res],
@@ -730,15 +771,18 @@ log(Format, Args, Mod, Line) ->
[self(), Mod, Line] ++ Args)
end.
+skip(Reason) ->
+ exit({skip, Reason}).
+
skip(Actual, File, Line) ->
- log("Skipping test case~n", [], File, Line),
- String = lists:flatten(io_lib:format("Skipping test case ~p(~p): ~p~n",
- [File, Line, Actual])),
- exit({skipped, String}).
+ log("Skipping test case: ~p~n", [Actual], File, Line),
+ String = f("~p(~p): ~p~n", [File, Line, Actual]),
+ skip(String).
fatal_skip(Actual, File, Line) ->
error(Actual, File, Line),
- exit({skipped, {fatal, Actual, File, Line}}).
+ skip({fatal, Actual, File, Line}).
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -750,7 +794,8 @@ flush() ->
after 1000 ->
[]
end.
-
+
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Check if process is alive and kicking
still_alive(Pid) ->
@@ -988,11 +1033,14 @@ node_to_name_and_host(Node) ->
start_nodes([Node | Nodes], File, Line) ->
case net_adm:ping(Node) of
pong ->
+ p("node ~p already running", [Node]),
start_nodes(Nodes, File, Line);
pang ->
[Name, Host] = node_to_name_and_host(Node),
+ p("try start node ~p", [Node]),
case slave:start_link(Host, Name) of
{ok, NewNode} when NewNode =:= Node ->
+ p("node ~p started - now set path, cwd and sync", [Node]),
Path = code:get_path(),
{ok, Cwd} = file:get_cwd(),
true = rpc:call(Node, code, set_path, [Path]),
@@ -1001,6 +1049,7 @@ start_nodes([Node | Nodes], File, Line) ->
{_, []} = rpc:multicall(global, sync, []),
start_nodes(Nodes, File, Line);
Other ->
+ p("failed starting node ~p: ~p", [Node, Other]),
fatal_skip({cannot_start_node, Node, Other}, File, Line)
end
end;
@@ -1014,4 +1063,4 @@ f(F, A) ->
lists:flatten(io_lib:format(F, A)).
p(F, A) ->
- io:format("~p~w:" ++ F ++ "~n", [self(), ?MODULE |A]).
+ io:format("~s ~p " ++ F ++ "~n", [?FTS(), self() | A]).
diff --git a/lib/megaco/test/megaco_test_lib.hrl b/lib/megaco/test/megaco_test_lib.hrl
index 409f8d52e5..546ecaab9a 100644
--- a/lib/megaco/test/megaco_test_lib.hrl
+++ b/lib/megaco/test/megaco_test_lib.hrl
@@ -32,8 +32,6 @@
-define(ERROR(Reason),
megaco_test_lib:error(Reason, ?MODULE, ?LINE)).
--define(F(FMT, ARGS), lists:flatten(io_lib:format(FMT, ARGS))).
-
-define(OS_BASED_SKIP(Skippable),
megaco_test_lib:os_based_skip(Skippable)).
@@ -83,7 +81,10 @@
-define(SLEEP(MSEC), megaco_test_lib:sleep(MSEC)).
-define(HOURS(T), megaco_test_lib:hours(T)).
--define(MINUTES(T), megaco_test_lib:minutes(T)).
--define(SECONDS(T), megaco_test_lib:seconds(T)).
+-define(MINS(T), megaco_test_lib:minutes(T)).
+-define(MINUTES(T), ?MINS(T)).
+-define(SECS(T), megaco_test_lib:seconds(T)).
+-define(SECONDS(T), ?SECS(T)).
-define(FTS(), megaco:format_timestamp(erlang:timestamp())).
-
+-define(FTS(TS), megaco:format_timestamp(TS)).
+-define(F(F,A), lists:flatten(io_lib:format(F, A))).
diff --git a/lib/megaco/test/megaco_test_megaco_generator.erl b/lib/megaco/test/megaco_test_megaco_generator.erl
index 8a37fa33fe..4e0f2e334c 100644
--- a/lib/megaco/test/megaco_test_megaco_generator.erl
+++ b/lib/megaco/test/megaco_test_megaco_generator.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -118,7 +118,7 @@ init([]) ->
%% ----- instruction parser -----
handle_parse({debug, Debug} = Instruction, State)
- when (Debug == true) orelse (Debug == false) ->
+ when is_boolean(Debug) ->
{ok, Instruction, State};
handle_parse({expect_nothing, To} = Instruction, State)
@@ -126,9 +126,9 @@ handle_parse({expect_nothing, To} = Instruction, State)
{ok, Instruction, State};
handle_parse({megaco_trace, Level} = Instruction, State)
- when (Level == disable) orelse
- (Level == max) orelse
- (Level == min) orelse
+ when (Level =:= disable) orelse
+ (Level =:= max) orelse
+ (Level =:= min) orelse
is_integer(Level) ->
{ok, Instruction, State};
@@ -1096,11 +1096,10 @@ handle_megaco_callback_reply(_, _, _, _) ->
%%----------------------------------------------------------------------
random_init() ->
- {A,B,C} = now(),
- random:seed(A,B,C).
+ ok.
random(N) ->
- random:uniform(N).
+ rand:uniform(N).
get_config(Key, Opts) ->
diff --git a/lib/megaco/test/megaco_test_mg.erl b/lib/megaco/test/megaco_test_mg.erl
index d6a9a8c314..38883c94f0 100644
--- a/lib/megaco/test/megaco_test_mg.erl
+++ b/lib/megaco/test/megaco_test_mg.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -79,7 +79,10 @@
reply_counter = 0,
mload_info = undefined,
parent = undefined,
- dsi_timer}).
+ dsi_timer,
+ evs = []}).
+
+-define(EVS_MAX, 10).
%%% --------------------------------------------------------------------
@@ -364,7 +367,7 @@ mg(Parent, Verbosity, Config) ->
MG = #mg{parent = Parent, mid = Mid, dsi_timer = DSITimer},
i("mg -> started"),
put(verbosity, Verbosity),
- case (catch loop(MG)) of
+ case (catch loop(evs(MG, started))) of
{'EXIT', normal} ->
exit(normal);
{'EXIT', Reason} ->
@@ -438,12 +441,12 @@ loop(#mg{parent = Parent, mid = Mid} = S) ->
{display_system_info, Time} ->
display_system_info(S#mg.mid),
NewTimer = create_timer(Time, display_system_info),
- loop(S#mg{dsi_timer = NewTimer});
+ loop(evs(S#mg{dsi_timer = NewTimer}, {dsi, Time}));
{verbosity, V, Parent} ->
i("loop -> received new verbosity: ~p", [V]),
put(verbosity,V),
- loop(S);
+ loop(evs(S, {verb, V}));
{stop, Parent} ->
@@ -453,7 +456,7 @@ loop(#mg{parent = Parent, mid = Mid} = S) ->
Res = do_stop(Mid),
d("loop -> stop result: ~p", [Res]),
server_reply(Parent, stopped, {ok, Res}),
- exit(normal);
+ done(evs(S, stop), normal);
{{enable_test_code, Tag, Fun}, Parent} ->
i("loop -> enable_test_code: ~p, ~p", [Tag, Fun]),
@@ -463,52 +466,52 @@ loop(#mg{parent = Parent, mid = Mid} = S) ->
"~n ets:tab2list(megaco_test_data): ~p",
[Reply,ets:tab2list(megaco_test_data)]),
server_reply(Parent, enable_test_code_reply, Reply),
- loop(S);
+ loop(evs(S, {enable_test_code, Tag}));
{{encode_ar_first, EAF}, Parent} ->
i("loop -> encode_ar_first: ~p", [EAF]),
{Reply, S1} = handle_encode_ar_first(S, EAF),
server_reply(Parent, encode_ar_first_reply, Reply),
- loop(S1#mg{encode_ar_first = EAF});
+ loop(evs(S1#mg{encode_ar_first = EAF}, {enc_arf, EAF}));
%% Give me statistics
{statistics, Parent} ->
i("loop -> got request for statistics", []),
Stats = do_get_statistics(Mid),
server_reply(Parent, statistics_reply, {ok, Stats}),
- loop(S);
+ loop(evs(S, stats));
{reset_stats, Parent} ->
i("loop -> got request to reset stats counters", []),
do_reset_stats(Mid),
server_reply(Parent, reset_stats_ack, ok),
- loop(S);
+ loop(evs(S, rst_stats));
{{user_info, Tag}, Parent} ->
i("loop -> got user_info request for ~w", [Tag]),
Res = do_get_user_info(Mid, Tag),
d("loop -> Res: ~p", [Res]),
server_reply(Parent, user_info_ack, Res),
- loop(S);
+ loop(evs(S, {ui, Tag}));
{{update_user_info, Tag, Val}, Parent} ->
i("loop -> got update_user_info: ~w -> ~p", [Tag, Val]),
Res = do_update_user_info(Mid, Tag, Val),
d("loop -> Res: ~p", [Res]),
server_reply(Parent, update_user_info_ack, Res),
- loop(S);
+ loop(evs(S, {uui, {Tag, Val}}));
{{conn_info, Tag}, Parent} ->
i("loop -> got conn_info request for ~w", [Tag]),
Res = do_get_conn_info(Mid, Tag),
server_reply(Parent, conn_info_ack, Res),
- loop(S);
+ loop(evs(S, {ci, Tag}));
{{update_conn_info, Tag, Val}, Parent} ->
i("loop -> got update_conn_info: ~w -> ~p", [Tag, Val]),
Res = do_update_conn_info(Mid, Tag, Val),
server_reply(Parent, update_conn_info_ack, Res),
- loop(S);
+ loop(evs(S, {uci, {Tag, Val}}));
%% Do a service change
@@ -526,28 +529,28 @@ loop(#mg{parent = Parent, mid = Mid} = S) ->
server_reply(Parent, service_change_reply, Error),
S
end,
- loop(S1);
+ loop(evs(S1, svc_ch));
{{group_requests, N}, Parent} when N > 0 ->
i("loop -> received group_requests ~p", [N]),
Reply = {ok, S#mg.group_size},
server_reply(Parent, group_requests_reply, Reply),
- loop(S#mg{group_size = N});
+ loop(evs(S#mg{group_size = N}, {grp_reqs, N}));
{{ack_info, To}, Parent} ->
i("loop -> received request to inform about received ack's ", []),
- loop(S#mg{ack_info = To});
+ loop(evs(S#mg{ack_info = To}, {acki, To}));
{{rep_info, To}, Parent} ->
i("loop -> received request to inform about received rep's ", []),
- loop(S#mg{rep_info = To});
+ loop(evs(S#mg{rep_info = To}, {repi, To}));
%% Make a sync-call
{notify_request, Parent} ->
i("loop -> received request to send notify request ", []),
{Res, S1} = do_handle_notify_request(S),
d("loop -> notify request result: ~p", [Res]),
- loop(S1);
+ loop(evs(S1, not_req));
%% sync-call complete
{notify_request_complete, NotifyReply, Pid} ->
@@ -556,7 +559,7 @@ loop(#mg{parent = Parent, mid = Mid} = S) ->
"~n NotifyReply: ~p",
[Pid, NotifyReply]),
server_reply(Parent, notify_request_reply, NotifyReply),
- loop(S#mg{req_handler = undefined});
+ loop(evs(S#mg{req_handler = undefined}, {not_reqc, NotifyReply}));
%% cancel requests
@@ -564,14 +567,14 @@ loop(#mg{parent = Parent, mid = Mid} = S) ->
i("loop -> received request to cancel (all) megaco requests ", []),
Res = do_cancel_requests(Mid, Reason),
server_reply(Parent, cancel_request_reply, Res),
- loop(S);
+ loop(evs(S, {creq, Reason}));
%% Apply multi-load
{apply_multi_load, {NL, NR}, Parent} ->
i("loop -> received apply_multi_load request: ~w, ~w", [NL, NR]),
S1 = start_loaders(S, NL, NR),
- loop(S1);
+ loop(evs(S1, {apply_mload, {NL, NR}}));
%% Apply some load
@@ -587,12 +590,12 @@ loop(#mg{parent = Parent, mid = Mid} = S) ->
server_reply(Parent, apply_load_ack, Error),
S
end,
- loop(S1);
+ loop(evs(S1, {apply_load, Times}));
{apply_load_timeout, _} ->
d("loop -> received apply_load timeout", []),
S1 = do_apply_load(S),
- loop(S1);
+ loop(evs(S1, apply_loadto));
%% Megaco callback messages
@@ -604,22 +607,35 @@ loop(#mg{parent = Parent, mid = Mid} = S) ->
{Reply, S1} = handle_megaco_request(S, Request),
d("loop -> send (megaco callback) request reply: ~n~p", [Reply]),
From ! {reply, Reply, self()},
- loop(S1);
+ loop(evs(S1, {req, {Request, Mid, From}}));
{'EXIT', Pid, Reason} ->
- i("loop -> received exit signal from ~p: ~n~p", [Pid, Reason]),
+ i("loop -> received exit signal from ~p: "
+ "~n ~p", [Pid, Reason]),
S1 = handle_exit(S, Pid, Reason),
- loop(S1);
+ loop(evs(S1, {exit, {Pid, Reason}}));
Invalid ->
error_msg("received invalid request: ~n~p", [Invalid]),
- loop(S)
+ loop(evs(S, {invalid, Invalid}))
end.
+evs(#mg{evs = EVS} = S, Ev) when (length(EVS) < ?EVS_MAX) ->
+ S#mg{evs = [{?FTS(), Ev}|EVS]};
+evs(#mg{evs = EVS} = S, Ev) ->
+ S#mg{evs = [{?FTS(), Ev}|lists:droplast(EVS)]}.
+
+done(#mg{evs = EVS}, Reason) ->
+ info_msg("Exiting with latest event(s): "
+ "~n ~p"
+ "~n", [EVS]),
+ exit(Reason).
+
+
handle_encode_ar_first(#mg{encode_ar_first = Old} = MG, New)
when (New =:= true) orelse (New =:= false) ->
{{ok, Old}, MG#mg{encode_ar_first = New}};
@@ -776,7 +792,7 @@ do_service_change(#mg{state = State} = MG) ->
{{error, {invalid_state, State}}, MG}.
do_service_change(ConnHandle, Method, EAF, Reason) ->
- d("sending service change using:"
+ d("send service change using:"
"~n ConnHandle: ~p"
"~n Method: ~p"
"~n EAF: ~p"
@@ -844,10 +860,10 @@ loader_main(EAF, N, CH) ->
-handle_exit(#mg{parent = Pid}, Pid, Reason) ->
+handle_exit(#mg{parent = Pid} = S, Pid, Reason) ->
error_msg("received exit from the parent:"
"~n ~p", [Reason]),
- exit({parent_terminated, Reason});
+ done(S, {parent_terminated, Reason});
handle_exit(#mg{parent = Parent, req_handler = Pid} = MG, Pid, Reason) ->
error_msg("received unexpected exit from the request handler:"
@@ -1423,6 +1439,7 @@ sleep(X) ->
receive after X -> ok end.
+info_msg(F,A) -> error_logger:info_msg("MG: " ++ F ++ "~n",A).
error_msg(F,A) -> error_logger:error_msg("MG: " ++ F ++ "~n",A).
@@ -1536,37 +1553,28 @@ print(Severity, Verbosity, P, F, A) ->
print(true, P, F, A) ->
io:format("*** [~s] ~s ~p ~s ***"
"~n " ++ F ++ "~n~n",
- [format_timestamp(now()), P, self(), get(sname) | A]);
+ [?FTS(), P, self(), get(sname) | A]);
print(_, _, _, _) ->
ok.
-format_timestamp({_N1, _N2, N3} = Now) ->
- {Date, Time} = calendar:now_to_datetime(Now),
- {YYYY,MM,DD} = Date,
- {Hour,Min,Sec} = Time,
- FormatDate =
- io_lib:format("~.4w:~.2.0w:~.2.0w ~.2.0w:~.2.0w:~.2.0w 4~w",
- [YYYY,MM,DD,Hour,Min,Sec,round(N3/1000)]),
- lists:flatten(FormatDate).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
random_init() ->
- {A,B,C} = now(),
- random:seed(A,B,C).
+ ok.
random() ->
random(50).
random(N) ->
- random:uniform(N).
+ rand:uniform(N).
display_system_info(Mid) ->
display_system_info(Mid, "").
display_system_info(Mid, Pre) ->
- TimeStr = format_timestamp(now()),
+ TimeStr = ?FTS(),
MibStr = lists:flatten(io_lib:format("~p ", [Mid])),
megaco_test_lib:display_system_info(MibStr ++ Pre ++ TimeStr).
diff --git a/lib/megaco/test/megaco_test_mgc.erl b/lib/megaco/test/megaco_test_mgc.erl
index 045bc7c9fd..a9027ca68e 100644
--- a/lib/megaco/test/megaco_test_mgc.erl
+++ b/lib/megaco/test/megaco_test_mgc.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -75,7 +75,10 @@
abort_info = undefined,
req_info = undefined,
mg = [],
- dsi_timer}).
+ dsi_timer,
+ evs = []}).
+
+-define(EVS_MAX, 10).
%%% ------------------------------------------------------------------
@@ -297,7 +300,7 @@ mgc(Parent, Verbosity, Config) ->
dsi_timer = DSITimer},
i("mgc -> started"),
display_system_info("at start "),
- loop(S)
+ loop(evs(S, started))
end.
init(Config) ->
@@ -359,7 +362,7 @@ loop(S) ->
{display_system_info, Time} ->
display_system_info(S#mgc.mid),
NewTimer = create_timer(Time, display_system_info),
- loop(S#mgc{dsi_timer = NewTimer});
+ loop(evs(S#mgc{dsi_timer = NewTimer}, {dsi, Time}));
{stop, Parent} when S#mgc.parent =:= Parent ->
i("loop -> stopping", []),
@@ -371,7 +374,7 @@ loop(S) ->
application:stop(megaco),
i("loop -> stopped", []),
server_reply(Parent, stopped, ok),
- exit(normal);
+ done(evs(S, stop), normal);
{{disconnect, Reason}, Parent} when S#mgc.parent == Parent ->
i("loop -> disconnecting", []),
@@ -379,22 +382,22 @@ loop(S) ->
[Conn|_] = megaco:user_info(Mid, connections),
Res = megaco:disconnect(Conn, {self(), Reason}),
server_reply(Parent, disconnected, Res),
- loop(S);
+ loop(evs(S, {disconnect, Reason}));
{{update_user_info, Tag, Val}, Parent} when S#mgc.parent == Parent ->
i("loop -> got update_user_info: ~w -> ~p", [Tag, Val]),
Res = (catch megaco:update_user_info(S#mgc.mid, Tag, Val)),
d("loop -> Res: ~p", [Res]),
server_reply(Parent, update_user_info_ack, Res),
- loop(S);
-
- {{user_info, Tag}, Parent} when S#mgc.parent == Parent ->
- i("loop -> got user_info request for ~w", [Tag]),
- Res = (catch megaco:user_info(S#mgc.mid, Tag)),
- d("loop -> Res: ~p", [Res]),
- server_reply(Parent, user_info_ack, Res),
- loop(S);
-
+ loop(evs(S, {uui, {Tag, Val}}));
+
+ {{user_info, Tag}, Parent} when S#mgc.parent == Parent ->
+ i("loop -> got user_info request for ~w", [Tag]),
+ Res = (catch megaco:user_info(S#mgc.mid, Tag)),
+ d("loop -> Res: ~p", [Res]),
+ server_reply(Parent, user_info_ack, Res),
+ loop(evs(S, {ui, Tag}));
+
{{update_conn_info, Tag, Val}, Parent} when S#mgc.parent == Parent ->
i("loop -> got update_conn_info: ~w -> ~p", [Tag, Val]),
Conns = megaco:user_info(S#mgc.mid, connections),
@@ -404,7 +407,7 @@ loop(S) ->
Res = lists:map(Fun, Conns),
d("loop -> Res: ~p", [Res]),
server_reply(Parent, update_conn_info_ack, Res),
- loop(S);
+ loop(evs(S, {uci, {Tag, Val}}));
{{conn_info, Tag}, Parent} when S#mgc.parent == Parent ->
i("loop -> got conn_info request for ~w", [Tag]),
@@ -415,11 +418,11 @@ loop(S) ->
Res = lists:map(Fun, Conns),
d("loop -> Res: ~p", [Res]),
server_reply(Parent, conn_info_ack, Res),
- loop(S);
+ loop(evs(S, {ci, Tag}));
%%
- {request_action, {Action, To}, Parent} when S#mgc.parent == Parent ->
+ {request_action, {Action, To}, Parent} when S#mgc.parent == Parent ->
i("loop -> got new request_action: ~p:~w", [Action,To]),
{Reply, S1} =
case lists:member(Action, ?valid_actions) of
@@ -432,7 +435,7 @@ loop(S) ->
{{error, {invalid_action, Action}}, S}
end,
server_reply(Parent, request_action_ack, Reply),
- loop(S1);
+ loop(evs(S1, {req_act, {Action, To}}));
%% Reset stats
@@ -440,7 +443,7 @@ loop(S) ->
i("loop -> got request to reset stats counters"),
do_reset_stats(S#mgc.mid),
server_reply(Parent, reset_stats_ack, ok),
- loop(S);
+ loop(evs(S, rst_stats));
%% Give me statistics
@@ -466,7 +469,7 @@ loop(S) ->
lists:map(GetTrans, megaco:user_info(Mid, connections)),
Reply = {ok, [{gen, Gen}, {trans, Trans}]},
server_reply(Parent, {statistics_reply, 1}, Reply),
- loop(S);
+ loop(evs(S, {stats, 1}));
{{statistics, 2}, Parent} when S#mgc.parent == Parent ->
@@ -477,7 +480,7 @@ loop(S) ->
UdpStats = get_trans_stats(UdpSup, megaco_udp),
Reply = {ok, [{gen, Gen}, {trans, [TcpStats, UdpStats]}]},
server_reply(Parent, {statistics_reply, 2}, Reply),
- loop(S);
+ loop(evs(S, {stats, 2}));
%% Megaco callback messages
@@ -487,28 +490,28 @@ loop(S) ->
{Reply, S1} = handle_megaco_request(Request, S),
d("loop -> send request reply: ~n~p", [Reply]),
reply(From, Reply),
- loop(S1);
+ loop(evs(S1, {req, Request}));
{ack_info, To, Parent} when S#mgc.parent == Parent ->
i("loop -> received request to inform about received ack's ", []),
- loop(S#mgc{ack_info = To});
+ loop(evs(S#mgc{ack_info = To}, {acki, To}));
{abort_info, To, Parent} when S#mgc.parent == Parent ->
i("loop -> received request to inform about received aborts ", []),
- loop(S#mgc{abort_info = To});
+ loop(evs(S#mgc{abort_info = To}, {abi, To}));
{req_info, To, Parent} when S#mgc.parent == Parent ->
i("loop -> received request to inform about received req's ", []),
- loop(S#mgc{req_info = To});
+ loop(evs(S#mgc{req_info = To}, {reqi, To}));
{verbosity, V, Parent} when S#mgc.parent == Parent ->
i("loop -> received new verbosity: ~p", [V]),
put(verbosity,V),
- loop(S);
+ loop(evs(S, {verb, V}));
{'EXIT', Pid, Reason} when S#mgc.tcp_sup =:= Pid ->
@@ -525,7 +528,7 @@ loop(S) ->
i("loop -> stopped", []),
StopReason = {error, {tcp_terminated, Pid, Reason}},
server_reply(S#mgc.parent, stopped, StopReason),
- exit(StopReason);
+ done(evs(S, {tcp_sup_exit, Reason}), StopReason);
{'EXIT', Pid, Reason} when S#mgc.udp_sup =:= Pid ->
@@ -542,15 +545,27 @@ loop(S) ->
i("loop -> stopped", []),
StopReason = {error, {udp_terminated, Pid, Reason}},
server_reply(S#mgc.parent, stopped, StopReason),
- exit(StopReason);
+ done(evs(S, {udp_sup_exit, Reason}), StopReason);
Invalid ->
i("loop -> received invalid request: ~p", [Invalid]),
- loop(S)
+ loop(evs(S, {invalid, Invalid}))
end.
+evs(#mgc{evs = EVS} = S, Ev) when (length(EVS) < ?EVS_MAX) ->
+ S#mgc{evs = [{?FTS(), Ev}|EVS]};
+evs(#mgc{evs = EVS} = S, Ev) ->
+ S#mgc{evs = [{?FTS(), Ev}|lists:droplast(EVS)]}.
+
+done(#mgc{evs = EVS}, Reason) ->
+ info_msg("Exiting with latest event(s): "
+ "~n ~p"
+ "~n", [EVS]),
+ exit(Reason).
+
+
do_reset_stats(Mid) ->
megaco:reset_stats(),
do_reset_trans_stats(megaco:user_info(Mid, connections), []).
@@ -825,10 +840,10 @@ handle_megaco_request({handle_trans_ack, CH, PV, AS, AD},
handle_megaco_request({handle_trans_ack, CH, PV, AS, AD}, S) ->
d("handle_megaco_request(handle_trans_ack) -> entry with"
- "~n CH: ~p"
- "~n PV: ~p"
- "~n AS: ~p"
- "~n AD: ~p", [CH, PV, AS, AD]),
+ "~n Conn Handle: ~p"
+ "~n Prot Version: ~p"
+ "~n Ack Status: ~p"
+ "~n Ack Data: ~p", [CH, PV, AS, AD]),
{ok, S};
handle_megaco_request({handle_unexpected_trans, CH, PV, TR}, S) ->
@@ -1090,6 +1105,7 @@ sleep(X) ->
receive after X -> ok end.
+info_msg(F,A) -> error_logger:info_msg("MGC: " ++ F ++ "~n",A).
error_msg(F,A) -> error_logger:error_msg("MGC: " ++ F ++ "~n",A).
@@ -1153,18 +1169,17 @@ get_conf(Key, Config, Default) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
random_init() ->
- {A,B,C} = now(),
- random:seed(A,B,C).
+ ok.
random(N) ->
- random:uniform(N).
+ rand:uniform(N).
display_system_info(Mid) ->
display_system_info(Mid, "").
display_system_info(Mid, Pre) ->
- TimeStr = format_timestamp(now()),
+ TimeStr = ?FTS(),
MibStr = lists:flatten(io_lib:format("~p ", [Mid])),
megaco_test_lib:display_system_info(MibStr ++ Pre ++ TimeStr).
@@ -1209,14 +1224,6 @@ print(_, _, _, _) ->
print(P, F, A) ->
io:format("*** [~s] ~s ~p ~s ***"
"~n " ++ F ++ "~n~n",
- [format_timestamp(now()), P, self(), get(sname) | A]).
-
-format_timestamp({_N1, _N2, N3} = Now) ->
- {Date, Time} = calendar:now_to_datetime(Now),
- {YYYY,MM,DD} = Date,
- {Hour,Min,Sec} = Time,
- FormatDate =
- io_lib:format("~.4w:~.2.0w:~.2.0w ~.2.0w:~.2.0w:~.2.0w 4~w",
- [YYYY,MM,DD,Hour,Min,Sec,round(N3/1000)]),
- lists:flatten(FormatDate).
+ [?FTS(), P, self(), get(sname) | A]).
+
diff --git a/lib/megaco/test/megaco_timer_test.erl b/lib/megaco/test/megaco_timer_test.erl
index d3e8e27636..87aa51b8de 100644
--- a/lib/megaco/test/megaco_timer_test.erl
+++ b/lib/megaco/test/megaco_timer_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -354,16 +354,23 @@ integer_timer_start_and_stop(Config) when is_list(Config) ->
i("starting"),
Timeout = 5000,
- Ref = tmr_start(Timeout),
+ i("try start (~w msec) timer", [Timeout]),
+ Ref = tmr_start(Timeout),
+ i("timer started "),
receive
{timeout, Timeout} ->
+ i("unexpected premature timer expire"),
error(bad_timeout)
after Timeout - 100 ->
+ i("try stop timer"),
case tmr_stop(Ref) of
- ok ->
+ {ok, Rem} ->
+ i("timer stopped with ~w msec remaining", [Rem]),
ok;
CancelRes ->
- ?SKIP({cancel_failed, CancelRes})
+ i("failed stop timer: "
+ "~n ~p", [CancelRes]),
+ ?SKIP({cancel_failed, CancelRes}) % Race - not our problem
end
end,
diff --git a/lib/megaco/test/megaco_trans_test.erl b/lib/megaco/test/megaco_trans_test.erl
index fb44a3c6e6..37bc134c8d 100644
--- a/lib/megaco/test/megaco_trans_test.erl
+++ b/lib/megaco/test/megaco_trans_test.erl
@@ -295,7 +295,7 @@ multi_ack_timeout(doc) ->
[];
multi_ack_timeout(Config) when is_list(Config) ->
%% <CONDITIONAL-SKIP>
- Skippable = [win32, {unix, [darwin, linux]}],
+ Skippable = [win32, {unix, [darwin, linux, sunos]}], % Is there any left?
Condition = fun() -> ?OS_BASED_SKIP(Skippable) end,
?NON_PC_TC_MAYBE_SKIP(Config, Condition),
%% </CONDITIONAL-SKIP>
@@ -9247,10 +9247,10 @@ await_ack(User, N, Timeout, Expected) when (N > 0) andalso is_integer(Timeout) -
T = tim(),
receive
{ack_received, User, Expected} ->
- d("await_ack -> received another ack"),
+ d("await_ack -> received another expected ack"),
await_ack(User, N-1, Timeout - (tim() - T), Expected);
{ack_received, User, UnExpected} ->
- e("await_ack -> unexpected ack result: ~p", [UnExpected]),
+ e("await_ack -> received unexpected ack result: ~p", [UnExpected]),
exit({unexpected_ack_result, UnExpected, Expected})
after Timeout ->
exit({await_ack_timeout, N})
@@ -9266,35 +9266,6 @@ await_ack(User, N, infinity, Expected) when N > 0 ->
exit({unexpected_ack_result, UnExpected, Expected})
end.
-%% await_req(_User, 0, Timeout) ->
-%% d("await_req -> done when Timeout = ~p", [Timeout]),
-%% ok;
-%% await_req(User, N, Timeout) when (N > 0) andalso is_integer(Timeout) ->
-%% d("await_req -> entry with N: ~p, Timeout: ~p", [N,Timeout]),
-%% T = tim(),
-%% receive
-%% {req_received, User, ARs} ->
-%% d("await_req -> received req(s) when N = ~w", [N]),
-%% N1 = await_req1(N, ARs),
-%% await_req(User, N1, Timeout - (tim() - T))
-%% after Timeout ->
-%% exit({await_req_timeout, N})
-%% end;
-%% await_req(User, N, infinity) when N > 0 ->
-%% d("await_req -> entry with N: ~p", [N]),
-%% receive
-%% {req_received, User, ARs} ->
-%% d("await_req -> received req(s) when N = ~2",[N]),
-%% N1 = await_req1(N, ARs),
-%% await_req(User, N1, infinity)
-%% end.
-
-%% await_req1(N, []) when N >= 0 ->
-%% N;
-%% await_req1(N, [AR|ARs]) when (N > 0) andalso is_record(AR, 'ActionRequest') ->
-%% await_req1(N-1, ARs);
-%% await_req1(N, ARs) ->
-%% exit({unexpected_req_result, N, ARs}).
tim() ->
{A,B,C} = erlang:timestamp(),
diff --git a/lib/megaco/test/megaco_udp_test.erl b/lib/megaco/test/megaco_udp_test.erl
index cc03ec733a..39ff44709e 100644
--- a/lib/megaco/test/megaco_udp_test.erl
+++ b/lib/megaco/test/megaco_udp_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -1211,23 +1211,14 @@ p(F, A) ->
p(S, F, A) when is_list(S) ->
io:format("*** [~s] ~p ~s ***"
"~n " ++ F ++ "~n",
- [format_timestamp(now()), self(), S | A]);
+ [?FTS(), self(), S | A]);
p(_S, F, A) ->
io:format("*** [~s] ~p ~s *** "
"~n " ++ F ++ "~n",
- [format_timestamp(now()), self(), "undefined" | A]).
+ [?FTS(), self(), "undefined" | A]).
ms() ->
- {A,B,C} = erlang:now(),
- A*1000000000+B*1000+(C div 1000).
+ erlang:monotonic_time(milli_seconds).
-format_timestamp({_N1, _N2, N3} = Now) ->
- {Date, Time} = calendar:now_to_datetime(Now),
- {YYYY,MM,DD} = Date,
- {Hour,Min,Sec} = Time,
- FormatDate =
- io_lib:format("~.4w:~.2.0w:~.2.0w ~.2.0w:~.2.0w:~.2.0w 4~w",
- [YYYY,MM,DD,Hour,Min,Sec,round(N3/1000)]),
- lists:flatten(FormatDate).
diff --git a/lib/megaco/vsn.mk b/lib/megaco/vsn.mk
index 843a3dccc5..381def97d7 100644
--- a/lib/megaco/vsn.mk
+++ b/lib/megaco/vsn.mk
@@ -19,6 +19,6 @@
# %CopyrightEnd%
APPLICATION = megaco
-MEGACO_VSN = 3.18.5
+MEGACO_VSN = 3.18.6
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(MEGACO_VSN)$(PRE_VSN)"
diff --git a/lib/mnesia/doc/src/Mnesia_chap7.xmlsrc b/lib/mnesia/doc/src/Mnesia_chap7.xmlsrc
index ffbbdadec0..968e89a745 100644
--- a/lib/mnesia/doc/src/Mnesia_chap7.xmlsrc
+++ b/lib/mnesia/doc/src/Mnesia_chap7.xmlsrc
@@ -295,7 +295,7 @@ ok</pre>
if the log is large. Notice that the <c>Mnesia</c> system
continues to operate during log dumps.</p>
<p>By default <c>Mnesia</c> either dumps the log whenever
- 100 records have
+ 1000 records have
been written in the log or when three minutes have passed.
This is controlled by the two application parameters
<c>-mnesia dump_log_write_threshold WriteOperations</c> and
diff --git a/lib/mnesia/doc/src/notes.xml b/lib/mnesia/doc/src/notes.xml
index 2d38e4d01c..e918338c8a 100644
--- a/lib/mnesia/doc/src/notes.xml
+++ b/lib/mnesia/doc/src/notes.xml
@@ -39,7 +39,36 @@
thus constitutes one section in this document. The title of each
section is the version number of Mnesia.</p>
- <section><title>Mnesia 4.16</title>
+ <section><title>Mnesia 4.16.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ <c>mnesia:add_table_copy/3</c> could cause a deadlock if
+ called when a new node was starting.</p>
+ <p>
+ Own Id: OTP-15933 Aux Id: ERL-872 </p>
+ </item>
+ <item>
+ <p>Transactions with sticky locks could with async_asym
+ transactions be committed in the wrong order, since asym
+ transaction are spawned on the remote nodes.</p> <p>To
+ fix this bug the communication protocol between mnesia
+ nodes had to be updated, thus mnesia will no longer be
+ able to connect to nodes earlier than mnesia-4.14 ,
+ OTP-19.0.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-15979 Aux Id: ERL-768 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Mnesia 4.16</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/mnesia/src/mnesia_controller.erl b/lib/mnesia/src/mnesia_controller.erl
index 882de0d613..0f221b0c1f 100644
--- a/lib/mnesia/src/mnesia_controller.erl
+++ b/lib/mnesia/src/mnesia_controller.erl
@@ -331,35 +331,39 @@ release_schema_commit_lock() ->
%% Special for preparation of add table copy
get_network_copy(Tid, Tab, Cs) ->
-% We can't let the controller queue this one
-% because that may cause a deadlock between schema_operations
-% and initial tableloadings which both takes schema locks.
-% But we have to get copier_done msgs when the other side
-% goes down.
- call({add_other, self()}),
- Reason = {dumper,{add_table_copy, Tid}},
- Work = #net_load{table = Tab,reason = Reason,cstruct = Cs},
- %% I'll need this cause it's linked trough the subscriber
- %% might be solved by using monitor in subscr instead.
- process_flag(trap_exit, true),
- Load = load_table_fun(Work),
- Res = ?CATCH(Load()),
- process_flag(trap_exit, false),
- call({del_other, self()}),
- case Res of
- #loader_done{is_loaded = true} ->
- Tab = Res#loader_done.table_name,
- case Res#loader_done.needs_announce of
- true ->
- i_have_tab(Tab);
- false ->
- ignore
- end,
- Res#loader_done.reply;
- #loader_done{} ->
- Res#loader_done.reply;
- Else ->
- {not_loaded, Else}
+ %% We can't let the controller queue this one
+ %% because that may cause a deadlock between schema_operations
+ %% and initial tableloadings which both takes schema locks.
+ %% But we have to get copier_done msgs when the other side
+ %% goes down.
+ case call({add_other, self()}) of
+ ok ->
+ Reason = {dumper,{add_table_copy, Tid}},
+ Work = #net_load{table = Tab,reason = Reason,cstruct = Cs},
+ %% I'll need this cause it's linked trough the subscriber
+ %% might be solved by using monitor in subscr instead.
+ process_flag(trap_exit, true),
+ Load = load_table_fun(Work),
+ Res = ?CATCH(Load()),
+ process_flag(trap_exit, false),
+ call({del_other, self()}),
+ case Res of
+ #loader_done{is_loaded = true} ->
+ Tab = Res#loader_done.table_name,
+ case Res#loader_done.needs_announce of
+ true ->
+ i_have_tab(Tab);
+ false ->
+ ignore
+ end,
+ Res#loader_done.reply;
+ #loader_done{} ->
+ Res#loader_done.reply;
+ Else ->
+ {not_loaded, Else}
+ end;
+ {error, Else} ->
+ {not_loaded, Else}
end.
%% This functions is invoked from the dumper
@@ -772,6 +776,18 @@ handle_call({unannounce_add_table_copy, [Tab, Node], From}, ReplyTo, State) ->
noreply(State#state{early_msgs = [{call, Msg, undefined} | Msgs]})
end;
+handle_call({add_other, Who}, _From, State = #state{others=Others0, schema_is_merged=SM}) ->
+ case SM of
+ true ->
+ Others = [Who|Others0],
+ {reply, ok, State#state{others=Others}};
+ false ->
+ {reply, {error, {not_active,schema,node()}}, State}
+ end;
+handle_call({del_other, Who}, _From, State = #state{others=Others0}) ->
+ Others = lists:delete(Who, Others0),
+ {reply, ok, State#state{others=Others}};
+
handle_call(Msg, From, State) when State#state.schema_is_merged /= true ->
%% Buffer early messages
Msgs = State#state.early_msgs,
@@ -803,13 +819,6 @@ handle_call({block_table, [Tab], From}, _Dummy, State) ->
handle_call({check_w2r, _Node, Tab}, _From, State) ->
{reply, val({Tab, where_to_read}), State};
-handle_call({add_other, Who}, _From, State = #state{others=Others0}) ->
- Others = [Who|Others0],
- {reply, ok, State#state{others=Others}};
-handle_call({del_other, Who}, _From, State = #state{others=Others0}) ->
- Others = lists:delete(Who, Others0),
- {reply, ok, State#state{others=Others}};
-
handle_call(Msg, _From, State) ->
error("~p got unexpected call: ~tp~n", [?SERVER_NAME, Msg]),
noreply(State).
diff --git a/lib/mnesia/src/mnesia_locker.erl b/lib/mnesia/src/mnesia_locker.erl
index f68626413e..0222c5b1a0 100644
--- a/lib/mnesia/src/mnesia_locker.erl
+++ b/lib/mnesia/src/mnesia_locker.erl
@@ -774,10 +774,12 @@ do_sticky_lock(Tid, Store, {Tab, Key} = Oid, Lock) ->
N = node(),
receive
{?MODULE, N, granted} ->
+ ?ets_insert(Store, {sticky, true}),
?ets_insert(Store, {{locks, Tab, Key}, write}),
[?ets_insert(Store, {nodes, Node}) || Node <- WNodes],
granted;
{?MODULE, N, {granted, Val}} -> %% for rwlocks
+ ?ets_insert(Store, {sticky, true}),
case opt_lookup_in_client(Val, Oid, write) of
C = #cyclic{} ->
exit({aborted, C});
diff --git a/lib/mnesia/src/mnesia_monitor.erl b/lib/mnesia/src/mnesia_monitor.erl
index 4cfe16dec0..4e50b46da8 100644
--- a/lib/mnesia/src/mnesia_monitor.erl
+++ b/lib/mnesia/src/mnesia_monitor.erl
@@ -83,9 +83,9 @@
going_down = [], tm_started = false, early_connects = [],
connecting, mq = [], remote_node_status = []}).
--define(current_protocol_version, {8,3}).
+-define(current_protocol_version, {8,4}).
--define(previous_protocol_version, {8,2}).
+-define(previous_protocol_version, {8,3}).
start() ->
gen_server:start_link({local, ?MODULE}, ?MODULE,
@@ -196,7 +196,7 @@ protocol_version() ->
%% A sorted list of acceptable protocols the
%% preferred protocols are first in the list
acceptable_protocol_versions() ->
- [protocol_version(), ?previous_protocol_version, {8,1}].
+ [protocol_version(), ?previous_protocol_version].
needs_protocol_conversion(Node) ->
case {?catch_val({protocol, Node}), protocol_version()} of
diff --git a/lib/mnesia/src/mnesia_schema.erl b/lib/mnesia/src/mnesia_schema.erl
index ef38adca1e..d0f5d0e07b 100644
--- a/lib/mnesia/src/mnesia_schema.erl
+++ b/lib/mnesia/src/mnesia_schema.erl
@@ -697,7 +697,7 @@ schema_coordinator(Client, _Fun, undefined) ->
schema_coordinator(Client, Fun, Controller) when is_pid(Controller) ->
%% Do not trap exit in order to automatically die
%% when the controller dies
-
+ put(transaction_client, Client), %% debug
link(Controller),
unlink(Client),
@@ -730,7 +730,10 @@ api_list2cs(Other) ->
mnesia:abort({badarg, Other}).
vsn_cs2list(Cs) ->
- cs2list(need_old_cstructs(), Cs).
+ cs2list(Cs).
+
+cs2list(false, Cs) ->
+ cs2list(Cs).
cs2list(Cs) when is_record(Cs, cstruct) ->
Tags = record_info(fields, cstruct),
@@ -755,25 +758,6 @@ cs2list(Cs) when element(1, Cs) == cstruct, tuple_size(Cs) == 19 ->
cookie,version],
rec2list(Tags, Tags, 2, Cs).
-cs2list(false, Cs) ->
- cs2list(Cs);
-cs2list({8,3}, Cs) ->
- cs2list(Cs);
-cs2list({8,Minor}, Cs) when Minor =:= 2; Minor =:= 1 ->
- Orig = record_info(fields, cstruct),
- Tags = [name,type,ram_copies,disc_copies,disc_only_copies,
- load_order,access_mode,majority,index,snmp,local_content,
- record_name,attributes,
- user_properties,frag_properties,storage_properties,
- cookie,version],
- CsList = rec2list(Tags, Orig, 2, Cs),
- case proplists:get_value(index, CsList, []) of
- [] -> CsList;
- NewFormat ->
- OldFormat = [Pos || {Pos, _Pref} <- NewFormat],
- lists:keyreplace(index, 1, CsList, {index, OldFormat})
- end.
-
rec2list([index | Tags], [index|Orig], Pos, Rec) ->
Val = element(Pos, Rec),
[{index, lists:map(
@@ -796,19 +780,8 @@ rec2list([], _, _Pos, _Rec) ->
rec2list(Tags, [_|Orig], Pos, Rec) ->
rec2list(Tags, Orig, Pos+1, Rec).
-normalize_cs(Cstructs, Node) ->
- %% backward-compatibility hack; normalize before returning
- case need_old_cstructs([Node]) of
- false ->
- Cstructs;
- Version ->
- %% some other format
- [convert_cs(Version, Cs) || Cs <- Cstructs]
- end.
-
-convert_cs(Version, Cs) ->
- Fields = [Value || {_, Value} <- cs2list(Version, Cs)],
- list_to_tuple([cstruct|Fields]).
+normalize_cs(Cstructs, _Node) ->
+ Cstructs.
list2cs(List) ->
list2cs(List, get_ext_types()).
@@ -1864,11 +1837,7 @@ do_move_table(schema, _FromNode, _ToNode) ->
mnesia:abort({bad_type, schema});
do_move_table(Tab, FromNode, ToNode) when is_atom(FromNode), is_atom(ToNode) ->
TidTs = get_tid_ts_and_lock(schema, write),
- AnyOld = lists:any(fun(Node) -> mnesia_monitor:needs_protocol_conversion(Node) end,
- [ToNode|val({Tab, where_to_write})]),
- if AnyOld -> ignore; %% Leads to deadlock on old nodes
- true -> get_tid_ts_and_lock(Tab, write)
- end,
+ get_tid_ts_and_lock(Tab, write),
insert_schema_ops(TidTs, make_move_table(Tab, FromNode, ToNode));
do_move_table(Tab, FromNode, ToNode) ->
mnesia:abort({badarg, Tab, FromNode, ToNode}).
@@ -3438,15 +3407,14 @@ do_merge_schema(LockTabs0) ->
mnesia_lib:intersect(Ns,NeedsLock))
|| {T,Ns} <- LockTabs],
- NeedsConversion = need_old_cstructs(NeedsLock ++ LockedAlready),
{value, SchemaCs} = lists:keysearch(schema, #cstruct.name, Cstructs),
- SchemaDef = cs2list(NeedsConversion, SchemaCs),
+ SchemaDef = cs2list(false, SchemaCs),
%% Announce that Node is running
A = [{op, announce_im_running, node(), SchemaDef, Running, RemoteRunning}],
do_insert_schema_ops(Store, A),
%% Introduce remote tables to local node
- do_insert_schema_ops(Store, make_merge_schema(Node, NeedsConversion, Cstructs)),
+ do_insert_schema_ops(Store, make_merge_schema(Node, false, Cstructs)),
%% Introduce local tables to remote nodes
Tabs = val({schema, tables}),
@@ -3471,23 +3439,7 @@ do_merge_schema(LockTabs0) ->
end.
fetch_cstructs(Node) ->
- Convert = mnesia_monitor:needs_protocol_conversion(Node),
- case rpc:call(Node, mnesia_controller, get_remote_cstructs, []) of
- {cstructs, Cs0, RemoteRunning1} when Convert ->
- {cstructs, [list2cs(cs2list(Cs)) || Cs <- Cs0], RemoteRunning1};
- Result ->
- Result
- end.
-
-need_old_cstructs() ->
- need_old_cstructs(val({schema, where_to_write})).
-
-need_old_cstructs(Nodes) ->
- Filter = fun(Node) -> mnesia_monitor:needs_protocol_conversion(Node) end,
- case lists:filter(Filter, Nodes) of
- [] -> false;
- Ns -> lists:min([element(1, ?catch_val({protocol, Node})) || Node <- Ns])
- end.
+ rpc:call(Node, mnesia_controller, get_remote_cstructs, []).
tab_to_nodes(Tab) when is_atom(Tab) ->
Cs = val({Tab, cstruct}),
diff --git a/lib/mnesia/src/mnesia_text.erl b/lib/mnesia/src/mnesia_text.erl
index cc21621ff4..ee31fdfd27 100644
--- a/lib/mnesia/src/mnesia_text.erl
+++ b/lib/mnesia/src/mnesia_text.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -170,7 +170,7 @@ read_terms(Stream, File, Line, L) ->
end.
read_term_from_stream(Stream, File, Line) ->
- R = io:request(Stream, {get_until,'',erl_scan,tokens,[Line]}),
+ R = io:request(Stream, {get_until,latin1,'',erl_scan,tokens,[Line]}),
case R of
{ok,Toks,EndLine} ->
case erl_parse:parse_term(Toks) of
diff --git a/lib/mnesia/src/mnesia_tm.erl b/lib/mnesia/src/mnesia_tm.erl
index 8b79fca1d7..8a4113422a 100644
--- a/lib/mnesia/src/mnesia_tm.erl
+++ b/lib/mnesia/src/mnesia_tm.erl
@@ -26,7 +26,7 @@
init/1,
non_transaction/5,
transaction/6,
- commit_participant/5,
+ commit_participant/6,
dirty/2,
display_info/2,
do_update_op/3,
@@ -62,13 +62,14 @@
%% Format on coordinators is [{Tid, EtsTabList} .....
-record(prep, {protocol = sym_trans,
- %% async_dirty | sync_dirty | sym_trans | sync_sym_trans | asym_trans
+ %% async_dirty | sync_dirty | sym_trans | sync_sym_trans | asym_trans | sync_asym_trans
records = [],
prev_tab = [], % initiate to a non valid table name
prev_types,
prev_snmp,
types,
- majority = []
+ majority = [],
+ sync = false
}).
-record(participant, {tid, pid, commit, disc_nodes = [],
@@ -250,11 +251,13 @@ doit_loop(#state{coordinators=Coordinators,participants=Participants,supervisor=
mnesia_checkpoint:tm_enter_pending(Tid, DiscNs, RamNs),
Commit = new_cr_format(Commit0),
Pid =
- case Protocol of
- asym_trans when node(Tid#tid.pid) /= node() ->
- Args = [tmpid(From), Tid, Commit, DiscNs, RamNs],
+ if
+ node(Tid#tid.pid) =:= node() ->
+ error({internal_error, local_node});
+ Protocol =:= asym_trans orelse Protocol =:= sync_asym_trans ->
+ Args = [Protocol, tmpid(From), Tid, Commit, DiscNs, RamNs],
spawn_link(?MODULE, commit_participant, Args);
- _ when node(Tid#tid.pid) /= node() -> %% *_sym_trans
+ true -> %% *_sym_trans
reply(From, {vote_yes, Tid}),
nopid
end,
@@ -1190,7 +1193,15 @@ do_arrange(Tid, Store, RestoreKey, Prep, N) when RestoreKey == restore_op ->
P2 = Prep#prep{protocol = asym_trans, records = Recs2},
do_arrange(Tid, Store, ?ets_next(Store, RestoreKey), P2, N + 1);
do_arrange(_Tid, _Store, '$end_of_table', Prep, N) ->
- {N, Prep};
+ case Prep of
+ #prep{sync=true, protocol=asym_trans} ->
+ {N, Prep#prep{protocol=sync_asym_trans}};
+ _ ->
+ {N, Prep}
+ end;
+do_arrange(Tid, Store, sticky, Prep, N) ->
+ P2 = Prep#prep{sync=true},
+ do_arrange(Tid, Store, ?ets_next(Store, sticky), P2, N);
do_arrange(Tid, Store, IgnoredKey, Prep, N) -> %% locks, nodes ... local atoms...
do_arrange(Tid, Store, ?ets_next(Store, IgnoredKey), Prep, N).
@@ -1448,7 +1459,8 @@ multi_commit(sync_sym_trans, _Maj = [], Tid, CR, Store) ->
[{tid, Tid}, {outcome, Outcome}]),
Outcome;
-multi_commit(asym_trans, Majority, Tid, CR, Store) ->
+multi_commit(Protocol, Majority, Tid, CR, Store)
+ when Protocol =:= asym_trans; Protocol =:= sync_asym_trans ->
%% This more expensive commit protocol is used when
%% table definitions are changed (schema transactions).
%% It is also used when the involved tables are
@@ -1515,7 +1527,7 @@ multi_commit(asym_trans, Majority, Tid, CR, Store) ->
end,
Pending = mnesia_checkpoint:tm_enter_pending(Tid, DiscNs, RamNs),
?ets_insert(Store, Pending),
- {WaitFor, Local} = ask_commit(asym_trans, Tid, CR2, DiscNs, RamNs),
+ {WaitFor, Local} = ask_commit(Protocol, Tid, CR2, DiscNs, RamNs),
SchemaPrep = ?CATCH(mnesia_schema:prepare_commit(Tid, Local, {coord, WaitFor})),
{Votes, Pids} = rec_all(WaitFor, Tid, do_commit, []),
@@ -1563,38 +1575,38 @@ multi_commit(asym_trans, Majority, Tid, CR, Store) ->
%% Returns do_commit or {do_abort, Reason}
rec_acc_pre_commit([Pid | Tail], Tid, Store, Commit, Res, DumperMode,
- GoodPids, SchemaAckPids) ->
+ GoodPids, AckPids) ->
receive
{?MODULE, _, {acc_pre_commit, Tid, Pid, true}} ->
rec_acc_pre_commit(Tail, Tid, Store, Commit, Res, DumperMode,
- [Pid | GoodPids], [Pid | SchemaAckPids]);
+ [Pid | GoodPids], [Pid | AckPids]);
{?MODULE, _, {acc_pre_commit, Tid, Pid, false}} ->
rec_acc_pre_commit(Tail, Tid, Store, Commit, Res, DumperMode,
- [Pid | GoodPids], SchemaAckPids);
+ [Pid | GoodPids], AckPids);
{?MODULE, _, {acc_pre_commit, Tid, Pid}} ->
%% Kept for backwards compatibility. Remove after Mnesia 4.x
rec_acc_pre_commit(Tail, Tid, Store, Commit, Res, DumperMode,
- [Pid | GoodPids], [Pid | SchemaAckPids]);
+ [Pid | GoodPids], [Pid | AckPids]);
{?MODULE, _, {do_abort, Tid, Pid, _Reason}} ->
AbortRes = {do_abort, {bad_commit, node(Pid)}},
rec_acc_pre_commit(Tail, Tid, Store, Commit, AbortRes, DumperMode,
- GoodPids, SchemaAckPids);
+ GoodPids, AckPids);
{mnesia_down, Node} when Node == node(Pid) ->
AbortRes = {do_abort, {bad_commit, Node}},
?SAFE(Pid ! {Tid, AbortRes}), %% Tell him that he has died
rec_acc_pre_commit(Tail, Tid, Store, Commit, AbortRes, DumperMode,
- GoodPids, SchemaAckPids)
+ GoodPids, AckPids)
end;
-rec_acc_pre_commit([], Tid, Store, {Commit,OrigC}, Res, DumperMode, GoodPids, SchemaAckPids) ->
+rec_acc_pre_commit([], Tid, Store, {Commit,OrigC}, Res, DumperMode, GoodPids, AckPids) ->
D = Commit#commit.decision,
case Res of
do_commit ->
%% Now everybody knows that the others
%% has voted yes. We also know that
%% everybody are uncertain.
- prepare_sync_schema_commit(Store, SchemaAckPids),
+ prepare_sync_schema_commit(Store, AckPids),
tell_participants(GoodPids, {Tid, committed}),
D2 = D#decision{outcome = committed},
mnesia_recover:log_decision(D2),
@@ -1606,7 +1618,7 @@ rec_acc_pre_commit([], Tid, Store, {Commit,OrigC}, Res, DumperMode, GoodPids, Sc
do_commit(Tid, Commit, DumperMode),
?eval_debug_fun({?MODULE, rec_acc_pre_commit_done_commit},
[{tid, Tid}]),
- sync_schema_commit(Tid, Store, SchemaAckPids),
+ sync_schema_commit(Tid, Store, AckPids),
mnesia_locker:release_tid(Tid),
?MODULE ! {delete_transaction, Tid};
@@ -1623,6 +1635,7 @@ rec_acc_pre_commit([], Tid, Store, {Commit,OrigC}, Res, DumperMode, GoodPids, Sc
Res.
%% Note all nodes in case of mnesia_down mgt
+%% sync_schema_commit is (ab)used for sync_asym_trans as well.
prepare_sync_schema_commit(_Store, []) ->
ok;
prepare_sync_schema_commit(Store, [Pid | Pids]) ->
@@ -1648,17 +1661,17 @@ tell_participants([Pid | Pids], Msg) ->
tell_participants([], _Msg) ->
ok.
--spec commit_participant(_, _, _, _, _) -> no_return().
+-spec commit_participant(_, _, _, _, _, _) -> no_return().
%% Trap exit because we can get a shutdown from application manager
-commit_participant(Coord, Tid, Bin, DiscNs, RamNs) when is_binary(Bin) ->
+commit_participant(Protocol, Coord, Tid, Bin, DiscNs, RamNs) when is_binary(Bin) ->
process_flag(trap_exit, true),
Commit = binary_to_term(Bin),
- commit_participant(Coord, Tid, Bin, Commit, DiscNs, RamNs);
-commit_participant(Coord, Tid, C = #commit{}, DiscNs, RamNs) ->
+ commit_participant(Protocol, Coord, Tid, Bin, Commit, DiscNs, RamNs);
+commit_participant(Protocol, Coord, Tid, C = #commit{}, DiscNs, RamNs) ->
process_flag(trap_exit, true),
- commit_participant(Coord, Tid, C, C, DiscNs, RamNs).
+ commit_participant(Protocol, Coord, Tid, C, C, DiscNs, RamNs).
-commit_participant(Coord, Tid, Bin, C0, DiscNs, _RamNs) ->
+commit_participant(Protocol, Coord, Tid, Bin, C0, DiscNs, _RamNs) ->
?eval_debug_fun({?MODULE, commit_participant, pre}, [{tid, Tid}]),
try mnesia_schema:prepare_commit(Tid, C0, {part, Coord}) of
{Modified, C = #commit{}, DumperMode} ->
@@ -1683,8 +1696,9 @@ commit_participant(Coord, Tid, Bin, C0, DiscNs, _RamNs) ->
mnesia_recover:log_decision(D#decision{outcome = unclear}),
?eval_debug_fun({?MODULE, commit_participant, pre_commit},
[{tid, Tid}]),
- Expect_schema_ack = C#commit.schema_ops /= [],
- reply(Coord, {acc_pre_commit, Tid, self(), Expect_schema_ack}),
+ ExpectAck = C#commit.schema_ops /= []
+ orelse Protocol =:= sync_asym_trans,
+ reply(Coord, {acc_pre_commit, Tid, self(), ExpectAck}),
%% Now we are vulnerable for failures, since
%% we cannot decide without asking others
@@ -1694,7 +1708,7 @@ commit_participant(Coord, Tid, Bin, C0, DiscNs, _RamNs) ->
?eval_debug_fun({?MODULE, commit_participant, log_commit},
[{tid, Tid}]),
do_commit(Tid, C, DumperMode),
- case Expect_schema_ack of
+ case ExpectAck of
false -> ignore;
true -> reply(Coord, {schema_commit, Tid, self()})
end,
@@ -1978,7 +1992,7 @@ sync_send_dirty(Tid, [Head | Tail], Tab, WaitFor) ->
Res = do_dirty(Tid, Head),
{WF, Res};
true ->
- {?MODULE, Node} ! {self(), {sync_dirty, Tid, ext_format(Head), Tab}},
+ {?MODULE, Node} ! {self(), {sync_dirty, Tid, Head, Tab}},
sync_send_dirty(Tid, Tail, Tab, [Node | WaitFor])
end;
sync_send_dirty(_Tid, [], _Tab, WaitFor) ->
@@ -1997,11 +2011,11 @@ async_send_dirty(Tid, [Head | Tail], Tab, ReadNode, WaitFor, Res) ->
NewRes = do_dirty(Tid, Head),
async_send_dirty(Tid, Tail, Tab, ReadNode, WaitFor, NewRes);
ReadNode == Node ->
- {?MODULE, Node} ! {self(), {sync_dirty, Tid, ext_format(Head), Tab}},
+ {?MODULE, Node} ! {self(), {sync_dirty, Tid, Head, Tab}},
NewRes = {'EXIT', {aborted, {node_not_running, Node}}},
async_send_dirty(Tid, Tail, Tab, ReadNode, [Node | WaitFor], NewRes);
true ->
- {?MODULE, Node} ! {self(), {async_dirty, Tid, ext_format(Head), Tab}},
+ {?MODULE, Node} ! {self(), {async_dirty, Tid, Head, Tab}},
async_send_dirty(Tid, Tail, Tab, ReadNode, WaitFor, Res)
end;
async_send_dirty(_Tid, [], _Tab, _ReadNode, WaitFor, Res) ->
@@ -2058,24 +2072,20 @@ ask_commit(Protocol, Tid, [Head | Tail], DiscNs, RamNs, WaitFor, Local) ->
Node == node() ->
ask_commit(Protocol, Tid, Tail, DiscNs, RamNs, WaitFor, Head);
true ->
- CR = ext_format(Head),
- Msg = {ask_commit, Protocol, Tid, CR, DiscNs, RamNs},
+ Msg = {ask_commit, convert_old(Protocol, Node), Tid, Head, DiscNs, RamNs},
{?MODULE, Node} ! {self(), Msg},
ask_commit(Protocol, Tid, Tail, DiscNs, RamNs, [Node | WaitFor], Local)
end;
ask_commit(_Protocol, _Tid, [], _DiscNs, _RamNs, WaitFor, Local) ->
{WaitFor, Local}.
-ext_format(#commit{ext=[]}=CR) -> CR;
-ext_format(#commit{node=Node, ext=Ext}=CR) ->
+convert_old(sync_asym_trans, Node) ->
case mnesia_monitor:needs_protocol_conversion(Node) of
- true ->
- case lists:keyfind(snmp, 1, Ext) of
- false -> CR#commit{ext=[]};
- {snmp, List} -> CR#commit{ext=List}
- end;
- false -> CR
- end.
+ true -> asym_trans;
+ false -> sync_asym_trans
+ end;
+convert_old(Protocol, _) ->
+ Protocol.
new_cr_format(#commit{ext=[]}=Cr) -> Cr;
new_cr_format(#commit{ext=[{_,_}|_]}=Cr) -> Cr;
@@ -2304,7 +2314,7 @@ reconfigure_participants(_, []) ->
%% tell mnesia_tm on all involved nodes (including the local node)
%% about the outcome.
tell_outcome(Tid, Protocol, Node, CheckNodes, TellNodes) ->
- Outcome = mnesia_recover:what_happened(Tid, Protocol, CheckNodes),
+ Outcome = mnesia_recover:what_happened(Tid, proto(Protocol), CheckNodes),
case Outcome of
aborted ->
rpc:abcast(TellNodes, ?MODULE, {Tid,{do_abort, {mnesia_down, Node}}});
@@ -2313,6 +2323,9 @@ tell_outcome(Tid, Protocol, Node, CheckNodes, TellNodes) ->
end,
Outcome.
+proto(sync_asym_trans) -> asym_trans;
+proto(Proto) -> Proto.
+
do_stop(#state{coordinators = Coordinators}) ->
Msg = {mnesia_down, node()},
lists:foreach(fun({Tid, _}) -> Tid#tid.pid ! Msg end, gb_trees:to_list(Coordinators)),
diff --git a/lib/mnesia/test/mnesia_isolation_test.erl b/lib/mnesia/test/mnesia_isolation_test.erl
index 49bcec14af..c99158945d 100644
--- a/lib/mnesia/test/mnesia_isolation_test.erl
+++ b/lib/mnesia/test/mnesia_isolation_test.erl
@@ -29,7 +29,7 @@
-export([no_conflict/1, simple_queue_conflict/1,
advanced_queue_conflict/1, simple_deadlock_conflict/1,
advanced_deadlock_conflict/1, schema_deadlock/1, lock_burst/1,
- nasty/1, basic_sticky_functionality/1,
+ nasty/1, basic_sticky_functionality/1, sticky_sync/1,
unbound1/1, unbound2/1,
create_table/1, delete_table/1, move_table_copy/1,
add_table_index/1, del_table_index/1, transform_table/1,
@@ -71,7 +71,8 @@ groups() ->
advanced_deadlock_conflict, schema_deadlock, lock_burst,
{group, sticky_locks}, {group, unbound_locking},
{group, admin_conflict}, nasty]},
- {sticky_locks, [], [basic_sticky_functionality]},
+ {sticky_locks, [],
+ [basic_sticky_functionality,sticky_sync]},
{unbound_locking, [], [unbound1, unbound2]},
{admin_conflict, [],
[create_table, delete_table, move_table_copy,
@@ -594,9 +595,49 @@ get_held() ->
mnesia_locker ! {get_table, self(), mnesia_sticky_locks},
receive {mnesia_sticky_locks, Locks} -> Locks end.
+sticky_sync(suite) -> [];
+sticky_sync(Config) when is_list(Config) ->
+ %% BUG ERIERL-768
+ Nodes = [N1, N2] = ?acquire_nodes(2, Config),
+
+ mnesia:create_table(dc, [{type, ordered_set}, {disc_copies, Nodes}]),
+ mnesia:create_table(ec, [{type, ordered_set}, {ram_copies, [N2]}]),
+
+ TestFun =
+ fun(I) ->
+ %% In first transaction we initialise {dc, I} record with value 0
+ First = fun() ->
+ %% Do a lot of writes into ram copies table
+ %% which on the Slave in do_commit will be
+ %% processed first
+ lists:foreach(fun(J) -> ok = mnesia:write(ec, {ec, J, 0}, write) end,
+ lists:seq(1, 750)),
+ %% Then set initial value of {dc, I} record to 0 with sticky_write
+ mnesia:write(dc, {dc, I, 0}, sticky_write)
+ end,
+ ok = mnesia:activity(transaction, First),
+ %% In second transaction we set value of {dc, I} record to 1
+ Upd = fun() ->
+ %% Modify a single ram copies record with ensured lock grant
+ %% (key not used in previous transactions)
+ %% we use this second table only to force asym_trans protocol
+ mnesia:write(ec, {ec, 1001 + I, 0}, write),
+ %% And set final version of {dc, I} record to 1 with sticky_write
+ mnesia:write(dc, {dc, I, 1}, sticky_write)
+ end,
+ ok = mnesia:activity(transaction, Upd)
+ end,
+
+ %% Fill 1000 dc records. At the end all dc records should have value 1.
+ lists:foreach(TestFun, lists:seq(1,1000)),
+ io:format("Written, check content~n",[]),
+ All = fun() -> mnesia:select(dc, [ {{dc, '_', 0}, [] ,['$_']} ]) end,
+ ?match({atomic, []}, rpc:call(N1, mnesia, sync_transaction, [All])),
+ ?match({atomic, []}, rpc:call(N2, mnesia, sync_transaction, [All])),
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ ?verify_mnesia(Nodes, []).
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
unbound1(suite) -> [];
unbound1(Config) when is_list(Config) ->
@@ -1036,29 +1077,57 @@ add_table_copy(Config) when is_list(Config) ->
Def = [{ram_copies, [ThisNode]}, {attributes, [key, attr1, attr2]}],
?match({atomic, ok}, mnesia:create_table(Tab, Def)),
insert(Tab, 50),
- {success, [A]} = ?start_activities([ThisNode]),
+ {success, [A]} = ?start_activities([ThisNode]),
mnesia_test_lib:start_sync_transactions([A], 0),
A ! fun() -> mnesia:write({Tab, 1, 1, updated}) end,
?match_receive({A, ok}), %% A is executed
- Pid = spawn_link(?MODULE, op, [self(), mnesia, add_table_copy,
+ Pid = spawn_link(?MODULE, op, [self(), mnesia, add_table_copy,
[Tab, Node2, ram_copies]]),
-
+
?match_receive(timeout), %% op waits for locks occupied by A
A ! end_trans, %% Kill A, locks should be released
- ?match_receive({A,{atomic,end_trans}}),
-
- receive
+ ?match_receive({A,{atomic,end_trans}}),
+
+ receive
Msg -> ?match({Pid, {atomic, ok}}, Msg)
after
timer:seconds(20) -> ?error("Operation timed out", [])
end,
+ ?match_receive({'EXIT', Pid, normal}),
sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
- ?match([], mnesia:system_info(held_locks)),
- ?match([], mnesia:system_info(lock_queue)),
+ ?match([], mnesia:system_info(held_locks)),
+ ?match([], mnesia:system_info(lock_queue)),
+
+ {atomic, ok} = mnesia:del_table_copy(Tab, Node2),
+ Self = self(),
+ New = spawn_link(Node2,
+ fun () ->
+ application:stop(mnesia),
+ Self ! {self(), ok},
+ io:format(user, "restart mnesia~n", []),
+ Self ! {self(), catch application:start(mnesia)}
+ end),
+ receive {New,ok} -> ok end,
+
+ Add = fun Add() ->
+ case mnesia:add_table_copy(Tab, Node2, disc_copies) of
+ {atomic, ok} -> ok;
+ _R -> io:format(user, "aborted with reason ~p~n", [_R]),
+ timer:sleep(10),
+ Add()
+ end
+ end,
+
+ ?match(ok, Add()),
+ ?match_receive({New,ok}),
+
+ sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async
+ ?match([], mnesia:system_info(held_locks)),
+ ?match([], mnesia:system_info(lock_queue)),
ok.
del_table_copy(suite) -> [];
diff --git a/lib/mnesia/test/mt b/lib/mnesia/test/mt
index a398ee0422..b169734f56 100755
--- a/lib/mnesia/test/mt
+++ b/lib/mnesia/test/mt
@@ -34,8 +34,35 @@ erlcmd="erl -sname a $p $args -mnesia_test_timeout"
erlcmd1="erl -sname a1 $p $args"
erlcmd2="erl -sname a2 $p $args"
-xterm -geometry 70x20+0+550 -T a1 -e $erlcmd1 &
-xterm -geometry 70x20+450+550 -T a2 -e $erlcmd2 &
+if test z"$MT_TERM" = z ; then
+ MT_TERM=xterm
+fi
+
+case $MT_TERM in
+ xterm)
+ geom0="-geometry 142x40+0+0"
+ geom1="-geometry 70x20+0+550"
+ geom2="-geometry 70x20+480+550"
+ title="-T"
+ exec="-e"
+ ;;
+ gnome-terminal)
+ geom0="--geometry 142x40+0+0"
+ geom1="--geometry 70x20+0+740"
+ geom2="--geometry 70x20+700+740"
+ title="--title"
+ exec="--hide-menubar --"
+ ;;
+ *rxvt)
+ geom0="-geometry 142x40+0+0"
+ geom1="-geometry 70x20+0+680"
+ geom2="-geometry 70x20+630+680"
+ title="-title"
+ exec="-e"
+esac
+
+$MT_TERM $geom1 $title a1 $exec $erlcmd1 &
+$MT_TERM $geom2 $title a2 $exec $erlcmd2 &
rm "$latest" 2>/dev/null
ln -s "$log" "$latest"
@@ -51,11 +78,6 @@ echo "Give the following command in order to see the outcome from node a@$h"":"
echo ""
echo " less test_log$$"
-ostype=`uname -s`
-if [ "$ostype" = "SunOS" ] ; then
- /usr/openwin/bin/xterm -geometry 145x40+0+0 -T a -l -lf "$log" -e $erlcmd &
-else
- xterm -geometry 145x40+0+0 -T a -e script -f -c "$erlcmd" "$log" &
-fi
+$MT_TERM $geom0 $title a $exec script -f -c "$erlcmd" "$log" &
tail -f "$log" | egrep 'Eval|<>ERROR|NYI'
diff --git a/lib/mnesia/vsn.mk b/lib/mnesia/vsn.mk
index aa5d9adb6d..f1a6333c18 100644
--- a/lib/mnesia/vsn.mk
+++ b/lib/mnesia/vsn.mk
@@ -1 +1 @@
-MNESIA_VSN = 4.16
+MNESIA_VSN = 4.16.1
diff --git a/lib/observer/Makefile b/lib/observer/Makefile
index 4770a72ba8..ffd73051b9 100644
--- a/lib/observer/Makefile
+++ b/lib/observer/Makefile
@@ -36,5 +36,6 @@ SPECIAL_TARGETS =
#
include $(ERL_TOP)/make/otp_subdir.mk
+DIA_PLT_APPS=runtime_tools et wx
include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/observer/doc/src/notes.xml b/lib/observer/doc/src/notes.xml
index f05e58dc21..f66ab95893 100644
--- a/lib/observer/doc/src/notes.xml
+++ b/lib/observer/doc/src/notes.xml
@@ -32,6 +32,34 @@
<p>This document describes the changes made to the Observer
application.</p>
+<section><title>Observer 2.9.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix bug after a user followed link on a pid from an
+ expanded term window.</p>
+ <p>
+ Own Id: OTP-15980 Aux Id: PR-2201 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Improved dark mode colors on Linux.</p>
+ <p>
+ Own Id: OTP-15916 Aux Id: ERL-921 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Observer 2.9.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/observer/src/cdv_bin_cb.erl b/lib/observer/src/cdv_bin_cb.erl
index 91d33474c8..819596b483 100644
--- a/lib/observer/src/cdv_bin_cb.erl
+++ b/lib/observer/src/cdv_bin_cb.erl
@@ -33,42 +33,43 @@ detail_pages() ->
[{"Binary", fun init_bin_page/2}].
init_bin_page(Parent,{Type,Bin}) ->
+ Cs = observer_lib:colors(Parent),
cdv_multi_wx:start_link(
Parent,
- [{"Format \~p",cdv_html_wx,{Type,format_bin_fun("~p",Bin)}},
- {"Format \~tp",cdv_html_wx,{Type,format_bin_fun("~tp",Bin)}},
- {"Format \~w",cdv_html_wx,{Type,format_bin_fun("~w",Bin)}},
- {"Format \~tw",cdv_html_wx,{Type,format_bin_fun("~tw",Bin)}},
- {"Format \~s",cdv_html_wx,{Type,format_bin_fun("~s",Bin)}},
- {"Format \~ts",cdv_html_wx,{Type,format_bin_fun("~ts",Bin)}},
- {"Hex",cdv_html_wx,{Type,hex_binary_fun(Bin)}},
- {"Term",cdv_html_wx,{Type,binary_to_term_fun(Bin)}}]).
+ [{"Format \~p",cdv_html_wx,{Type,format_bin_fun("~p",Bin,Cs)}},
+ {"Format \~tp",cdv_html_wx,{Type,format_bin_fun("~tp",Bin,Cs)}},
+ {"Format \~w",cdv_html_wx,{Type,format_bin_fun("~w",Bin,Cs)}},
+ {"Format \~tw",cdv_html_wx,{Type,format_bin_fun("~tw",Bin,Cs)}},
+ {"Format \~s",cdv_html_wx,{Type,format_bin_fun("~s",Bin,Cs)}},
+ {"Format \~ts",cdv_html_wx,{Type,format_bin_fun("~ts",Bin,Cs)}},
+ {"Hex",cdv_html_wx,{Type,hex_binary_fun(Bin,Cs)}},
+ {"Term",cdv_html_wx,{Type,binary_to_term_fun(Bin,Cs)}}]).
-format_bin_fun(Format,Bin) ->
+format_bin_fun(Format,Bin,Cs) ->
fun() ->
try io_lib:format(Format,[Bin]) of
- Str -> plain_html(lists:flatten(Str))
+ Str -> plain_html(lists:flatten(Str),Cs)
catch error:badarg ->
Warning = "This binary cannot be formatted with " ++ Format,
- observer_html_lib:warning(Warning)
+ observer_html_lib:warning(Warning,Cs)
end
end.
-binary_to_term_fun(Bin) ->
+binary_to_term_fun(Bin,Cs) ->
fun() ->
try binary_to_term(Bin) of
- Term -> plain_html(io_lib:format("~tp",[Term]))
+ Term -> plain_html(io_lib:format("~tp",[Term]),Cs)
catch error:badarg ->
Warning = "This binary cannot be converted to an Erlang term",
- observer_html_lib:warning(Warning)
+ observer_html_lib:warning(Warning,Cs)
end
end.
-define(line_break,25).
-hex_binary_fun(Bin) ->
+hex_binary_fun(Bin,Cs) ->
fun() ->
S = "<<" ++ format_hex(Bin,?line_break) ++ ">>",
- plain_html(io_lib:format("~s",[S]))
+ plain_html(io_lib:format("~s",[S]), Cs)
end.
format_hex(<<>>,_) ->
@@ -82,5 +83,5 @@ format_hex(<<B1:4,B2:4,Bin/binary>>,N) ->
[integer_to_list(B1,16),integer_to_list(B2,16),$,
| format_hex(Bin,N-1)].
-plain_html(Text) ->
- observer_html_lib:plain_page(Text).
+plain_html(Text,Cs) ->
+ observer_html_lib:plain_page(Text,Cs).
diff --git a/lib/observer/src/cdv_html_wx.erl b/lib/observer/src/cdv_html_wx.erl
index 8956173c93..83ee98de6e 100644
--- a/lib/observer/src/cdv_html_wx.erl
+++ b/lib/observer/src/cdv_html_wx.erl
@@ -30,7 +30,8 @@
%% Records
-record(state,
- {panel,
+ {parent,
+ panel,
app, %% which tool is the user
expand_table,
expand_wins=[],
@@ -62,7 +63,7 @@ init(ParentWin, HtmlText, Tab, App) ->
HtmlWin = observer_lib:html_window(ParentWin),
wxHtmlWindow:setPage(HtmlWin,HtmlText),
wx_misc:endBusyCursor(),
- {HtmlWin, #state{panel=HtmlWin,expand_table=Tab,app=App}}.
+ {HtmlWin, #state{parent=ParentWin, panel=HtmlWin,expand_table=Tab,app=App}}.
init(ParentWin, Callback) ->
{HtmlWin, State} = init(ParentWin, "", undefined, cdv),
@@ -70,12 +71,15 @@ init(ParentWin, Callback) ->
%%%%%%%%%%%%%%%%%%%%%%% Callbacks %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-handle_info(active, #state{panel=HtmlWin,delayed_fetch=Callback}=State)
+handle_info(active, #state{parent=Parent, panel=HtmlWin,delayed_fetch=Callback}=State)
when Callback=/=undefined ->
observer_lib:display_progress_dialog(HtmlWin,
"Crashdump Viewer",
"Reading data"),
- {{expand,HtmlText,Tab},TW} = Callback:get_info(),
+ {{expand,Title,Info,Tab},TW} = Callback:get_info(),
+ Cs = observer_lib:colors(Parent),
+ HtmlText = observer_html_lib:expandable_term(Title,Info,Tab,Cs),
+
observer_lib:sync_destroy_progress_dialog(),
wx_misc:beginBusyCursor(),
wxHtmlWindow:setPage(HtmlWin,HtmlText),
@@ -138,7 +142,8 @@ handle_event(#wx{event=#wxHtmlLink{type=command_html_link_clicked,
list_to_integer(Key3)}}},
expand(Id,cdv_term_cb,State);
_ when App =:= obs ->
- observer ! {open_link, Target};
+ observer ! {open_link, Target},
+ State;
_ ->
cdv_virtual_list_wx:start_detail_win(Target),
State
diff --git a/lib/observer/src/cdv_mod_cb.erl b/lib/observer/src/cdv_mod_cb.erl
index 2183e1aa3d..7f2cd0cf87 100644
--- a/lib/observer/src/cdv_mod_cb.erl
+++ b/lib/observer/src/cdv_mod_cb.erl
@@ -85,7 +85,8 @@ init_old_comp_page(Parent, Info) ->
init_info_page(Parent, undefined) ->
init_info_page(Parent, "");
init_info_page(Parent, String) ->
- cdv_html_wx:start_link(Parent,observer_html_lib:plain_page(String)).
+ Cs = observer_lib:colors(Parent),
+ cdv_html_wx:start_link(Parent,observer_html_lib:plain_page(String,Cs)).
format({Bin,q}) when is_binary(Bin) ->
[$'|binary_to_list(Bin)];
diff --git a/lib/observer/src/cdv_persistent_cb.erl b/lib/observer/src/cdv_persistent_cb.erl
index d5da18f7fc..90abc6a4f5 100644
--- a/lib/observer/src/cdv_persistent_cb.erl
+++ b/lib/observer/src/cdv_persistent_cb.erl
@@ -26,7 +26,4 @@
get_info() ->
Tab = ets:new(pt_expand,[set,public]),
{ok,PT,TW} = crashdump_viewer:persistent_terms(),
- {{expand,
- observer_html_lib:expandable_term("Persistent Terms",PT,Tab),
- Tab},
- TW}.
+ {{expand, "Persistent Terms", PT, Tab}, TW}.
diff --git a/lib/observer/src/cdv_proc_cb.erl b/lib/observer/src/cdv_proc_cb.erl
index 2497b4889e..61bd86f188 100644
--- a/lib/observer/src/cdv_proc_cb.erl
+++ b/lib/observer/src/cdv_proc_cb.erl
@@ -108,7 +108,7 @@ init_stack_page(Parent, Info) ->
init_memory_page(Parent, Info0, Tag, Heading) ->
Info = proplists:get_value(Tag,Info0),
Tab = proplists:get_value(expand_table,Info0),
- Html = observer_html_lib:expandable_term(Heading,Info,Tab),
+ Html = observer_html_lib:expandable_term(Heading,Info,Tab, observer_lib:colors(Parent)),
cdv_html_wx:start_link(Parent,{expand,Html,Tab}).
init_ets_page(Parent, Info) ->
diff --git a/lib/observer/src/cdv_term_cb.erl b/lib/observer/src/cdv_term_cb.erl
index 85da1d227a..a2a7a8750d 100644
--- a/lib/observer/src/cdv_term_cb.erl
+++ b/lib/observer/src/cdv_term_cb.erl
@@ -35,31 +35,32 @@ init_term_page(ParentWin, {Type, [Term, Tab]}) ->
Expanded = expand(Term, true),
BinSaved = expand(Term, Tab),
observer_lib:report_progress({ok,stop_pulse}),
+ Cs = observer_lib:colors(ParentWin),
cdv_multi_wx:start_link(
ParentWin,
- [{"Format \~p",cdv_html_wx,{Type, format_term_fun("~p",BinSaved,Tab)}},
- {"Format \~tp",cdv_html_wx,{Type,format_term_fun("~tp",BinSaved,Tab)}},
- {"Format \~w",cdv_html_wx,{Type,format_term_fun("~w",BinSaved,Tab)}},
- {"Format \~tw",cdv_html_wx,{Type,format_term_fun("~tw",BinSaved,Tab)}},
- {"Format \~s",cdv_html_wx,{Type,format_term_fun("~s",Expanded,Tab)}},
- {"Format \~ts",cdv_html_wx,{Type,format_term_fun("~ts",Expanded,Tab)}}]).
+ [{"Format \~p",cdv_html_wx,{Type, format_term_fun("~p",BinSaved,Tab,Cs)}},
+ {"Format \~tp",cdv_html_wx,{Type,format_term_fun("~tp",BinSaved,Tab,Cs)}},
+ {"Format \~w",cdv_html_wx,{Type,format_term_fun("~w",BinSaved,Tab,Cs)}},
+ {"Format \~tw",cdv_html_wx,{Type,format_term_fun("~tw",BinSaved,Tab,Cs)}},
+ {"Format \~s",cdv_html_wx,{Type,format_term_fun("~s",Expanded,Tab,Cs)}},
+ {"Format \~ts",cdv_html_wx,{Type,format_term_fun("~ts",Expanded,Tab,Cs)}}]).
-format_term_fun(Format,Term,Tab) ->
+format_term_fun(Format,Term,Tab,Cs) ->
fun() ->
observer_lib:report_progress({ok,"Formatting term"}),
observer_lib:report_progress({ok,start_pulse}),
try io_lib:format(Format,[Term]) of
- Str -> {expand, plain_html(Str), Tab}
+ Str -> {expand, plain_html(Str,Cs), Tab}
catch error:badarg ->
Warning = "This term cannot be formatted with " ++ Format,
- observer_html_lib:warning(Warning)
+ observer_html_lib:warning(Warning,Cs)
after
observer_lib:report_progress({ok,stop_pulse})
end
end.
-plain_html(Text) ->
- observer_html_lib:plain_page(Text).
+plain_html(Text,Cs) ->
+ observer_html_lib:plain_page(Text,Cs).
expand(['#CDVBin',Offset,Size,Pos], true) ->
{ok,Bin} = crashdump_viewer:expand_binary({Offset,Size,Pos}),
diff --git a/lib/observer/src/cdv_virtual_list_wx.erl b/lib/observer/src/cdv_virtual_list_wx.erl
index 14877b7eab..51e85e17c1 100644
--- a/lib/observer/src/cdv_virtual_list_wx.erl
+++ b/lib/observer/src/cdv_virtual_list_wx.erl
@@ -96,8 +96,9 @@ start_detail_win_2(Callback,Id) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
init([ParentWin, Callback, Owner]) ->
- {Holder,TW} = spawn_table_holder(Callback, Owner),
Panel = wxPanel:new(ParentWin),
+ Attrs = observer_lib:create_attrs(Panel),
+ {Holder,TW} = spawn_table_holder(Callback, Owner, Attrs),
{Grid,MenuCols} = create_list_box(Panel, Holder, Callback, Owner),
Sizer = wxBoxSizer:new(?wxVERTICAL),
wxSizer:add(Sizer, Grid, [{flag, ?wxEXPAND bor ?wxALL},
@@ -233,7 +234,8 @@ handle_call(new_dump, _From,
Ref = erlang:monitor(process,Holder),
Holder ! stop,
receive {'DOWN',Ref,_,_,_} -> ok end,
- {NewHolder,TW} = spawn_table_holder(Callback, all),
+ Attrs = observer_lib:create_attrs(Grid),
+ {NewHolder,TW} = spawn_table_holder(Callback, all, Attrs),
{reply, ok, State#state{detail_wins=[],holder=NewHolder,trunc_warn=TW}};
handle_call(Msg, _From, State) ->
@@ -329,9 +331,8 @@ handle_event(Event, State) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%TABLE HOLDER%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-spawn_table_holder(Callback, Owner) ->
+spawn_table_holder(Callback, Owner, Attrs) ->
{Info,TW} = Callback:get_info(Owner),
- Attrs = observer_lib:create_attrs(),
Parent = self(),
Holder =
case Owner of
diff --git a/lib/observer/src/cdv_wx.erl b/lib/observer/src/cdv_wx.erl
index 7100cc8790..8ad5da857e 100644
--- a/lib/observer/src/cdv_wx.erl
+++ b/lib/observer/src/cdv_wx.erl
@@ -197,8 +197,7 @@ setup(#state{frame=Frame, notebook=Notebook}=State) ->
MemPanel = add_page(Notebook, ?MEM_STR, cdv_multi_wx, cdv_mem_cb),
%% Persistent Terms Panel
- PersistentPanel = add_page(Notebook, ?PERSISTENT_STR,
- cdv_html_wx, cdv_persistent_cb),
+ PersistentPanel = add_page(Notebook, ?PERSISTENT_STR, cdv_html_wx, cdv_persistent_cb),
%% Memory Panel
IntPanel = add_page(Notebook, ?INT_STR, cdv_multi_wx, cdv_int_tab_cb),
diff --git a/lib/observer/src/observer_app_wx.erl b/lib/observer/src/observer_app_wx.erl
index 8c3eef5411..c4527ba063 100644
--- a/lib/observer/src/observer_app_wx.erl
+++ b/lib/observer/src/observer_app_wx.erl
@@ -48,7 +48,7 @@
usegc = false
}).
--record(paint, {font, pen, brush, sel, links}).
+-record(paint, {font, fg, pen, brush, sel, links}).
-record(app, {ptree, n2p, links, dim}).
-record(box, {x,y, w,h, s1}).
@@ -92,7 +92,8 @@ init([Notebook, Parent, _Config]) ->
Extra = wxBoxSizer:new(?wxVERTICAL),
DrawingArea = wxScrolledWindow:new(P2, [{winid, ?DRAWAREA},
{style,?wxFULL_REPAINT_ON_RESIZE}]),
- wxWindow:setBackgroundColour(DrawingArea, ?wxWHITE),
+ BG = wxWindow:getBackgroundColour(Apps),
+ wxWindow:setBackgroundStyle(DrawingArea, ?wxBG_STYLE_SYSTEM),
wxWindow:setVirtualSize(DrawingArea, 800, 800),
wxSplitterWindow:setMinimumPaneSize(Splitter,50),
wxSizer:add(Extra, DrawingArea, [{flag, ?wxEXPAND},{proportion, 1}]),
@@ -127,7 +128,17 @@ init([Notebook, Parent, _Config]) ->
Font0
end,
SelCol = wxSystemSettings:getColour(?wxSYS_COLOUR_HIGHLIGHT),
- GreyBrush = wxBrush:new({230,230,240}),
+ {Fg,BGBrush,Pen} =
+ case observer_lib:is_darkmode(BG) of
+ false ->
+ {wxSystemSettings:getColour(?wxSYS_COLOUR_BTNTEXT),
+ wxBrush:new(wxSystemSettings:getColour(?wxSYS_COLOUR_BTNSHADOW)),
+ wxPen:new({80,80,80}, [{width, Scale * 2}])};
+ true ->
+ {wxSystemSettings:getColour(?wxSYS_COLOUR_BTNTEXT),
+ wxBrush:new(wxSystemSettings:getColour(?wxSYS_COLOUR_BTNSHADOW)),
+ wxPen:new({0,0,0}, [{width, Scale * 2}])}
+ end,
SelBrush = wxBrush:new(SelCol),
LinkPen = wxPen:new(SelCol, [{width, Scale * 2}]),
process_flag(trap_exit, true),
@@ -137,8 +148,9 @@ init([Notebook, Parent, _Config]) ->
app_w =DrawingArea,
usegc = UseGC,
paint=#paint{font = Font,
- pen = wxPen:new({80,80,80}, [{width, Scale * 2}]),
- brush= GreyBrush,
+ fg = Fg,
+ pen = Pen,
+ brush= BGBrush,
sel = SelBrush,
links= LinkPen
}
@@ -306,11 +318,11 @@ handle_info({delivery, _Pid, app, _Curr, {[], [], [], []}},
handle_info({delivery, Pid, app, Curr, AppData},
State = #state{panel=Panel, appmon=Pid, current=Curr, usegc=UseGC,
- app_w=AppWin, paint=#paint{font=Font}}) ->
+ app_w=AppWin, paint=#paint{fg=Fg, font=Font}}) ->
GC = if UseGC -> {?wxGC:create(AppWin), false};
true -> {false, wxWindowDC:new(AppWin)}
end,
- setFont(GC, Font, {0,0,0}),
+ setFont(GC, Font, Fg),
App = build_tree(AppData, GC),
destroy_gc(GC),
setup_scrollbar(AppWin, App),
@@ -508,13 +520,13 @@ tree_map([], _ , Acc) -> Acc.
draw(_DC, undefined, _, _) ->
ok;
draw(DC, #app{dim={_W,_H}, ptree=Tree, links=Links}, Sel,
- #paint{font=Font, pen=Pen, brush=Brush, links=LPen, sel=SelBrush}) ->
+ #paint{font=Font, fg=Fg, pen=Pen, brush=Brush, links=LPen, sel=SelBrush}) ->
setPen(DC, LPen),
[draw_xlink(Link, DC) || Link <- Links],
setPen(DC, Pen),
%% ?wxGC:drawRectangle(DC, 2,2, _W-2,_H-2), %% DEBUG
setBrush(DC, Brush),
- setFont(DC, Font, {0,0,0}),
+ setFont(DC, Font, Fg),
draw_tree(Tree, root, DC),
case Sel of
undefined -> ok;
diff --git a/lib/observer/src/observer_defs.hrl b/lib/observer/src/observer_defs.hrl
index 504d0877d9..7902b32cba 100644
--- a/lib/observer/src/observer_defs.hrl
+++ b/lib/observer/src/observer_defs.hrl
@@ -36,6 +36,7 @@
check = false
}).
+-record(colors, {fg, even, odd}).
-record(attrs, {even, odd, searched, deleted, changed_odd, changed_even, new_odd, new_even}).
-define(EVEN(Row), ((Row rem 2) =:= 0)).
-define(BG_EVEN, {230,230,250}).
diff --git a/lib/observer/src/observer_html_lib.erl b/lib/observer/src/observer_html_lib.erl
index c67fa28c6d..4c92a8faab 100644
--- a/lib/observer/src/observer_html_lib.erl
+++ b/lib/observer/src/observer_html_lib.erl
@@ -24,9 +24,9 @@
%% viewer. No logic or states are kept by this module.
%%
--export([plain_page/1,
- expandable_term/3,
- warning/1]).
+-export([plain_page/2,
+ expandable_term/4,
+ warning/2]).
-include("crashdump_viewer.hrl").
-include("observer_defs.hrl").
@@ -34,8 +34,9 @@
%%%-----------------------------------------------------------------
%%% Display the given information as is, no heading
%%% Empty body if no info exists.
-warning(Info) ->
- header(body(warning_body(Info))).
+warning(Info, Colors0) ->
+ Colors = convert(Colors0),
+ header(body(warning_body(Info), Colors)).
warning_body(Info) ->
[warn(Info)].
@@ -43,18 +44,22 @@ warning_body(Info) ->
%%%-----------------------------------------------------------------
%%% Display the given information as is, no heading
%%% Empty body if no info exists.
-plain_page(Info) ->
- header(body(plain_body(Info))).
+plain_page(Info, Colors0) ->
+ Colors = convert(Colors0),
+ header(body(plain_body(Info), Colors)).
plain_body(Info) ->
[pre(href_proc_port(lists:flatten(Info)))].
%%%-----------------------------------------------------------------
%%% Expanded memory
-expandable_term(Heading,Expanded,Tab) ->
- header(Heading,body(expandable_term_body(Heading,Expanded,Tab))).
+expandable_term(Heading,Expanded,Tab, Colors0) ->
+ Colors = convert(Colors0),
+ header(Heading,
+ body(expandable_term_body(Heading,Expanded,Tab,Colors),
+ Colors)).
-expandable_term_body(Heading,[],_Tab) ->
+expandable_term_body(Heading,[],_Tab, _) ->
[case Heading of
"MsgQueue" -> "No messages were found";
"Message Queue" -> "No messages were found";
@@ -65,7 +70,7 @@ expandable_term_body(Heading,[],_Tab) ->
"SaslLog" -> "No log entry was found";
"Persistent Terms" -> "No persistent terms were found"
end];
-expandable_term_body(Heading,Expanded,Tab) ->
+expandable_term_body(Heading,Expanded,Tab, Colors) ->
Attr = "BORDER=0 CELLPADDING=0 CELLSPACING=1 WIDTH=100%",
[case Heading of
"MsgQueue" ->
@@ -74,7 +79,7 @@ expandable_term_body(Heading,Expanded,Tab) ->
[th("WIDTH=70%","Message"),
th("WIDTH=30%","SeqTraceToken")]) |
element(1, lists:mapfoldl(fun(Msg, Even) ->
- {msgq_table(Tab, Msg, Even),
+ {msgq_table(Tab, Msg, Even, Colors),
not Even}
end,
true, Expanded))]);
@@ -84,7 +89,7 @@ expandable_term_body(Heading,Expanded,Tab) ->
[th("WIDTH=10%","Id"),
th("WIDTH=90%","Message")]) |
element(1, lists:mapfoldl(fun(Msg, {Even,N}) ->
- {msgq_table(Tab, Msg, N, Even),
+ {msgq_table(Tab, Msg, N, Even, Colors),
{not Even, N+1}}
end,
{true,1}, Expanded))]);
@@ -94,7 +99,7 @@ expandable_term_body(Heading,Expanded,Tab) ->
[th("WIDTH=20%","Label"),
th("WIDTH=80%","Term")]) |
element(1, lists:mapfoldl(fun(Entry, Even) ->
- {stackdump_table(Tab, Entry, Even),
+ {stackdump_table(Tab, Entry, Even, Colors),
not Even}
end, true, Expanded))]);
"ProcState" ->
@@ -103,7 +108,7 @@ expandable_term_body(Heading,Expanded,Tab) ->
[th("WIDTH=20%","Label"),
th("WIDTH=80%","Information")]) |
element(1, lists:mapfoldl(fun(Entry, Even) ->
- {proc_state(Tab, Entry,Even),
+ {proc_state(Tab, Entry,Even, Colors),
not Even}
end, true, Expanded))]);
"SaslLog" ->
@@ -115,37 +120,37 @@ expandable_term_body(Heading,Expanded,Tab) ->
[th("WIDTH=30%","Key"),
th("WIDTH=70%","Value")]) |
element(1, lists:mapfoldl(fun(Entry, Even) ->
- {dict_table(Tab, Entry,Even),
+ {dict_table(Tab, Entry, Even, Colors),
not Even}
end, true, Expanded))])
end].
-msgq_table(Tab,{Msg0,Token0}, Even) ->
+msgq_table(Tab,{Msg0,Token0}, Even, Colors) ->
Token = case Token0 of
[] -> "";
_ -> io_lib:fwrite("~w",[Token0])
end,
Msg = all_or_expand(Tab,Msg0),
- tr(color(Even),[td(pre(Msg)), td(Token)]).
+ tr(color(Even, Colors),[td(pre(Msg)), td(Token)]).
-msgq_table(Tab,Msg0, Id, Even) ->
+msgq_table(Tab,Msg0, Id, Even, Colors) ->
Msg = all_or_expand(Tab,Msg0),
- tr(color(Even),[td(integer_to_list(Id)), td(pre(Msg))]).
+ tr(color(Even, Colors),[td(integer_to_list(Id)), td(pre(Msg))]).
-stackdump_table(Tab,{Label0,Term0},Even) ->
+stackdump_table(Tab,{Label0,Term0},Even, Colors) ->
Label = io_lib:format("~w",[Label0]),
Term = all_or_expand(Tab,Term0),
- tr(color(Even), [td("VALIGN=center",pre(Label)), td(pre(Term))]).
+ tr(color(Even, Colors), [td("VALIGN=center",pre(Label)), td(pre(Term))]).
-dict_table(Tab,{Key0,Value0}, Even) ->
+dict_table(Tab,{Key0,Value0}, Even, Colors) ->
Key = all_or_expand(Tab,Key0),
Value = all_or_expand(Tab,Value0),
- tr(color(Even), [td("VALIGN=center",pre(Key)), td(pre(Value))]).
+ tr(color(Even, Colors), [td("VALIGN=center",pre(Key)), td(pre(Value))]).
-proc_state(Tab,{Key0,Value0}, Even) ->
+proc_state(Tab,{Key0,Value0}, Even, Colors) ->
Key = lists:flatten(io_lib:format("~ts",[Key0])),
Value = all_or_expand(Tab,Value0),
- tr(color(Even), [td("VALIGN=center",Key), td(pre(Value))]).
+ tr(color(Even, Colors), [td("VALIGN=center",Key), td(pre(Value))]).
all_or_expand(Tab,Term) ->
Preview = io_lib:format("~tP",[Term,8]),
@@ -171,8 +176,8 @@ all_or_expand(Tab,Bin,_PreviewStr,_Expand)
Term = io_lib:format("~tp", [OBSBin]),
href_proc_port(lists:flatten(Term), true).
-color(true) -> io_lib:format("BGCOLOR=\"#~2.16.0B~2.16.0B~2.16.0B\"", tuple_to_list(?BG_EVEN));
-color(false) -> io_lib:format("BGCOLOR=\"#~2.16.0B~2.16.0B~2.16.0B\"", tuple_to_list(?BG_ODD)).
+color(true, #colors{even=Even}) -> "BGCOLOR="++Even;
+color(false,#colors{odd=Odd}) -> "BGCOLOR="++Odd.
%%%-----------------------------------------------------------------
%%% Internal library
@@ -180,10 +185,10 @@ start_html() ->
"<HTML>\n".
stop_html() ->
"</HTML>".
-start_html_body() ->
- "<BODY BGCOLOR=\"#FFFFFF\">\n".
+start_html_body(#colors{even=Even, fg=Fg}) ->
+ "<BODY BGCOLOR=" ++ Even ++ ">\n <FONT COLOR=" ++ Fg ++ ">\n".
stop_html_body() ->
- "</BODY>\n".
+ "</FONT> </BODY>\n".
header(Body) ->
header("","",Body).
@@ -205,8 +210,8 @@ only_html_header(Title,JavaScript) ->
JavaScript,
"</HEAD>\n"].
-body(Text) ->
- [start_html_body(),
+body(Text, Colors) ->
+ [start_html_body(Colors),
Text,
stop_html_body()].
@@ -417,3 +422,8 @@ warn([]) ->
[];
warn(Warning) ->
font("COLOR=\"#FF0000\"",p([Warning,br(),br()])).
+
+convert(#colors{fg={FR,FB,FG}, even={ER,EB,EG}, odd={OR,OG,OB}}) ->
+ #colors{fg = io_lib:format("\"#~2.16.0B~2.16.0B~2.16.0B\"", [FR,FB,FG]),
+ even = io_lib:format("\"#~2.16.0B~2.16.0B~2.16.0B\"", [ER,EB,EG]),
+ odd = io_lib:format("\"#~2.16.0B~2.16.0B~2.16.0B\"", [OR,OG,OB])}.
diff --git a/lib/observer/src/observer_lib.erl b/lib/observer/src/observer_lib.erl
index 7c68b0ebb6..7d115306bd 100644
--- a/lib/observer/src/observer_lib.erl
+++ b/lib/observer/src/observer_lib.erl
@@ -28,8 +28,8 @@
interval_dialog/4, start_timer/1, start_timer/2, stop_timer/1, timer_config/1,
display_info/2, display_info/3, fill_info/2, update_info/2, to_str/1,
create_menus/3, create_menu_item/3,
- create_attrs/0,
- set_listctrl_col_size/2,
+ is_darkmode/1, colors/1, create_attrs/1,
+ set_listctrl_col_size/2, mix/3,
create_status_bar/1,
html_window/1, html_window/2,
make_obsbin/2,
@@ -373,26 +373,44 @@ create_menu_item(separator, Menu, Index) ->
wxMenu:insertSeparator(Menu, Index),
Index+1.
-create_attrs() ->
- Font = wxSystemSettings:getFont(?wxSYS_DEFAULT_GUI_FONT),
+colors(Window) ->
+ DarkMode = is_darkmode(wxWindow:getBackgroundColour(Window)),
Text = case wxSystemSettings:getColour(?wxSYS_COLOUR_LISTBOXTEXT) of
- {255,255,255,_} -> {10,10,10}; %% Is white on Mac for some reason
- Color -> Color
- end,
- #attrs{even = wxListItemAttr:new(Text, ?BG_EVEN, Font),
- odd = wxListItemAttr:new(Text, ?BG_ODD, Font),
- deleted = wxListItemAttr:new(?FG_DELETED, ?BG_DELETED, Font),
- changed_even = wxListItemAttr:new(Text, mix(?BG_CHANGED,?BG_EVEN), Font),
- changed_odd = wxListItemAttr:new(Text, mix(?BG_CHANGED,?BG_ODD), Font),
- new_even = wxListItemAttr:new(Text, mix(?BG_NEW,?BG_EVEN), Font),
- new_odd = wxListItemAttr:new(Text, mix(?BG_NEW, ?BG_ODD), Font),
- searched = wxListItemAttr:new(Text, ?BG_SEARCHED, Font)
- }.
-
-mix(RGB,_) -> RGB.
-
-%% mix({R,G,B},{MR,MG,MB}) ->
-%% {trunc(R*MR/255), trunc(G*MG/255), trunc(B*MB/255)}.
+ {255,255,255,_} when not DarkMode -> {10,10,10}; %% Is white on Mac for some reason
+ Color -> Color
+ end,
+ Even = wxSystemSettings:getColour(?wxSYS_COLOUR_LISTBOX),
+ Odd = mix(Even, wxSystemSettings:getColour(?wxSYS_COLOUR_HIGHLIGHT), 0.8),
+ #colors{fg=rgb(Text), even=rgb(Even), odd=rgb(Odd)}.
+
+create_attrs(Window) ->
+ Font = wxSystemSettings:getFont(?wxSYS_DEFAULT_GUI_FONT),
+ #colors{fg=Text, even=Even, odd=Odd} = colors(Window),
+ #attrs{even = wxListItemAttr:new(Text, Even, Font),
+ odd = wxListItemAttr:new(Text, Odd, Font),
+ deleted = wxListItemAttr:new(?FG_DELETED, ?BG_DELETED, Font),
+ changed_even = wxListItemAttr:new(Text, mix(?BG_CHANGED, ?BG_EVEN, 0.9), Font),
+ changed_odd = wxListItemAttr:new(Text, mix(?BG_CHANGED, ?BG_ODD, 0.9), Font),
+ new_even = wxListItemAttr:new(Text, mix(?BG_NEW, ?BG_EVEN, 0.9), Font),
+ new_odd = wxListItemAttr:new(Text, mix(?BG_NEW, ?BG_ODD, 0.9), Font),
+ searched = wxListItemAttr:new(Text, ?BG_SEARCHED, Font)
+ }.
+
+rgb({R,G,B,_}) -> {R,G,B};
+rgb({_,_,_}=RGB) -> RGB.
+
+mix(RGB,{MR,MG,MB,_}, V) ->
+ mix(RGB, {MR,MG,MB}, V);
+mix({R,G,B,_}, RGB, V) ->
+ mix({R,G,B}, RGB, V);
+mix({R,G,B},{MR,MG,MB}, V) when V =< 1.0 ->
+ {min(255, round(R*V+MR*(1.0-V))),
+ min(255, round(G*V+MG*(1.0-V))),
+ min(255, round(B*V+MB*(1.0-V)))}.
+
+
+is_darkmode({R,G,B,_}) ->
+ ((R+G+B) div 3) < 100.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/observer/src/observer_perf_wx.erl b/lib/observer/src/observer_perf_wx.erl
index 79271addf2..50a6d6a915 100644
--- a/lib/observer/src/observer_perf_wx.erl
+++ b/lib/observer/src/observer_perf_wx.erl
@@ -55,7 +55,7 @@
-define(wxGC, wxGraphicsContext).
--record(paint, {font, small, pen, pen2, pens, dot_pens, usegc = false}).
+-record(paint, {font, small, fg, pen, pen2, pens, dot_pens, usegc = false}).
start_link(Notebook, Parent, Config) ->
wx_object:start_link(?MODULE, [Notebook, Parent, Config], []).
@@ -126,7 +126,16 @@ setup_graph_drawing(Panels) ->
SF = wxFont:new(Scale * (DefSize-2), DefFamily, ?wxFONTSTYLE_NORMAL, ?wxFONTWEIGHT_NORMAL),
{F, SF}
end,
- BlackPen = wxPen:new({0,0,0}, [{width, Scale}]),
+ BG = wxWindow:getBackgroundColour((hd(Panels))#win.panel),
+ Fg = case observer_lib:is_darkmode(BG) of
+ false -> {0,0,0};
+ true -> wxSystemSettings:getColour(?wxSYS_COLOUR_BTNTEXT)
+ end,
+
+ PenColor = case observer_lib:is_darkmode(BG) of
+ false -> {0,0,0};
+ true -> {0,0,0}
+ end,
Pens = [wxPen:new(Col, [{width, Scale}, {style, ?wxSOLID}])
|| Col <- tuple_to_list(colors())],
DotPens = [wxPen:new(Col, [{width, Scale}, {style, ?wxDOT}])
@@ -134,8 +143,9 @@ setup_graph_drawing(Panels) ->
#paint{usegc = UseGC,
font = Font,
small = SmallFont,
- pen = ?wxGREY_PEN,
- pen2 = BlackPen,
+ fg = Fg, %% Text color
+ pen = wxPen:new(PenColor),
+ pen2 = wxPen:new(PenColor, [{width, Scale}]),
pens = list_to_tuple(Pens),
dot_pens = list_to_tuple(DotPens)
}.
@@ -525,7 +535,7 @@ draw_win(DC, #win{name=Name, no_samples=Samples, geom=#{scale:={WS,HS}},
DrawBs(),
ok;
-draw_win(DC, #win{no_samples=Samples} = Win,Ti, #paint{small=Small}=Paint) ->
+draw_win(DC, #win{no_samples=Samples} = Win,Ti, #paint{fg=Fg, small=Small}=Paint) ->
%% Draw Error Msg
try draw_borders(DC, Ti, Win, Paint) of
{X0,_Y0,DrawBs} ->
@@ -533,7 +543,7 @@ draw_win(DC, #win{no_samples=Samples} = Win,Ti, #paint{small=Small}=Paint) ->
true -> "Waiting for data";
false -> "Information not available"
end,
- setFont(DC, Small, {0,0,0}),
+ setFont(DC, Small, Fg),
{_,WW} = getSize(DC),
drawText(DC, Text, X0 + 100, WW div 2),
DrawBs(),
@@ -628,7 +638,7 @@ spline_tan(Y0, Y1, Y2, Y3) ->
draw_borders(DC, #ti{secs=Secs, fetch=FetchFreq},
#win{name=Type, geom=Geom, info=Info, max={_,_,Unit,_}},
- #paint{pen=Pen, pen2=Pen2, font=Font, small=Small}) ->
+ #paint{pen=Pen, pen2=Pen2, fg=Fg, font=Font, small=Small}) ->
#{p0:={GraphX0, GraphY0}, p1:={GraphX1,GraphY1}, scale:={ScaleW0,_},
txsz:={TW,TH,SpaceW}, txt:={BottomTextY, MaxTextY}, strs:={Str1,Str2,Str3}} = Geom,
@@ -640,7 +650,7 @@ draw_borders(DC, #ti{secs=Secs, fetch=FetchFreq},
GraphY50 = GraphY0 + (GraphY1 - GraphY0) / 2,
GraphY75 = GraphY0 + 3*(GraphY1 - GraphY0) / 4,
- setFont(DC, Small, {0,0,0}),
+ setFont(DC, Small, Fg),
Align = fun(Str, Y) ->
{StrW, _} = getTextExtent(DC, Str),
drawText(DC, Str, GraphX0 - StrW - ?BW, Y)
@@ -670,11 +680,11 @@ draw_borders(DC, #ti{secs=Secs, fetch=FetchFreq},
strokeLine(DC, GraphX0-3, GraphY50, GraphX1, GraphY50),
strokeLine(DC, GraphX0-3, GraphY75, GraphX1, GraphY75),
- setFont(DC, Font, {0,0,0}),
+ setFont(DC, Font, Fg),
Text = fun(X,Y, Str, PenId) ->
if PenId == 0 ->
- setFont(DC, Font, {0,0,0});
+ setFont(DC, Font, Fg);
PenId > 0 ->
Id = 1 + ((PenId-1) rem tuple_size(colors())),
setFont(DC, Font, element(Id, colors()))
diff --git a/lib/observer/src/observer_port_wx.erl b/lib/observer/src/observer_port_wx.erl
index 00cf1b5fba..5cb6d9bc22 100644
--- a/lib/observer/src/observer_port_wx.erl
+++ b/lib/observer/src/observer_port_wx.erl
@@ -61,7 +61,8 @@
inet}).
-record(opt, {sort_key=2,
- sort_incr=true
+ sort_incr=true,
+ odd_bg
}).
-record(state,
@@ -111,7 +112,10 @@ init([Notebook, Parent, Config]) ->
wxListCtrl:connect(Grid, size, [{skip, true}]),
wxWindow:setFocus(Grid),
- {Panel, #state{grid=Grid, parent=Parent, panel=Panel, timer=Config}}.
+ Even = wxSystemSettings:getColour(?wxSYS_COLOUR_LISTBOX),
+ Odd = observer_lib:mix(Even, wxSystemSettings:getColour(?wxSYS_COLOUR_HIGHLIGHT), 0.8),
+ Opt = #opt{odd_bg=Odd},
+ {Panel, #state{grid=Grid, parent=Parent, panel=Panel, timer=Config, opt=Opt}}.
handle_event(#wx{id=?ID_REFRESH},
State = #state{node=Node, grid=Grid, opt=Opt}) ->
@@ -553,7 +557,7 @@ filter_monitor_info() ->
update_grid(Grid, Sel, Opt, Ports) ->
wx:batch(fun() -> update_grid2(Grid, Sel, Opt, Ports) end).
-update_grid2(Grid, Sel, #opt{sort_key=Sort,sort_incr=Dir}, Ports) ->
+update_grid2(Grid, Sel, #opt{sort_key=Sort,sort_incr=Dir, odd_bg=BG}, Ports) ->
wxListCtrl:deleteAllItems(Grid),
Update =
fun(#port{id = Id,
@@ -563,8 +567,8 @@ update_grid2(Grid, Sel, #opt{sort_key=Sort,sort_incr=Dir}, Ports) ->
controls = Ctrl},
Row) ->
_Item = wxListCtrl:insertItem(Grid, Row, ""),
- if (Row rem 2) =:= 0 ->
- wxListCtrl:setItemBackgroundColour(Grid, Row, ?BG_EVEN);
+ if (Row rem 2) =:= 1 ->
+ wxListCtrl:setItemBackgroundColour(Grid, Row, BG);
true -> ignore
end,
diff --git a/lib/observer/src/observer_pro_wx.erl b/lib/observer/src/observer_pro_wx.erl
index 4ab4a78462..6b359f3c44 100644
--- a/lib/observer/src/observer_pro_wx.erl
+++ b/lib/observer/src/observer_pro_wx.erl
@@ -94,7 +94,7 @@ start_link(Notebook, Parent, Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
init([Notebook, Parent, Config]) ->
- Attrs = observer_lib:create_attrs(),
+ Attrs = observer_lib:create_attrs(Notebook),
Self = self(),
Acc = maps:get(acc, Config, false),
Holder = spawn_link(fun() -> init_table_holder(Self, Acc, Attrs) end),
diff --git a/lib/observer/src/observer_procinfo.erl b/lib/observer/src/observer_procinfo.erl
index bd5fed0951..637a090a15 100644
--- a/lib/observer/src/observer_procinfo.erl
+++ b/lib/observer/src/observer_procinfo.erl
@@ -214,12 +214,14 @@ init_process_page(Panel, Pid) ->
init_message_page(Parent, Pid, Table) ->
Win = observer_lib:html_window(Parent),
+ Cs = observer_lib:colors(Parent),
Update = fun() ->
case observer_wx:try_rpc(node(Pid), erlang, process_info,
[Pid, messages])
of
{messages, Messages} ->
- Html = observer_html_lib:expandable_term("Message Queue", Messages, Table),
+ Html = observer_html_lib:expandable_term("Message Queue", Messages,
+ Table, Cs),
wxHtmlWindow:setPage(Win, Html);
_ ->
throw(process_undefined)
@@ -230,11 +232,12 @@ init_message_page(Parent, Pid, Table) ->
init_dict_page(Parent, Pid, Table) ->
Win = observer_lib:html_window(Parent),
+ Cs = observer_lib:colors(Parent),
Update = fun() ->
case observer_wx:try_rpc(node(Pid), erlang, process_info, [Pid, dictionary])
of
{dictionary,Dict} ->
- Html = observer_html_lib:expandable_term("Dictionary", Dict, Table),
+ Html = observer_html_lib:expandable_term("Dictionary", Dict, Table, Cs),
wxHtmlWindow:setPage(Win, Html);
_ ->
throw(process_undefined)
@@ -254,6 +257,8 @@ init_stack_page(Parent, Pid) ->
wxListCtrl:insertColumn(LCtrl, 1, Li),
wxListCtrl:setColumnWidth(LCtrl, 1, Scale * 300),
wxListItem:destroy(Li),
+ Even = wxSystemSettings:getColour(?wxSYS_COLOUR_LISTBOX),
+ Odd = observer_lib:mix(Even, wxSystemSettings:getColour(?wxSYS_COLOUR_HIGHLIGHT), 0.8),
Update = fun() ->
case observer_wx:try_rpc(node(Pid), erlang, process_info,
[Pid, current_stacktrace])
@@ -262,8 +267,8 @@ init_stack_page(Parent, Pid) ->
wxListCtrl:deleteAllItems(LCtrl),
wx:foldl(fun({M, F, A, Info}, Row) ->
_Item = wxListCtrl:insertItem(LCtrl, Row, ""),
- ?EVEN(Row) andalso
- wxListCtrl:setItemBackgroundColour(LCtrl, Row, ?BG_EVEN),
+ ?EVEN(Row) orelse
+ wxListCtrl:setItemBackgroundColour(LCtrl, Row, Odd),
wxListCtrl:setItem(LCtrl, Row, 0, observer_lib:to_str({M,F,A})),
FileLine = case Info of
[{file,File},{line,Line}] ->
@@ -288,9 +293,10 @@ init_stack_page(Parent, Pid) ->
init_state_page(Parent, Pid, Table) ->
Win = observer_lib:html_window(Parent),
+ Cs = observer_lib:colors(Parent),
Update = fun() ->
StateInfo = fetch_state_info(Pid),
- Html = observer_html_lib:expandable_term("ProcState", StateInfo, Table),
+ Html = observer_html_lib:expandable_term("ProcState", StateInfo, Table, Cs),
wxHtmlWindow:setPage(Win, Html)
end,
Update(),
@@ -341,6 +347,7 @@ fetch_state_info2(Pid, M) ->
init_log_page(Parent, Pid, Table) ->
Win = observer_lib:html_window(Parent),
+ Cs = observer_lib:colors(Parent),
Update = fun() ->
Fd = spawn_link(fun() -> io_server() end),
rpc:call(node(Pid), rb, rescan, [[{start_log, Fd}]]),
@@ -353,7 +360,7 @@ init_log_page(Parent, Pid, Table) ->
NbBlanks = length(Pref) - 1,
Re = "(<" ++ Pref ++ "\.[^>]{1,}>)[ ]{"++ integer_to_list(NbBlanks) ++ "}",
Look = re:replace(ExpPid, Re, "\\1", [global, {return, list}]),
- Html = observer_html_lib:expandable_term("SaslLog", Look, Table),
+ Html = observer_html_lib:expandable_term("SaslLog", Look, Table, Cs),
wxHtmlWindow:setPage(Win, Html)
end,
Update(),
diff --git a/lib/observer/src/observer_tv_table.erl b/lib/observer/src/observer_tv_table.erl
index 7bd67a0f0b..32d75f77d4 100644
--- a/lib/observer/src/observer_tv_table.erl
+++ b/lib/observer/src/observer_tv_table.erl
@@ -116,8 +116,8 @@ init([Parent, Opts]) ->
TabId = table_id(Table),
ColumnNames = column_names(Node, Source, TabId),
KeyPos = key_pos(Node, Source, TabId),
-
- Attrs = observer_lib:create_attrs(),
+ Panel = wxPanel:new(Frame),
+ Attrs = observer_lib:create_attrs(Panel),
Self = self(),
Holder = spawn_link(fun() ->
@@ -125,7 +125,6 @@ init([Parent, Opts]) ->
length(ColumnNames), Node, Attrs)
end),
- Panel = wxPanel:new(Frame),
Sizer = wxBoxSizer:new(?wxVERTICAL),
Style = ?wxLC_REPORT bor ?wxLC_VIRTUAL bor ?wxLC_SINGLE_SEL bor ?wxLC_HRULES,
Grid = wxListCtrl:new(Panel, [{style, Style},
diff --git a/lib/observer/src/observer_tv_wx.erl b/lib/observer/src/observer_tv_wx.erl
index 9743a6ed42..d622b1423b 100644
--- a/lib/observer/src/observer_tv_wx.erl
+++ b/lib/observer/src/observer_tv_wx.erl
@@ -71,7 +71,7 @@ init([Notebook, Parent, Config]) ->
Style = ?wxLC_REPORT bor ?wxLC_VIRTUAL bor ?wxLC_SINGLE_SEL bor ?wxLC_HRULES,
Self = self(),
- Attrs = observer_lib:create_attrs(),
+ Attrs = observer_lib:create_attrs(Panel),
Holder = spawn_link(fun() -> init_table_holder(Self, Attrs) end),
CBs = [{onGetItemText, fun(_, Item,Col) -> get_row(Holder, Item, Col) end},
{onGetItemAttr, fun(_, Item) -> get_attr(Holder, Item) end}],
diff --git a/lib/observer/test/crashdump_helper.erl b/lib/observer/test/crashdump_helper.erl
index 84ed99afa5..10d88c994a 100644
--- a/lib/observer/test/crashdump_helper.erl
+++ b/lib/observer/test/crashdump_helper.erl
@@ -48,7 +48,7 @@ n1_proc(Creator,_N2,Pid2,Port2,_L) ->
Ref = make_ref(),
Pid = self(),
Bin = list_to_binary(lists:seq(1, 255)),
- <<_:2,SubBin:17/binary,_/bits>> = Bin,
+ <<_:2,SubBin:65/binary,_/bits>> = Bin,
register(named_port,Port),
@@ -102,7 +102,7 @@ remote_proc(P1,Creator) ->
end).
create_binaries() ->
- Sizes = lists:seq(60, 70) ++ lists:seq(120, 140),
+ Sizes = lists:seq(100, 120) ++ lists:seq(200, 240),
[begin
<<H:16/unit:8>> = erlang:md5(<<Size:32>>),
Data = ((H bsl (8*150)) div (H+7919)),
@@ -113,7 +113,7 @@ create_sub_binaries(Bins) ->
[create_sub_binary(Bin, Start, LenSub) ||
Bin <- Bins,
Start <- [0,1,2,3,4,5,10,22],
- LenSub <- [0,1,2,3,4,6,9]].
+ LenSub <- [0,1,2,3,4,6,9,65]].
create_sub_binary(Bin, Start, LenSub) ->
Len = byte_size(Bin) - LenSub - Start,
diff --git a/lib/observer/vsn.mk b/lib/observer/vsn.mk
index c16c43f942..3bb316a5e2 100644
--- a/lib/observer/vsn.mk
+++ b/lib/observer/vsn.mk
@@ -1 +1 @@
-OBSERVER_VSN = 2.9.1
+OBSERVER_VSN = 2.9.2
diff --git a/lib/odbc/Makefile b/lib/odbc/Makefile
index dfa224ecd6..ee39992a84 100644
--- a/lib/odbc/Makefile
+++ b/lib/odbc/Makefile
@@ -69,12 +69,6 @@ do_configure: configure
configure: configure.in
autoconf
-.PHONY: info
-
-info:
- @echo "ODBC_VSN: $(ODBC_VSN)"
-
-
# ----------------------------------------------------
# Application (source) release targets
# ----------------------------------------------------
diff --git a/lib/os_mon/c_src/cpu_sup.c b/lib/os_mon/c_src/cpu_sup.c
index c96a5c9f7c..98a2526aab 100644
--- a/lib/os_mon/c_src/cpu_sup.c
+++ b/lib/os_mon/c_src/cpu_sup.c
@@ -359,7 +359,7 @@ static cpu_t *read_procstat(FILE *fp, cpu_t *cpu) {
memset(cpu, 0, sizeof(cpu_t));
return cpu;
}
- sscanf(buffer, "cpu%u %Lu %Lu %Lu %Lu %Lu %Lu %Lu %Lu",
+ sscanf(buffer, "cpu%u %llu %llu %llu %llu %llu %llu %llu %llu",
&(cpu->id),
&(cpu->user),
&(cpu->nice_user),
diff --git a/lib/os_mon/doc/src/notes.xml b/lib/os_mon/doc/src/notes.xml
index 1f169263e9..63efa96e2f 100644
--- a/lib/os_mon/doc/src/notes.xml
+++ b/lib/os_mon/doc/src/notes.xml
@@ -31,6 +31,29 @@
</header>
<p>This document describes the changes made to the OS_Mon application.</p>
+<section><title>Os_Mon 2.5.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix <c>disk_sup</c> to ignore squashfs on Linux when
+ determining if a mounted filesystem is full or not.</p>
+ <p>
+ Own Id: OTP-15778</p>
+ </item>
+ <item>
+ <p>
+ Fix bug where <c>cpu_sup:util()</c> always returned 100%
+ on systems not using gnu libc, for example Alpine OS.</p>
+ <p>
+ Own Id: OTP-15974 Aux Id: ERL-1012 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Os_Mon 2.5</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/os_mon/vsn.mk b/lib/os_mon/vsn.mk
index 845443d329..6081e181ff 100644
--- a/lib/os_mon/vsn.mk
+++ b/lib/os_mon/vsn.mk
@@ -1 +1 @@
-OS_MON_VSN = 2.5
+OS_MON_VSN = 2.5.1
diff --git a/lib/parsetools/doc/src/leex.xml b/lib/parsetools/doc/src/leex.xml
index 3b82f60201..3944c650d8 100644
--- a/lib/parsetools/doc/src/leex.xml
+++ b/lib/parsetools/doc/src/leex.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2009</year><year>2017</year>
+ <year>2009</year><year>2019</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -193,7 +193,7 @@ Token = tuple()</code>
but used through the i/o system where it can typically be
called in an application by:</p>
<code>
-io:request(InFile, {get_until,Prompt,Module,token,[Line]})
+io:request(InFile, {get_until,unicode,Prompt,Module,token,[Line]})
-> TokenRet</code>
</desc>
</func>
@@ -240,7 +240,7 @@ io:request(InFile, {get_until,Prompt,Module,token,[Line]})
but used through the i/o system where it can typically be
called in an application by:</p>
<code>
-io:request(InFile, {get_until,Prompt,Module,tokens,[Line]})
+io:request(InFile, {get_until,unicode,Prompt,Module,tokens,[Line]})
-> TokensRet</code>
</desc>
</func>
diff --git a/lib/public_key/Makefile b/lib/public_key/Makefile
index 3b6cb3ce6c..ed8901a8af 100644
--- a/lib/public_key/Makefile
+++ b/lib/public_key/Makefile
@@ -38,4 +38,6 @@ SPECIAL_TARGETS =
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_subdir.mk
+DIA_PLT_APPS=asn crypto
+
include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/public_key/doc/src/notes.xml b/lib/public_key/doc/src/notes.xml
index 57d9898661..f47988d6d8 100644
--- a/lib/public_key/doc/src/notes.xml
+++ b/lib/public_key/doc/src/notes.xml
@@ -35,6 +35,33 @@
<file>notes.xml</file>
</header>
+<section><title>Public_Key 1.7</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Support Password based encryption with AES</p>
+ <p>
+ Own Id: OTP-15870 Aux Id: ERL-952 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Change dialyzer spec to avoid confusion</p>
+ <p>
+ Own Id: OTP-15843 Aux Id: ERL-915 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Public_Key 1.6.7</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/public_key/src/pubkey_cert.erl b/lib/public_key/src/pubkey_cert.erl
index 12c61e158f..d85f322918 100644
--- a/lib/public_key/src/pubkey_cert.erl
+++ b/lib/public_key/src/pubkey_cert.erl
@@ -1169,7 +1169,7 @@ validity(Opts) ->
Format =
fun({Y,M,D}) ->
lists:flatten(
- io_lib:format("~4..0w~2..0w~2..0w000000Z",[Y,M,D]))
+ io_lib:format("~4..0w~2..0w~2..0w130000Z",[Y,M,D]))
end,
#'Validity'{notBefore={generalTime, Format(DefFrom)},
notAfter ={generalTime, Format(DefTo)}}.
diff --git a/lib/public_key/vsn.mk b/lib/public_key/vsn.mk
index a5e4ec8d5a..1982218574 100644
--- a/lib/public_key/vsn.mk
+++ b/lib/public_key/vsn.mk
@@ -1 +1 @@
-PUBLIC_KEY_VSN = 1.6.7
+PUBLIC_KEY_VSN = 1.7
diff --git a/lib/reltool/Makefile b/lib/reltool/Makefile
index 70c80e1c3c..0a39d7daa8 100644
--- a/lib/reltool/Makefile
+++ b/lib/reltool/Makefile
@@ -36,4 +36,6 @@ SPECIAL_TARGETS =
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_subdir.mk
+DIA_PLT_APPS=wx sasl
+
include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/runtime_tools/Makefile b/lib/runtime_tools/Makefile
index 9a0822b9b6..4b0f1633ab 100644
--- a/lib/runtime_tools/Makefile
+++ b/lib/runtime_tools/Makefile
@@ -36,5 +36,6 @@ SPECIAL_TARGETS =
#
include $(ERL_TOP)/make/otp_subdir.mk
+DIA_PLT_APPS=mnesia
include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/runtime_tools/doc/src/notes.xml b/lib/runtime_tools/doc/src/notes.xml
index 1b94c3e6d9..210d63687c 100644
--- a/lib/runtime_tools/doc/src/notes.xml
+++ b/lib/runtime_tools/doc/src/notes.xml
@@ -32,6 +32,22 @@
<p>This document describes the changes made to the Runtime_Tools
application.</p>
+<section><title>Runtime_Tools 1.14</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Fix <c>dbg:stop_clear/0</c> to also clear trace events
+ (<c>send</c> and <c>'receive'</c>).</p>
+ <p>
+ Own Id: OTP-16044</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Runtime_Tools 1.13.3</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/runtime_tools/src/dbg.erl b/lib/runtime_tools/src/dbg.erl
index 92938ed5c1..21e180e854 100644
--- a/lib/runtime_tools/src/dbg.erl
+++ b/lib/runtime_tools/src/dbg.erl
@@ -604,6 +604,8 @@ stop() ->
stop_clear() ->
{ok, _} = ctp(),
+ {ok, _} = ctpe('receive'),
+ {ok, _} = ctpe('send'),
stop().
%%% Calling the server.
diff --git a/lib/runtime_tools/test/dbg_SUITE.erl b/lib/runtime_tools/test/dbg_SUITE.erl
index 98cbc0360f..4d319c29c1 100644
--- a/lib/runtime_tools/test/dbg_SUITE.erl
+++ b/lib/runtime_tools/test/dbg_SUITE.erl
@@ -20,7 +20,7 @@
-module(dbg_SUITE).
%% Test functions
--export([all/0, suite/0,
+-export([all/0, suite/0, init_per_suite/1, end_per_suite/1,
big/1, tiny/1, simple/1, message/1, distributed/1, port/1,
send/1, recv/1,
ip_port/1, file_port/1, file_port2/1,
@@ -46,6 +46,12 @@ all() ->
local_trace, saved_patterns, tracer_exit_on_stop,
erl_tracer, distributed_erl_tracer].
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ dbg:stop_clear(),
+ ok.
%% Rudimentary interface test
big(Config) when is_list(Config) ->
@@ -83,7 +89,7 @@ big(Config) when is_list(Config) ->
ok=file:set_cwd(OldCurDir)
after
- dbg:stop()
+ dbg:stop_clear()
end,
ok.
@@ -114,7 +120,7 @@ tiny(Config) when is_list(Config) ->
failure
end
after
- ok = dbg:stop(),
+ dbg:stop_clear(),
ok = file:set_cwd(OldCurDir)
end,
ok.
@@ -130,7 +136,7 @@ simple(Config) when is_list(Config) ->
S = self(),
[{trace,S,call,{dbg,ltp,[]}}] = flush()
after
- dbg:stop()
+ dbg:stop_clear()
end,
ok.
@@ -146,7 +152,7 @@ message(Config) when is_list(Config) ->
ok = dbg:ltp(),
ok = dbg:ln()
after
- stop()
+ dbg:stop_clear()
end,
S = self(),
[{trace,S,call,{dbg,ltp,[]},S},
@@ -229,7 +235,7 @@ send(Config) when is_list(Config) ->
ok
after
- stop()
+ dbg:stop_clear()
end.
send_test(Pid, Pattern, Msg, TraceEvent) ->
@@ -340,7 +346,7 @@ recv(Config) when is_list(Config) ->
ok
after
- stop()
+ dbg:stop_clear()
end.
recv_test(Pid, Pattern, Msg, TraceEvent) ->
@@ -404,8 +410,8 @@ distributed(Config) when is_list(Config) ->
%%
stop()
after
- stop_slave(Node),
- stop()
+ dbg:stop_clear(),
+ stop_slave(Node)
end,
ok.
@@ -455,7 +461,7 @@ local_trace(Config) when is_list(Config) ->
{dbg_SUITE,not_exported,1},
{error,badarith}}] = flush()
after
- stop()
+ dbg:stop_clear()
end,
ok.
@@ -480,7 +486,7 @@ port(Config) when is_list(Config) ->
{trace,Port,getting_linked,S},
{trace,Port,closed,normal}] = flush()
after
- dbg:stop()
+ dbg:stop_clear()
end,
ok.
@@ -506,7 +512,7 @@ saved_patterns(Config) when is_list(Config) ->
S = self(),
[{trace,S,call,{dbg,ltp,[]},blahonga}] = flush()
after
- stop()
+ dbg:stop_clear()
end,
ok.
@@ -538,7 +544,7 @@ ip_port(Config) when is_list(Config) ->
[{trace,S,call,{dbg,ltp,[]},S},
{trace,S,call,{dbg,ln,[]},hej}] = flush()
after
- stop()
+ dbg:stop_clear()
end,
ok.
@@ -554,7 +560,7 @@ ip_port_busy(Config) when is_list(Config) ->
io:format("Error reason = ~p~n", [Reason]),
true = port_close(Port)
after
- dbg:stop()
+ dbg:stop_clear()
end,
ok.
@@ -584,7 +590,7 @@ file_port(Config) when is_list(Config) ->
{trace,S,call,{dbg,ln,[]},hej},
end_of_trace] = flush()
after
- stop(),
+ dbg:stop_clear(),
file:delete(FName)
end,
ok.
@@ -617,7 +623,7 @@ file_port2(Config) when is_list(Config) ->
stop(),
[] = flush()
after
- stop(),
+ dbg:stop_clear(),
file:delete(FName)
end,
ok.
@@ -688,7 +694,7 @@ wrap_port(Config) when is_list(Config) ->
%%
lists:map(fun(F) -> file:delete(F) end, Files)
after
- stop()
+ dbg:stop_clear()
end,
ok.
@@ -763,7 +769,7 @@ wrap_port_time(Config) when is_list(Config) ->
end_of_trace] = flush(),
lists:map(fun(F) -> file:delete(F) end, Files)
after
- stop()
+ dbg:stop_clear()
end,
ok.
@@ -789,7 +795,7 @@ with_seq_trace(Config) when is_list(Config) ->
{seq_trace,0,{send,_,Server,S,{dbg,{ok,Tracer}}}}] =
flush()
after
- stop()
+ dbg:stop_clear()
end,
ok.
@@ -800,7 +806,7 @@ dead_suspend(Config) when is_list(Config) ->
try
survived = run_dead_suspend()
after
- stop()
+ dbg:stop_clear()
end.
run_dead_suspend() ->
diff --git a/lib/runtime_tools/vsn.mk b/lib/runtime_tools/vsn.mk
index 3f38574be4..c01dd60009 100644
--- a/lib/runtime_tools/vsn.mk
+++ b/lib/runtime_tools/vsn.mk
@@ -1 +1 @@
-RUNTIME_TOOLS_VSN = 1.13.3
+RUNTIME_TOOLS_VSN = 1.14
diff --git a/lib/sasl/Makefile b/lib/sasl/Makefile
index 1710606d3d..06af80fd35 100644
--- a/lib/sasl/Makefile
+++ b/lib/sasl/Makefile
@@ -36,4 +36,6 @@ SPECIAL_TARGETS =
#
include $(ERL_TOP)/make/otp_subdir.mk
+DIA_PLT_APPS=tools
+
include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/sasl/doc/src/notes.xml b/lib/sasl/doc/src/notes.xml
index a1ae404776..245542242b 100644
--- a/lib/sasl/doc/src/notes.xml
+++ b/lib/sasl/doc/src/notes.xml
@@ -31,6 +31,22 @@
</header>
<p>This document describes the changes made to the SASL application.</p>
+<section><title>SASL 3.4.1</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ The net module has been split into 'net' (kernel) and
+ prim_net (preloaded).</p>
+ <p>
+ Own Id: OTP-15765</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>SASL 3.4</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/sasl/src/sasl.appup.src b/lib/sasl/src/sasl.appup.src
index 22a9027b7c..aabe1a904b 100644
--- a/lib/sasl/src/sasl.appup.src
+++ b/lib/sasl/src/sasl.appup.src
@@ -31,9 +31,13 @@
{<<"^3\\.2\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
{<<"^3\\.2\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
{<<"^3\\.3$">>,[restart_new_emulator]},
- {<<"^3\\.3\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}],
+ {<<"^3\\.3\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
+ {<<"^3\\.4$">>,[restart_new_emulator]},
+ {<<"^3\\.4\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}],
[{<<"^3\\.2$">>,[restart_new_emulator]},
{<<"^3\\.2\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
{<<"^3\\.2\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
{<<"^3\\.3$">>,[restart_new_emulator]},
- {<<"^3\\.3\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}]}.
+ {<<"^3\\.3\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
+ {<<"^3\\.4$">>,[restart_new_emulator]},
+ {<<"^3\\.4\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]}]}.
diff --git a/lib/sasl/src/systools_lib.erl b/lib/sasl/src/systools_lib.erl
index dd97aeff08..f5489e7900 100644
--- a/lib/sasl/src/systools_lib.erl
+++ b/lib/sasl/src/systools_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -63,8 +63,8 @@ read_term(File) ->
end.
read_term_from_stream(Stream, File) ->
- _ = epp:set_encoding(Stream),
- R = io:request(Stream, {get_until,'',erl_scan,tokens,[1]}),
+ Encoding = epp:set_encoding(Stream),
+ R = io:request(Stream, {get_until,Encoding,'',erl_scan,tokens,[1]}),
case R of
{ok,Toks,_EndLine} ->
case erl_parse:parse_term(Toks) of
diff --git a/lib/sasl/test/installer.erl b/lib/sasl/test/installer.erl
index 5429008a5f..d763fad11b 100644
--- a/lib/sasl/test/installer.erl
+++ b/lib/sasl/test/installer.erl
@@ -903,7 +903,7 @@ start_client(TestNode,Client,Sname) ->
receive
{nodeup, Node} ->
wait_started(TestNode,Node)
- after 30000 ->
+ after 60000 ->
?print([{start_client,failed,Node},net_adm:ping(Node)]),
?fail({"cannot start", Node})
end.
diff --git a/lib/sasl/test/release_handler_SUITE.erl b/lib/sasl/test/release_handler_SUITE.erl
index 2ff2f29591..98f96b7170 100644
--- a/lib/sasl/test/release_handler_SUITE.erl
+++ b/lib/sasl/test/release_handler_SUITE.erl
@@ -2331,7 +2331,7 @@ reg_print_proc() ->
rh_print() ->
receive
{print, {Module,Line}, [H|T]} ->
- ?t:format("=== ~p:~p - ~p",[Module,Line,H]),
+ ?t:format("=== ~p:~p - ~tp",[Module,Line,H]),
lists:foreach(fun(Term) -> ?t:format(" ~tp",[Term]) end, T),
?t:format("",[]),
rh_print();
diff --git a/lib/sasl/test/rh_test_lib.erl b/lib/sasl/test/rh_test_lib.erl
index dacd8b6b9f..6425fce7a7 100644
--- a/lib/sasl/test/rh_test_lib.erl
+++ b/lib/sasl/test/rh_test_lib.erl
@@ -29,7 +29,7 @@ erlsrv(Erlsrv,Action,Name) ->
erlsrv(Erlsrv,Action,Name,Rest) ->
Cmd = "\"" ++ Erlsrv ++ "\" " ++ atom_to_list(Action) ++ " " ++
Name ++ " " ++ Rest,
- io:format("erlsrv cmd: ~p~n",[Cmd]),
+ io:format("erlsrv cmd: ~tp~n",[Cmd]),
Port = open_port({spawn, Cmd}, [stream, {line, 100}, eof, in]),
Res = recv_prog_output(Port),
case Res of
diff --git a/lib/sasl/vsn.mk b/lib/sasl/vsn.mk
index 8838b514da..b13d03dc76 100644
--- a/lib/sasl/vsn.mk
+++ b/lib/sasl/vsn.mk
@@ -1 +1 @@
-SASL_VSN = 3.4
+SASL_VSN = 3.4.1
diff --git a/lib/snmp/Makefile b/lib/snmp/Makefile
index 86f227b01d..52debf1670 100644
--- a/lib/snmp/Makefile
+++ b/lib/snmp/Makefile
@@ -2,7 +2,7 @@
# %CopyrightBegin%
#
-# Copyright Ericsson AB 1996-2019. All Rights Reserved.
+# Copyright Ericsson AB 1996-2016. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -71,24 +71,6 @@ do_configure: configure
configure: configure.in
autoconf
-.PHONY: info gclean
-
-info:
- @echo "OS: $(OS)"
- @echo "DOCB: $(DOCB)"
- @echo ""
- @echo "SNMP_VSN: $(SNMP_VSN)"
- @echo "APP_VSN: $(APP_VSN)"
- @echo ""
- @echo "DIA_PLT: $(DIA_PLT)"
- @echo "DIA_ANALYSIS: $(DIA_ANALYSIS)"
- @echo ""
-
-
-gclean:
- git clean -fXd
-
-
# ----------------------------------------------------
# Application (source) release targets
# ----------------------------------------------------
@@ -130,30 +112,6 @@ tar: $(APP_TAR_FILE)
$(APP_TAR_FILE): $(APP_DIR)
(cd $(APP_RELEASE_DIR); gtar zcf $(APP_TAR_FILE) $(DIR_NAME))
-dclean:
- rm -f $(DIA_PLT)
- rm -f $(DIA_ANALYSIS)
-
-dialyzer_plt: $(DIA_PLT)
-
-$(DIA_PLT): Makefile
- @echo "Building $(APPLICATION) plt file"
- @dialyzer --build_plt \
- --output_plt $@ \
- -r ../$(APPLICATION)/ebin \
- ../../lib/kernel/ebin \
- ../../lib/stdlib/ebin \
- ../../lib/runtime_tools/ebin \
- ../../lib/crypto/ebin \
- ../../lib/mnesia/ebin \
- ../../erts/preloaded/ebin \
- --output $(DIA_ANALYSIS) \
- --verbose
-
-dialyzer: $(DIA_PLT)
- @echo "Running dialyzer on $(APPLICATION)"
- @dialyzer --plt $< \
- ../$(APPLICATION)/ebin \
- --verbose
+DIA_PLT_APPS=runtime_tools crypto mnesia
include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/snmp/doc/src/notes.xml b/lib/snmp/doc/src/notes.xml
index 780e0cae76..dd499d1069 100644
--- a/lib/snmp/doc/src/notes.xml
+++ b/lib/snmp/doc/src/notes.xml
@@ -34,7 +34,62 @@
</header>
- <section><title>SNMP 5.3</title>
+ <section><title>SNMP 5.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix various minor issues related to Dialyzer. Mostly
+ these are dialyzer warnings, but there was also some
+ minor bugs detected by Dialyzer.</p>
+ <p>
+ Own Id: OTP-15932</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Fixed a dets usage problem detected by dialyzer.</p>
+ <p>
+ Own Id: OTP-10400 Aux Id: kunagi-253 [164] </p>
+ </item>
+ <item>
+ <p>
+ The function snmp:print_version_info() prints various
+ version info. For each module a number of items are
+ printed, such as app vsn and md5 digest. And an attempt
+ was also made to print "compile time". This used to be
+ available in the module_info for each module, but has now
+ been removed.</p>
+ <p>
+ Own Id: OTP-15330</p>
+ </item>
+ <item>
+ <p>
+ The use of the deprecated random module has been replaced
+ the with rand module.</p>
+ <p>
+ Own Id: OTP-15331</p>
+ </item>
+ <item>
+ <p>
+ Removed use of the deprecated function
+ erlang:get_stacktrace(). Instead make use of the 'catch
+ Class:Error:Stacktrace' feature.</p>
+ <p>
+ Own Id: OTP-15332</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SNMP 5.3</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/snmp/src/agent/snmp_generic.erl b/lib/snmp/src/agent/snmp_generic.erl
index 26a0dd0648..429f78abd0 100644
--- a/lib/snmp/src/agent/snmp_generic.erl
+++ b/lib/snmp/src/agent/snmp_generic.erl
@@ -424,8 +424,8 @@ table_check_status(NameDb, Col, ?'RowStatus_createAndGo', RowIndex, Cols) ->
_:_E:_S ->
?vtrace(
"failed construct row (createAndGo): "
- " n Error: ~p"
- " n Stack: ~p",
+ "~n Error: ~p"
+ "~n Stack: ~p",
[_E, _S]),
{noCreation, Col} % Bad RowIndex
end;
@@ -444,8 +444,8 @@ table_check_status(NameDb, Col, ?'RowStatus_createAndWait', RowIndex, Cols) ->
_:_E:_S ->
?vtrace(
"failed construct row (createAndWait): "
- " n Error: ~p"
- " n Stack: ~p",
+ "~n Error: ~p"
+ "~n Stack: ~p",
[_E, _S]),
{noCreation, Col} % Bad RowIndex
end;
diff --git a/lib/snmp/src/agent/snmpa_general_db.erl b/lib/snmp/src/agent/snmpa_general_db.erl
index 431302b6cc..0d4e39f56d 100644
--- a/lib/snmp/src/agent/snmpa_general_db.erl
+++ b/lib/snmp/src/agent/snmpa_general_db.erl
@@ -534,16 +534,16 @@ dets_backup(close, _Cont, _Name, B) ->
ok;
dets_backup(read, Cont1, Name, B) ->
case dets:bchunk(Name, Cont1) of
+ {error, _} = ERROR ->
+ ERROR;
+ '$end_of_table' ->
+ dets:close(B),
+ end_of_input;
{Cont2, Data} ->
F = fun(Arg) ->
dets_backup(Arg, Cont2, Name, B)
end,
- {Data, F};
- '$end_of_table' ->
- dets:close(B),
- end_of_input;
- Error ->
- Error
+ {Data, F}
end.
diff --git a/lib/snmp/src/agent/snmpa_supervisor.erl b/lib/snmp/src/agent/snmpa_supervisor.erl
index 2cb0556001..7d5c6da2c8 100644
--- a/lib/snmp/src/agent/snmpa_supervisor.erl
+++ b/lib/snmp/src/agent/snmpa_supervisor.erl
@@ -22,7 +22,7 @@
-behaviour(supervisor).
%% External exports
--export([start_link/2]).
+-export([start_link/2, stop/0, stop/1]).
-export([start_sub_sup/1, start_master_sup/1]).
-export([start_sub_agent/3, stop_sub_agent/1]).
@@ -91,6 +91,39 @@ start_link(master, Opts, {takeover, Node}) ->
Else
end.
+
+stop() ->
+ stop(0).
+
+stop(Timeout) ->
+ case whereis(?SERVER) of
+ Pid when is_pid(Pid) ->
+ stop(Pid, Timeout);
+ _ ->
+ not_running
+ end.
+
+%% For some unfathomable reason there is no "nice" way to stop
+%% a supervisor. The "normal" way to do it is:
+%% 1) exit(Pid, kill) (kaboom)
+%% 2) If the caller is the *parent*: exit(Pid, shutdown)
+%% So, here we do it the really ugly way...but since this function is
+%% intended for testing (mostly)...
+stop(Pid, Timeout) when (Timeout =:= 0) ->
+ sys:terminate(Pid, shutdown),
+ ok;
+stop(Pid, Timeout) ->
+ MRef = erlang:monitor(process, Pid),
+ sys:terminate(Pid, shutdown),
+ receive
+ {'DOWN', MRef, process, Pid, _} ->
+ ok
+ after Timeout ->
+ erlang:demonitor(MRef, [flush]),
+ {error, timeout}
+ end.
+
+
get_own_loaded_mibs() ->
AgentInfo = snmpa:info(snmp_master_agent),
[ Name || {Name, _, _} <- loaded_mibs(AgentInfo) ].
diff --git a/lib/snmp/src/compile/snmpc_misc.erl b/lib/snmp/src/compile/snmpc_misc.erl
index 312074f2e7..d0fee418e1 100644
--- a/lib/snmp/src/compile/snmpc_misc.erl
+++ b/lib/snmp/src/compile/snmpc_misc.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2003-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2003-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -156,7 +156,8 @@ loop(Fd, Res, Func, StartLine, File) ->
%% io:read modified to give us line numbers.
%%-----------------------------------------------------------------
read(Io, Prompt, StartLine) ->
- case io:request(Io, {get_until, Prompt, erl_scan, tokens, [StartLine]}) of
+ Enc = latin1,
+ case io:request(Io, {get_until, Enc, Prompt, erl_scan, tokens, [StartLine]}) of
{ok, Toks, EndLine} ->
case erl_parse:parse_term(Toks) of
{ok, Term} ->
diff --git a/lib/snmp/src/manager/snmpm.erl b/lib/snmp/src/manager/snmpm.erl
index cf8c95d69f..8e60cecaf9 100644
--- a/lib/snmp/src/manager/snmpm.erl
+++ b/lib/snmp/src/manager/snmpm.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -32,7 +32,7 @@
%% Management API
start/0, start/1,
start_link/0, start_link/1,
- stop/0,
+ stop/0, stop/1,
monitor/0, demonitor/1,
notify_started/1, cancel_notify_started/1,
@@ -196,7 +196,12 @@ start(Opts) ->
ok.
stop() ->
- snmpm_supervisor:stop().
+ stop(0).
+
+stop(Timeout) when (Timeout =:= infinity) orelse
+ (is_integer(Timeout) andalso (Timeout >= 0)) ->
+ snmpm_supervisor:stop(Timeout).
+
monitor() ->
diff --git a/lib/snmp/src/manager/snmpm_supervisor.erl b/lib/snmp/src/manager/snmpm_supervisor.erl
index c36bbe1bdd..bc66025c6f 100644
--- a/lib/snmp/src/manager/snmpm_supervisor.erl
+++ b/lib/snmp/src/manager/snmpm_supervisor.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -24,7 +24,7 @@
%% External exports
--export([start_link/2, stop/0]).
+-export([start_link/2, stop/0, stop/1]).
%% supervisor callbacks
-export([init/1]).
@@ -38,26 +38,54 @@
%%%-------------------------------------------------------------------
%%% API
%%%-------------------------------------------------------------------
+
start_link(Type, Opts) ->
?d("start_link -> entry with"
"~n Opts: ~p", [Opts]),
SupName = {local, ?MODULE},
supervisor:start_link(SupName, ?MODULE, [Type, Opts]).
+
stop() ->
+ stop(0).
+
+stop(Timeout) ->
?d("stop -> entry", []),
case whereis(?SERVER) of
Pid when is_pid(Pid) ->
- ?d("stop -> Pid: ~p", [Pid]),
- exit(Pid, shutdown),
- ?d("stop -> stopped", []),
- ok;
+ stop(Pid, Timeout);
_ ->
?d("stop -> not running", []),
not_running
end.
+%% For some unfathomable reason there is no "nice" way to stop
+%% a supervisor. The "normal" way to do it is:
+%% 1) exit(Pid, kill) (kaboom)
+%% 2) If the caller is the *parent*: exit(Pid, shutdown)
+%% So, here we do it the really ugly way...but since this function is
+%% intended for testing (mostly)...
+stop(Pid, Timeout) when (Timeout =:= 0) ->
+ ?d("stop -> Pid: ~p", [Pid]),
+ sys:terminate(Pid, shutdown),
+ ?d("stop -> stopped", []),
+ ok;
+stop(Pid, Timeout) ->
+ ?d("stop -> Pid: ~p", [Pid]),
+ MRef = erlang:monitor(process, Pid),
+ sys:terminate(Pid, shutdown),
+ receive
+ {'DOWN', MRef, process, Pid, _} ->
+ ?d("stop -> stopped", []),
+ ok
+ after Timeout ->
+ ?d("stop -> timeout", []),
+ erlang:demonitor(MRef, [flush]),
+ {error, timeout}
+ end.
+
+
%%%-------------------------------------------------------------------
%%% Callback functions from supervisor
%%%-------------------------------------------------------------------
diff --git a/lib/snmp/src/misc/snmp_conf.erl b/lib/snmp/src/misc/snmp_conf.erl
index 20b7af0373..bec9d4d9d9 100644
--- a/lib/snmp/src/misc/snmp_conf.erl
+++ b/lib/snmp/src/misc/snmp_conf.erl
@@ -265,7 +265,8 @@ open_file(File) ->
end.
do_read(Io, Prompt, StartLine) ->
- case io:request(Io, {get_until,Prompt,erl_scan,tokens,[StartLine]}) of
+ Enc = latin1,
+ case io:request(Io, {get_until,Enc,Prompt,erl_scan,tokens,[StartLine]}) of
{ok, Toks, EndLine} ->
case erl_parse:parse_term(Toks) of
{ok, Term} ->
diff --git a/lib/snmp/src/misc/snmp_config.erl b/lib/snmp/src/misc/snmp_config.erl
index 3104f2a096..5aab9a74e0 100644
--- a/lib/snmp/src/misc/snmp_config.erl
+++ b/lib/snmp/src/misc/snmp_config.erl
@@ -2737,7 +2737,8 @@ read_lines(Fd, Acc, StartLine) ->
end.
read_and_parse_term(Fd, StartLine) ->
- case io:request(Fd, {get_until, "", erl_scan, tokens, [StartLine]}) of
+ Enc = latin1,
+ case io:request(Fd, {get_until, Enc, "", erl_scan, tokens, [StartLine]}) of
{ok, Tokens, EndLine} ->
case erl_parse:parse_term(Tokens) of
{ok, Term} ->
diff --git a/lib/snmp/test/snmp_agent_test.erl b/lib/snmp/test/snmp_agent_test.erl
index a45cfa9e98..f6416ee2f9 100644
--- a/lib/snmp/test/snmp_agent_test.erl
+++ b/lib/snmp/test/snmp_agent_test.erl
@@ -3550,7 +3550,8 @@ table_test() ->
?line ?expect1([{NewKeyc5, ?destroy}]),
s([{NewKeyc3, 3}]),
?line ?expect3(?v1_2(noSuchName, inconsistentName), 1, [{NewKeyc3, 3}]),
- otp_1128_test().
+ otp_1128_test(),
+ ok.
%% Req. system group
simple_standard_test() ->
@@ -5030,7 +5031,8 @@ snmpv2_mib_2(Config) when is_list(Config) ->
"then disable auth traps",[]),
try_test(snmpv2_mib_test_finish, [], [{community, "bad community"}]),
- ?LOG("snmpv2_mib_2 -> done",[]).
+ ?LOG("snmpv2_mib_2 -> done", []),
+ ok.
standard_mibs3_cases() ->
@@ -5126,7 +5128,8 @@ snmpv2_mib_a() ->
?line ?expect3(inconsistentValue, 2,
[{[sysLocation, 0], "val3"},
{[snmpSetSerialNo,0], SetSerial}]),
- ?line ["val2"] = get_req(5, [[sysLocation,0]]).
+ ?line ["val2"] = get_req(5, [[sysLocation,0]]),
+ ok.
%%-----------------------------------------------------------------
@@ -5149,6 +5152,7 @@ snmp_community_mib_test() ->
?INF("NOT YET IMPLEMENTED", []),
nyi.
+
%%-----------------------------------------------------------------
%% o Test engine boots / time
%%-----------------------------------------------------------------
@@ -5277,7 +5281,8 @@ snmp_mpd_mib_b() ->
snmp_mpd_mib_c(UnknownPDUHs) ->
?line [UnknownPDUHs2] = get_req(1, [[snmpUnknownPDUHandlers, 0]]),
- ?line UnknownPDUHs2 = UnknownPDUHs + 1.
+ ?line UnknownPDUHs2 = UnknownPDUHs + 1,
+ ok.
snmp_target_mib(suite) -> [];
@@ -5314,6 +5319,7 @@ snmp_notification_mib_test() ->
?INF("NOT YET IMPLEMENTED", []),
nyi.
+
%%-----------------------------------------------------------------
%% o add/delete views and try them
%% o try boundaries
@@ -5661,7 +5667,8 @@ usm_key_change3(OldShaKey, OldDesKey, ShaKey, DesKey) ->
Vbs3 = [{[usmUserAuthKeyChange, NewRowIndex], ShaKeyChange},
{[usmUserPrivKeyChange, NewRowIndex], DesKeyChange}],
s(Vbs3),
- ?line ?expect1(Vbs3).
+ ?line ?expect1(Vbs3),
+ ok.
usm_read() ->
NewRowIndex = [11,"agentEngine", 7, "newUser"],
@@ -5779,7 +5786,8 @@ loop_mib_1(Config) when is_list(Config) ->
?line unload_master("SNMP-VIEW-BASED-ACM-MIB"),
%% snmpa:verbosity(master_agent,log),
%% snmpa:verbosity(mib_server,silence),
- ?LOG("loop_mib_1 -> done",[]).
+ ?LOG("loop_mib_1 -> done",[]),
+ ok.
loop_mib_2(suite) -> [];
@@ -5808,7 +5816,8 @@ loop_mib_2(Config) when is_list(Config) ->
?line unload_master("SNMP-NOTIFICATION-MIB"),
?line unload_master("SNMP-FRAMEWORK-MIB"),
?line unload_master("SNMP-VIEW-BASED-ACM-MIB"),
- ?LOG("loop_mib_2 -> done",[]).
+ ?LOG("loop_mib_2 -> done",[]),
+ ok.
loop_mib_3(suite) -> [];
@@ -5833,7 +5842,8 @@ loop_mib_3(Config) when is_list(Config) ->
?line unload_master("SNMP-NOTIFICATION-MIB"),
?line unload_master("SNMP-VIEW-BASED-ACM-MIB"),
?line unload_master("SNMP-USER-BASED-SM-MIB"),
- ?LOG("loop_mib_3 -> done",[]).
+ ?LOG("loop_mib_3 -> done",[]),
+ ok.
%% Req. As many mibs all possible
@@ -6043,7 +6053,8 @@ otp_1128(Config) when is_list(Config) ->
?line load_master("OLD-SNMPEA-MIB"),
?line init_old(),
try_test(otp_1128_test),
- ?line unload_master("OLD-SNMPEA-MIB").
+ ?line unload_master("OLD-SNMPEA-MIB"),
+ ok.
otp_1128_2(X) -> ?P(otp_1128_2), otp_1128(X).
@@ -6065,7 +6076,8 @@ otp_1128_test() ->
g([NewKeyc5]),
?line ?expect1([{NewKeyc5, ?active}]),
s([{NewKeyc5, ?destroy}]),
- ?line ?expect1([{NewKeyc5, ?destroy}]).
+ ?line ?expect1([{NewKeyc5, ?destroy}]),
+ ok.
%%-----------------------------------------------------------------
@@ -6078,7 +6090,8 @@ otp_1129(Config) when is_list(Config) ->
init_case(Config),
?line load_master("Klas3"),
try_test(otp_1129_i, [node()]),
- ?line unload_master("Klas3").
+ ?line unload_master("Klas3"),
+ ok.
otp_1129_2(X) -> ?P(otp_1129_2), otp_1129(X).
@@ -6149,7 +6162,8 @@ otp_1131_test() ->
io:format("Testing bug reported in ticket OTP-1131...~n"),
s([{[friendsEntry, [2, 3, 1]], s, "kompis3"},
{[friendsEntry, [3, 3, 1]], i, ?createAndGo}]),
- ?line ?expect3(?v1_2(noSuchName, noCreation), 2, any).
+ ?line ?expect3(?v1_2(noSuchName, noCreation), 2, any),
+ ok.
%%-----------------------------------------------------------------
@@ -6170,7 +6184,8 @@ otp_1162_3(X) -> ?P(otp_1162_3), otp_1162(X).
otp_1162_test() ->
s([{[sa, [2,0]], 6}]), % wrongValue (i is_set_ok)
- ?line ?expect3(?v1_2(badValue, wrongValue), 1, any).
+ ?line ?expect3(?v1_2(badValue, wrongValue), 1, any),
+ ok.
%%-----------------------------------------------------------------
@@ -6185,7 +6200,8 @@ otp_1222(Config) when is_list(Config) ->
?line load_master("Klas4"),
try_test(otp_1222_test),
?line unload_master("Klas3"),
- ?line unload_master("Klas4").
+ ?line unload_master("Klas4"),
+ ok.
otp_1222_2(X) -> ?P(otp_1222_2), otp_1222(X).
@@ -6196,7 +6212,8 @@ otp_1222_test() ->
s([{[fStatus4,1], 4}, {[fName4,1], 1}]),
?line ?expect3(genErr, 0, any),
s([{[fStatus4,2], 4}, {[fName4,2], 1}]),
- ?line ?expect3(genErr, 0, any).
+ ?line ?expect3(genErr, 0, any),
+ ok.
%%-----------------------------------------------------------------
@@ -6210,7 +6227,8 @@ otp_1298(Config) when is_list(Config) ->
?line load_master("Klas2"),
try_test(otp_1298_test),
- ?line unload_master("Klas2").
+ ?line unload_master("Klas2"),
+ ok.
otp_1298_2(X) -> ?P(otp_1298_2), otp_1298(X).
@@ -6219,7 +6237,8 @@ otp_1298_3(X) -> ?P(otp_1298_3), otp_1298(X).
otp_1298_test() ->
io:format("Testing bug reported in ticket OTP-1298...~n"),
s([{[fint,0], -1}]),
- ?line ?expect1([{[fint,0], -1}]).
+ ?line ?expect1([{[fint,0], -1}]),
+ ok.
%%-----------------------------------------------------------------
@@ -6233,7 +6252,8 @@ otp_1331(Config) when is_list(Config) ->
?line load_master("OLD-SNMPEA-MIB"),
?line init_old(),
try_test(otp_1331_test),
- ?line unload_master("OLD-SNMPEA-MIB").
+ ?line unload_master("OLD-SNMPEA-MIB"),
+ ok.
otp_1331_2(X) -> ?P(otp_1331_2), otp_1331(X).
@@ -6242,7 +6262,8 @@ otp_1331_3(X) -> ?P(otp_1331_3), otp_1331(X).
otp_1331_test() ->
NewKeyc5 = [intCommunityStatus,[127,32,0,0],is("test")],
s([{NewKeyc5, ?destroy}]),
- ?line ?expect1([{NewKeyc5, ?destroy}]).
+ ?line ?expect1([{NewKeyc5, ?destroy}]),
+ ok.
%%-----------------------------------------------------------------
@@ -6280,7 +6301,8 @@ otp_1342(Config) when is_list(Config) ->
init_case(Config),
?line load_master("Klas4"),
try_test(otp_1342_test),
- ?line unload_master("Klas4").
+ ?line unload_master("Klas4"),
+ ok.
otp_1342_2(X) -> ?P(otp_1342_2), otp_1342(X).
@@ -6290,7 +6312,8 @@ otp_1342_test() ->
s([{[fIndex5, 1], i, 1},
{[fName5, 1], i, 3},
{[fStatus5, 1], i, ?createAndGo}]),
- ?line ?expect3(?v1_2(noSuchName, noCreation), 3, any).
+ ?line ?expect3(?v1_2(noSuchName, noCreation), 3, any),
+ ok.
%%-----------------------------------------------------------------
@@ -6306,7 +6329,8 @@ otp_1366(Config) when is_list(Config) ->
?line load_master("OLD-SNMPEA-MIB"),
?line init_old(),
try_test(otp_1366_test),
- ?line unload_master("OLD-SNMPEA-MIB").
+ ?line unload_master("OLD-SNMPEA-MIB"),
+ ok.
otp_1366_2(X) -> ?P(otp_1366_2), otp_1366(X).
@@ -6404,7 +6428,8 @@ otp_2979_3(X) -> ?P(otp_2979_3), otp_2979(X).
otp_2979_test() ->
gn([[sparseDescr], [sparseStatus]]),
?line ?expect1([{[sparseStr,0], "slut"},
- {[sparseStr,0], "slut"}]).
+ {[sparseStr,0], "slut"}]),
+ ok.
%%-----------------------------------------------------------------
diff --git a/lib/snmp/test/snmp_agent_test_lib.erl b/lib/snmp/test/snmp_agent_test_lib.erl
index 615d6774b9..c0da47dc4c 100644
--- a/lib/snmp/test/snmp_agent_test_lib.erl
+++ b/lib/snmp/test/snmp_agent_test_lib.erl
@@ -423,7 +423,18 @@ tc_wait(From, Env, M, F, A) ->
"~n ~p"
"~n", [Res]),
From ! {tc_runner_done, self(), Res, get(test_server_loc)},
- exit(Res).
+ %% The point of this is that in some cases we have seen that the
+ %% exit signal having been "passed on" to the CT, which consider any
+ %% exit a fail (even if its {'EXIT', ok}).
+ %% So, just to be on the safe side, convert an 'ok' to a 'normal'.
+ case Res of
+ ok ->
+ exit(normal);
+ {ok, _} ->
+ exit(normal);
+ _ ->
+ exit(Res)
+ end.
tc_run(Mod, Func, Args, Opts) ->
?PRINT2("tc_run -> entry with"
diff --git a/lib/snmp/test/snmp_manager_test.erl b/lib/snmp/test/snmp_manager_test.erl
index 7cd3eae0c7..e2356bd70a 100644
--- a/lib/snmp/test/snmp_manager_test.erl
+++ b/lib/snmp/test/snmp_manager_test.erl
@@ -62,41 +62,32 @@
register_user1/1,
- register_agent1/1,
+ register_agent_old/1,
register_agent2/1,
register_agent3/1,
info/1,
- simple_sync_get1/1,
simple_sync_get2/1,
simple_sync_get3/1,
- simple_async_get1/1,
simple_async_get2/1,
simple_async_get3/1,
- simple_sync_get_next1/1,
simple_sync_get_next2/1,
simple_sync_get_next3/1,
- simple_async_get_next1/1,
simple_async_get_next2/1,
simple_async_get_next3/1,
- simple_sync_set1/1,
simple_sync_set2/1,
simple_sync_set3/1,
- simple_async_set1/1,
simple_async_set2/1,
simple_async_set3/1,
- simple_sync_get_bulk1/1,
simple_sync_get_bulk2/1,
simple_sync_get_bulk3/1,
- simple_async_get_bulk1/1,
simple_async_get_bulk2/1,
simple_async_get_bulk3/1,
- misc_async1/1,
misc_async2/1,
discovery/1,
@@ -163,6 +154,10 @@ init_per_suite(Config0) when is_list(Config0) ->
%% until we have a more important reason to fix this.
case snmp_test_lib:crypto_start() of
ok ->
+
+ snmp_test_global_sys_monitor:start(),
+ snmp_test_sys_monitor:start(), % We need one on this node also
+
Config1 = snmp_test_lib:init_suite_top_dir(?MODULE, Config0),
Config2 = snmp_test_lib:fix_data_dir(Config1),
%% Mib-dirs
@@ -181,36 +176,31 @@ end_per_suite(Config) when is_list(Config) ->
?DBG("end_per_suite -> entry with"
"~n Config: ~p", [Config]),
+ snmp_test_sys_monitor:stop(),
+ snmp_test_global_sys_monitor:stop(),
+
Config.
init_per_testcase(Case, Config) when is_list(Config) ->
- io:format(user, "~n~n*** INIT ~w:~w ***~n~n", [?MODULE, Case]),
p(Case, "init_per_testcase begin when"
"~n Nodes: ~p~n~n", [erlang:nodes()]),
%% This version of the API, based on Addr and Port, has been deprecated
DeprecatedApiCases =
[
- simple_sync_get1,
- simple_async_get1,
- simple_sync_get_next1,
- simple_async_get_next1,
- simple_sync_set1,
- simple_async_set1,
- simple_sync_get_bulk1,
- simple_async_get_bulk1,
- misc_async1
],
Result =
case lists:member(Case, DeprecatedApiCases) of
true ->
- {skip, api_no_longer_supported};
+ {skip, "API no longer supported"};
false ->
try init_per_testcase2(Case, Config)
catch
- C:{skip, _} = E:_ when ((C =:= throw) orelse (C =:= exit)) ->
+ C:{skip, _} = E:_ when ((C =:= throw) orelse
+ (C =:= exit)) ->
E;
- C:E:_ when ((C =:= throw) orelse (C =:= exit)) ->
+ C:E:_ when ((C =:= throw) orelse
+ (C =:= exit)) ->
{skip, {catched, C, E}}
end
end,
@@ -479,7 +469,7 @@ groups() ->
},
{agent_tests, [],
[
- register_agent1,
+ register_agent_old,
register_agent2,
register_agent3
]
@@ -504,47 +494,38 @@ groups() ->
},
{get_tests, [],
[
- simple_sync_get1,
simple_sync_get2,
simple_sync_get3,
- simple_async_get1,
simple_async_get2,
simple_async_get3
]
},
{get_next_tests, [],
[
- simple_sync_get_next1,
simple_sync_get_next2,
simple_sync_get_next3,
- simple_async_get_next1,
simple_async_get_next2,
simple_async_get_next3
]
},
{set_tests, [],
[
- simple_sync_set1,
simple_sync_set2,
simple_sync_set3,
- simple_async_set1,
simple_async_set2,
simple_async_set3
]
},
{bulk_tests, [],
[
- simple_sync_get_bulk1,
simple_sync_get_bulk2,
simple_sync_get_bulk3,
- simple_async_get_bulk1,
simple_async_get_bulk2,
simple_async_get_bulk3
]
},
{misc_request_tests, [],
[
- misc_async1,
misc_async2
]
},
@@ -595,7 +576,7 @@ groups() ->
ipv6_tests() ->
[
- register_agent1,
+ register_agent_old,
simple_sync_get_next3,
simple_async_get2,
simple_sync_get3,
@@ -671,11 +652,11 @@ end_per_group(_GroupName, Config) ->
simple_start_and_stop1(suite) -> [];
simple_start_and_stop1(Config) when is_list(Config) ->
- %% ?SKIP(not_yet_implemented),
- process_flag(trap_exit, true),
- put(tname,ssas1),
- p("starting with Config: ~n~p", [Config]),
+ ?TC_TRY(simple_start_and_stop1,
+ fun() -> do_simple_start_and_stop1(Config) end).
+do_simple_start_and_stop1(Config) ->
+ p("starting with Config: ~n~p", [Config]),
ConfDir = ?config(manager_conf_dir, Config),
DbDir = ?config(manager_db_dir, Config),
@@ -696,7 +677,6 @@ simple_start_and_stop1(Config) when is_list(Config) ->
?SLEEP(1000),
- p("end"),
ok.
@@ -704,9 +684,10 @@ simple_start_and_stop1(Config) when is_list(Config) ->
simple_start_and_stop2(suite) -> [];
simple_start_and_stop2(Config) when is_list(Config) ->
- %% ?SKIP(not_yet_implemented),
- process_flag(trap_exit, true),
- put(tname,ssas2),
+ ?TC_TRY(simple_start_and_stop2,
+ fun() -> do_simple_start_and_stop2(Config) end).
+
+do_simple_start_and_stop2(Config) ->
p("starting with Config: ~p~n", [Config]),
ManagerNode = start_manager_node(),
@@ -744,7 +725,6 @@ simple_start_and_stop2(Config) when is_list(Config) ->
?SLEEP(1000),
- p("end"),
ok.
@@ -752,8 +732,10 @@ simple_start_and_stop2(Config) when is_list(Config) ->
simple_start_and_monitor_crash1(suite) -> [];
simple_start_and_monitor_crash1(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- put(tname,ssamc1),
+ ?TC_TRY(simple_start_and_monitor_crash1,
+ fun() -> do_simple_start_and_monitor_crash1(Config) end).
+
+do_simple_start_and_monitor_crash1(Config) ->
p("starting with Config: ~n~p", [Config]),
ConfDir = ?config(manager_conf_dir, Config),
@@ -797,7 +779,6 @@ simple_start_and_monitor_crash1(Config) when is_list(Config) ->
?FAIL(timeout)
end,
- p("end"),
ok.
@@ -805,8 +786,10 @@ simple_start_and_monitor_crash1(Config) when is_list(Config) ->
simple_start_and_monitor_crash2(suite) -> [];
simple_start_and_monitor_crash2(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- put(tname,ssamc2),
+ ?TC_TRY(simple_start_and_monitor_crash2,
+ fun() -> do_simple_start_and_monitor_crash2(Config) end).
+
+do_simple_start_and_monitor_crash2(Config) ->
p("starting with Config: ~n~p", [Config]),
ConfDir = ?config(manager_conf_dir, Config),
@@ -851,7 +834,6 @@ simple_start_and_monitor_crash2(Config) when is_list(Config) ->
?FAIL(timeout)
end,
- p("end"),
ok.
@@ -897,8 +879,10 @@ simulate_crash(NumKills, _) ->
notify_started01(suite) -> [];
notify_started01(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- put(tname,ns01),
+ ?TC_TRY(notify_started01,
+ fun() -> do_notify_started01(Config) end).
+
+do_notify_started01(Config) ->
p("starting with Config: ~n~p", [Config]),
ConfDir = ?config(manager_conf_dir, Config),
@@ -985,37 +969,34 @@ snmpm_starter(Opts, To) ->
notify_started02(suite) -> [];
notify_started02(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- put(tname,ns02),
+ ?TC_TRY(notify_started02,
+ fun() -> notify_started02_cond(Config) end,
+ fun() -> do_notify_started02(Config) end).
- %% <CONDITIONAL-SKIP>
- %% The point of this is to catch machines running
- %% SLES9 (2.6.5)
+notify_started02_cond(Config) ->
LinuxVersionVerify =
fun() ->
case os:cmd("uname -m") of
"i686" ++ _ ->
-%% io:format("found an i686 machine, "
-%% "now check version~n", []),
case os:version() of
{2, 6, Rev} when Rev >= 16 ->
- true;
+ false;
{2, Min, _} when Min > 6 ->
- true;
+ false;
{Maj, _, _} when Maj > 2 ->
- true;
+ false;
_ ->
- false
+ true
end;
_ ->
- true
+ false
end
end,
Skippable = [{unix, [{linux, LinuxVersionVerify}]}],
Condition = fun() -> ?OS_BASED_SKIP(Skippable) end,
- ?NON_PC_TC_MAYBE_SKIP(Config, Condition),
- %% </CONDITIONAL-SKIP>
-
+ ?NON_PC_TC_MAYBE_SKIP(Config, Condition).
+
+do_notify_started02(Config) ->
p("starting with Config: ~n~p", [Config]),
ConfDir = ?config(manager_conf_dir, Config),
@@ -1023,10 +1004,10 @@ notify_started02(Config) when is_list(Config) ->
write_manager_conf(ConfDir),
- Opts = [{server, [{verbosity, log}]},
- {net_if, [{verbosity, silence}]},
+ Opts = [{server, [{verbosity, log}]},
+ {net_if, [{verbosity, silence}]},
{note_store, [{verbosity, silence}]},
- {config, [{verbosity, log}, {dir, ConfDir}, {db_dir, DbDir}]}],
+ {config, [{verbosity, debug}, {dir, ConfDir}, {db_dir, DbDir}]}],
p("start snmpm client process"),
NumIterations = 5,
@@ -1056,8 +1037,14 @@ notify_started02(Config) when is_list(Config) ->
p("await snmpm client process exit (max ~p+10000 msec)", [ApproxStartTime]),
receive
+ %% We take this opportunity to check if we got a skip from
+ %% the ctrl process.
+ {'EXIT', Pid2, {skip, SkipReason1}} ->
+ ?SKIP(SkipReason1);
{'EXIT', Pid1, normal} ->
ok;
+ {'EXIT', Pid1, {suite_failed, Reason1}} ->
+ ?FAIL({client, Reason1});
{'EXIT', Pid1, Reason1} ->
?FAIL({client, Reason1})
after ApproxStartTime + 10000 ->
@@ -1070,6 +1057,9 @@ notify_started02(Config) when is_list(Config) ->
receive
{'EXIT', Pid2, normal} ->
ok;
+ {'EXIT', Pid2, {skip, SkipReason2}} ->
+ %% In case of a race
+ ?SKIP(SkipReason2);
{'EXIT', Pid2, Reason2} ->
?FAIL({ctrl, Reason2})
after 5000 ->
@@ -1094,7 +1084,7 @@ ns02_client_await_approx_runtime(Pid) ->
"~n ~p", [Pid, Reason]),
{error, Reason}
- after 15000 ->
+ after 30000 ->
%% Either something is *really* wrong or this machine
%% is dog slow. Either way, this is a skip-reason...
{skip, approx_runtime_timeout}
@@ -1159,6 +1149,12 @@ ns02_ctrl(Opts, N) ->
p("starting"),
ns02_ctrl_loop(Opts, N).
+
+%% We have seen that some times it takes unreasonably long time to
+%% start the manager (it got "stuck" in snmpm_config). But since
+%% we did not have enough verbosity, we do not know how far it got.
+%% So, we try to monitor each start attempt. We allow 5 sec (just
+%% to give slow boxes a chance).
ns02_ctrl_loop(_Opts, 0) ->
p("done"),
exit(normal);
@@ -1166,19 +1162,45 @@ ns02_ctrl_loop(Opts, N) ->
p("entry when N: ~p", [N]),
?SLEEP(2000),
p("start manager"),
- snmpm:start(Opts),
+ TS1 = erlang:system_time(millisecond),
+ {StarterPid, StarterMRef} =
+ erlang:spawn_monitor(fun() -> exit(snmpm:start(Opts)) end),
+ receive
+ {'DOWN', StarterMRef, process, StarterPid, ok} ->
+ TS2 = erlang:system_time(millisecond),
+ p("manager started: ~w ms", [TS2-TS1]),
+ ok
+ after 5000 ->
+ p("manager (~p) start timeout - kill", [StarterPid]),
+ exit(StarterPid, kill),
+ exit({skip, start_timeout})
+ end,
?SLEEP(2000),
p("stop manager"),
- snmpm:stop(),
+ ?SLEEP(100), % Give the verbosity to take effect...
+ TS3 = erlang:system_time(millisecond),
+ case snmpm:stop(5000) of
+ ok ->
+ TS4 = erlang:system_time(millisecond),
+ p("manager stopped: ~p ms", [TS4-TS3]),
+ ok;
+ {error, timeout} ->
+ p("manager stop timeout - kill (cleanup) and skip"),
+ exit(whereis(snmpm_supervisor), kill),
+ exit({skip, stop_timeout})
+ end,
ns02_ctrl_loop(Opts, N-1).
+
%%======================================================================
info(suite) -> [];
info(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- put(tname,info),
+ ?TC_TRY(info,
+ fun() -> do_info(Config) end).
+
+do_info(Config) ->
p("starting with Config: ~n~p", [Config]),
ConfDir = ?config(manager_conf_dir, Config),
@@ -1206,7 +1228,6 @@ info(Config) when is_list(Config) ->
?SLEEP(1000),
- p("end"),
ok.
verify_info(Info) when is_list(Info) ->
@@ -1246,9 +1267,10 @@ verify_info([{Key, SubKeys}|Keys], Info) ->
register_user1(suite) -> [];
register_user1(Config) when is_list(Config) ->
- %% ?SKIP(not_yet_implemented).
- process_flag(trap_exit, true),
- put(tname,ru1),
+ ?TC_TRY(register_user1,
+ fun() -> do_register_user1(Config) end).
+
+do_register_user1(Config) ->
p("starting with Config: ~p~n", [Config]),
ManagerNode = start_manager_node(),
@@ -1322,7 +1344,6 @@ register_user1(Config) when is_list(Config) ->
?SLEEP(1000),
- p("end"),
ok.
verify_users([], []) ->
@@ -1340,14 +1361,15 @@ verify_users(ActualUsers0, [User|RegUsers]) ->
%%======================================================================
-register_agent1(doc) ->
+register_agent_old(doc) ->
["Test registration of agents with the OLD interface functions"];
-register_agent1(suite) ->
+register_agent_old(suite) ->
[];
-register_agent1(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- put(tname,ra1),
-
+register_agent_old(Config) when is_list(Config) ->
+ ?TC_TRY(register_agent_old,
+ fun() -> do_register_agent_old(Config) end).
+
+do_register_agent_old(Config) ->
p("starting with Config: ~p~n", [Config]),
ManagerNode = start_manager_node(),
@@ -1462,7 +1484,6 @@ register_agent1(Config) when is_list(Config) ->
?SLEEP(1000),
- p("end"),
ok.
@@ -1473,8 +1494,10 @@ register_agent2(doc) ->
register_agent2(suite) ->
[];
register_agent2(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- put(tname, ra2),
+ ?TC_TRY(register_agent2,
+ fun() -> do_register_agent2(Config) end).
+
+do_register_agent2(Config) ->
p("starting with Config: ~p~n", [Config]),
ManagerNode = start_manager_node(),
@@ -1607,7 +1630,6 @@ register_agent2(Config) when is_list(Config) ->
?SLEEP(1000),
- p("end"),
ok.
@@ -1619,8 +1641,10 @@ register_agent3(doc) ->
register_agent3(suite) ->
[];
register_agent3(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- put(tname, ra3),
+ ?TC_TRY(register_agent3,
+ fun() -> do_register_agent3(Config) end).
+
+do_register_agent3(Config) ->
p("starting with Config: ~p~n", [Config]),
ManagerNode = start_manager_node(),
@@ -1757,89 +1781,27 @@ register_agent3(Config) when is_list(Config) ->
?SLEEP(1000),
- p("end"),
ok.
%%======================================================================
-simple_sync_get1(doc) -> ["Simple sync get-request - Old style (Addr & Port)"];
-simple_sync_get1(suite) -> [];
-simple_sync_get1(Config) when is_list(Config) ->
- ?SKIP(api_no_longer_supported),
-
- process_flag(trap_exit, true),
- put(tname, ssg1),
- p("starting with Config: ~p~n", [Config]),
-
- Node = ?config(manager_node, Config),
- Addr = ?config(manager_agent_target_name, Config),
- Port = ?AGENT_PORT,
-
- p("issue get-request without loading the mib"),
- Oids1 = [?sysObjectID_instance, ?sysDescr_instance, ?sysUpTime_instance],
- ?line ok = do_simple_sync_get(Node, Addr, Port, Oids1),
-
- p("issue get-request after first loading the mibs"),
- ?line ok = mgr_user_load_mib(Node, std_mib()),
- Oids2 = [[sysObjectID, 0], [sysDescr, 0], [sysUpTime, 0]],
- ?line ok = do_simple_sync_get(Node, Addr, Port, Oids2),
-
- p("Display log"),
- display_log(Config),
-
- p("done"),
- ok.
-
-do_simple_sync_get(Node, Addr, Port, Oids) ->
- ?line {ok, Reply, _Rem} = mgr_user_sync_get(Node, Addr, Port, Oids),
-
- ?DBG("~n Reply: ~p"
- "~n Rem: ~w", [Reply, _Rem]),
-
- %% verify that the operation actually worked:
- %% The order should be the same, so no need to seach
- ?line ok = case Reply of
- {noError, 0, [#varbind{oid = ?sysObjectID_instance,
- value = SysObjectID},
- #varbind{oid = ?sysDescr_instance,
- value = SysDescr},
- #varbind{oid = ?sysUpTime_instance,
- value = SysUpTime}]} ->
- p("expected result from get: "
- "~n SysObjectID: ~p"
- "~n SysDescr: ~s"
- "~n SysUpTime: ~w",
- [SysObjectID, SysDescr, SysUpTime]),
- ok;
- {noError, 0, Vbs} ->
- p("unexpected varbinds: ~n~p", [Vbs]),
- {error, {unexpected_vbs, Vbs}};
- Else ->
- p("unexpected reply: ~n~p", [Else]),
- {error, {unexpected_response, Else}}
- end,
- ok.
-
-
-%%======================================================================
-
simple_sync_get2(doc) ->
["Simple sync get-request - Version 2 API (TargetName)"];
simple_sync_get2(suite) -> [];
simple_sync_get2(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- put(tname, ssg2),
- do_simple_sync_get2(Config),
- display_log(Config),
- ok.
+ ?TC_TRY(simple_sync_get2,
+ fun() -> do_simple_sync_get2(Config) end).
do_simple_sync_get2(Config) ->
+ p("starting with Config: ~n~p", [Config]),
Get = fun(Node, TargetName, Oids) ->
mgr_user_sync_get(Node, TargetName, Oids)
end,
PostVerify = fun() -> ok end,
- do_simple_sync_get2(Config, Get, PostVerify).
+ do_simple_sync_get2(Config, Get, PostVerify),
+ display_log(Config),
+ ok.
do_simple_sync_get2(Config, Get, PostVerify) ->
p("starting with Config: ~p~n", [Config]),
@@ -1895,13 +1857,11 @@ simple_sync_get3(doc) ->
["Simple sync get-request - Version 3 API (TargetName and send-opts)"];
simple_sync_get3(suite) -> [];
simple_sync_get3(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- put(tname, ssg3),
- do_simple_sync_get3(Config),
- display_log(Config),
- ok.
+ ?TC_TRY(simple_sync_get3,
+ fun() -> do_simple_sync_get3(Config) end).
do_simple_sync_get3(Config) ->
+ p("starting with Config: ~n~p", [Config]),
Self = self(),
Msg = simple_sync_get3,
Fun = fun() -> Self ! Msg end,
@@ -1920,81 +1880,14 @@ do_simple_sync_get3(Config) ->
ok
end
end,
- do_simple_sync_get2(Config, Get, PostVerify).
-
-
-%%======================================================================
-
-simple_async_get1(doc) ->
- ["Simple (async) get-request - Old style (Addr & Port)"];
-simple_async_get1(suite) -> [];
-simple_async_get1(Config) when is_list(Config) ->
- ?SKIP(api_no_longer_supported),
-
- process_flag(trap_exit, true),
- put(tname, sag1),
- p("starting with Config: ~p~n", [Config]),
-
- MgrNode = ?config(manager_node, Config),
- AgentNode = ?config(agent_node, Config),
- Addr = ?config(ip, Config),
- Port = ?AGENT_PORT,
-
- ?line ok = mgr_user_load_mib(MgrNode, std_mib()),
- Test2Mib = test2_mib(Config),
- ?line ok = mgr_user_load_mib(MgrNode, Test2Mib),
- ?line ok = agent_load_mib(AgentNode, Test2Mib),
-
- Exec = fun(Data) ->
- async_g_exec1(MgrNode, Addr, Port, Data)
- end,
-
- Requests =
- [
- { 1,
- [?sysObjectID_instance],
- Exec,
- fun(X) -> sag_verify(X, [?sysObjectID_instance]) end },
- { 2,
- [?sysDescr_instance, ?sysUpTime_instance],
- Exec,
- fun(X) ->
- sag_verify(X, [?sysObjectID_instance,
- ?sysUpTime_instance])
- end },
- { 3,
- [[sysObjectID, 0], [sysDescr, 0], [sysUpTime, 0]],
- Exec,
- fun(X) ->
- sag_verify(X, [?sysObjectID_instance,
- ?sysDescr_instance,
- ?sysUpTime_instance])
- end },
- { 4,
- [?sysObjectID_instance,
- ?sysDescr_instance,
- ?sysUpTime_instance],
- Exec,
- fun(X) ->
- sag_verify(X, [?sysObjectID_instance,
- ?sysDescr_instance,
- ?sysUpTime_instance])
- end }
- ],
-
- p("manager info when starting test: ~n~p", [mgr_info(MgrNode)]),
- p("agent info when starting test: ~n~p", [agent_info(AgentNode)]),
+ do_simple_sync_get2(Config, Get, PostVerify),
+ display_log(Config),
+ ok.
- ?line ok = async_exec(Requests, []),
- p("manager info when ending test: ~n~p", [mgr_info(MgrNode)]),
- p("agent info when ending test: ~n~p", [agent_info(AgentNode)]),
- display_log(Config),
- ok.
-async_g_exec1(Node, Addr, Port, Oids) ->
- mgr_user_async_get(Node, Addr, Port, Oids).
+%%======================================================================
sag_verify({noError, 0, _Vbs}, any) ->
p("verified [any]"),
@@ -2033,8 +1926,10 @@ simple_async_get2(doc) ->
["Simple (async) get-request - Version 2 API (TargetName)"];
simple_async_get2(suite) -> [];
simple_async_get2(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- put(tname, sag2),
+ ?TC_TRY(simple_async_get2,
+ fun() -> do_simple_async_get2(Config) end).
+
+do_simple_async_get2(Config) ->
p("starting with Config: ~p~n", [Config]),
MgrNode = ?config(manager_node, Config),
AgentNode = ?config(agent_node, Config),
@@ -2114,8 +2009,10 @@ simple_async_get3(doc) ->
["Simple (async) get-request - Version 3 API (TargetName and send-opts)"];
simple_async_get3(suite) -> [];
simple_async_get3(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- put(tname, sag3),
+ ?TC_TRY(simple_async_get3,
+ fun() -> do_simple_async_get3(Config) end).
+
+do_simple_async_get3(Config) ->
p("starting with Config: ~p~n", [Config]),
MgrNode = ?config(manager_node, Config),
AgentNode = ?config(agent_node, Config),
@@ -2142,113 +2039,6 @@ async_g_exec3(Node, TargetName, Oids, SendOpts) ->
%%======================================================================
-simple_sync_get_next1(doc) -> ["Simple (sync) get_next-request - "
- "Old style (Addr & Port)"];
-simple_sync_get_next1(suite) -> [];
-simple_sync_get_next1(Config) when is_list(Config) ->
- ?SKIP(api_no_longer_supported),
-
- process_flag(trap_exit, true),
- put(tname, ssgn1),
- p("starting with Config: ~p~n", [Config]),
-
- MgrNode = ?config(manager_node, Config),
- AgentNode = ?config(agent_node, Config),
- Addr = ?config(ip, Config),
- Port = ?AGENT_PORT,
-
- %% -- 1 --
- Oids01 = [[1,3,7,1]],
- VF01 = fun(X) -> verify_ssgn_reply1(X, [{[1,3,7,1],endOfMibView}]) end,
- ?line ok = do_simple_get_next(1,
- MgrNode, Addr, Port, Oids01, VF01),
-
- ?line ok = mgr_user_load_mib(MgrNode, std_mib()),
-
- %% -- 2 --
- Oids02 = [[sysDescr], [1,3,7,1]],
- VF02 = fun(X) ->
- verify_ssgn_reply1(X, [?sysDescr_instance, endOfMibView])
- end,
- ?line ok = do_simple_get_next(2,
- MgrNode, Addr, Port, Oids02, VF02),
-
- Test2Mib = test2_mib(Config),
- ?line ok = mgr_user_load_mib(MgrNode, Test2Mib),
- ?line ok = agent_load_mib(AgentNode, Test2Mib),
-
- %% -- 3 --
- ?line {ok, [TCnt2|_]} = mgr_user_name_to_oid(MgrNode, tCnt2),
- Oids03 = [[TCnt2, 1]],
- VF03 = fun(X) ->
- verify_ssgn_reply1(X, [{fl([TCnt2,2]), 100}])
- end,
- ?line ok = do_simple_get_next(3,
- MgrNode, Addr, Port, Oids03, VF03),
-
- %% -- 4 --
- Oids04 = [[TCnt2, 2]],
- VF04 = fun(X) ->
- verify_ssgn_reply1(X, [{fl([TCnt2,2]), endOfMibView}])
- end,
- ?line ok = do_simple_get_next(4,
- MgrNode, Addr, Port, Oids04, VF04),
-
- %% -- 5 --
- ?line {ok, [TGenErr1|_]} = mgr_user_name_to_oid(MgrNode, tGenErr1),
- Oids05 = [TGenErr1],
- VF05 = fun(X) ->
- verify_ssgn_reply2(X, {genErr, 1, [TGenErr1]})
- end,
- ?line ok = do_simple_get_next(5,
- MgrNode, Addr, Port, Oids05, VF05),
-
- %% -- 6 --
- ?line {ok, [TGenErr2|_]} = mgr_user_name_to_oid(MgrNode, tGenErr2),
- Oids06 = [TGenErr2],
- VF06 = fun(X) ->
- verify_ssgn_reply2(X, {genErr, 1, [TGenErr2]})
- end,
- ?line ok = do_simple_get_next(6,
- MgrNode, Addr, Port, Oids06, VF06),
-
- %% -- 7 --
- ?line {ok, [TGenErr3|_]} = mgr_user_name_to_oid(MgrNode, tGenErr3),
- Oids07 = [[sysDescr], TGenErr3],
- VF07 = fun(X) ->
- verify_ssgn_reply2(X, {genErr, 2,
- [?sysDescr, TGenErr3]})
- end,
- ?line ok = do_simple_get_next(7,
- MgrNode, Addr, Port, Oids07, VF07),
-
- %% -- 8 --
- ?line {ok, [TTooBig|_]} = mgr_user_name_to_oid(MgrNode, tTooBig),
- Oids08 = [TTooBig],
- VF08 = fun(X) ->
- verify_ssgn_reply2(X, {tooBig, 0, []})
- end,
- ?line ok = do_simple_get_next(8,
- MgrNode, Addr, Port, Oids08, VF08),
-
- display_log(Config),
- ok.
-
-
-do_simple_get_next(N, Node, Addr, Port, Oids, Verify) ->
- p("issue get-next command ~w", [N]),
- case mgr_user_sync_get_next(Node, Addr, Port, Oids) of
- {ok, Reply, _Rem} ->
- ?DBG("get-next ok:"
- "~n Reply: ~p"
- "~n Rem: ~w", [Reply, _Rem]),
- Verify(Reply);
-
- Error ->
- {error, {unexpected_reply, Error}}
- end.
-
-
verify_ssgn_reply1({noError, 0, _Vbs}, any) ->
ok;
verify_ssgn_reply1({noError, 0, Vbs}, Expected) ->
@@ -2287,8 +2077,10 @@ simple_sync_get_next2(doc) ->
["Simple (sync) get_next-request - Version 2 API (TargetName)"];
simple_sync_get_next2(suite) -> [];
simple_sync_get_next2(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- put(tname, ssgn2),
+ ?TC_TRY(simple_sync_get_next2,
+ fun() -> do_simple_sync_get_next2(Config) end).
+
+do_simple_sync_get_next2(Config) ->
p("starting with Config: ~p~n", [Config]),
GetNext = fun(Node, TargetName, Oids) ->
@@ -2436,112 +2228,14 @@ simple_sync_get_next3(Config) when is_list(Config) ->
%%======================================================================
-simple_async_get_next1(doc) -> ["Simple (async) get_next-request - "
- "Old style (Addr & Port)"];
-simple_async_get_next1(suite) -> [];
-simple_async_get_next1(Config) when is_list(Config) ->
- ?SKIP(api_no_longer_supported),
-
- process_flag(trap_exit, true),
- put(tname, ssgn1),
- p("starting with Config: ~p~n", [Config]),
-
- MgrNode = ?config(manager_node, Config),
- AgentNode = ?config(agent_node, Config),
- Addr = ?config(ip, Config),
- Port = ?AGENT_PORT,
-
- ?line ok = mgr_user_load_mib(MgrNode, std_mib()),
- Test2Mib = test2_mib(Config),
- ?line ok = mgr_user_load_mib(MgrNode, Test2Mib),
- ?line ok = agent_load_mib(AgentNode, Test2Mib),
-
- Exec = fun(X) ->
- async_gn_exec1(MgrNode, Addr, Port, X)
- end,
-
- ?line {ok, [TCnt2|_]} = mgr_user_name_to_oid(MgrNode, tCnt2),
- ?line {ok, [TGenErr1|_]} = mgr_user_name_to_oid(MgrNode, tGenErr1),
- ?line {ok, [TGenErr2|_]} = mgr_user_name_to_oid(MgrNode, tGenErr2),
- ?line {ok, [TGenErr3|_]} = mgr_user_name_to_oid(MgrNode, tGenErr3),
- ?line {ok, [TTooBig|_]} = mgr_user_name_to_oid(MgrNode, tTooBig),
-
- Requests =
- [
- {1,
- [[1,3,7,1]],
- Exec,
- fun(X) ->
- verify_ssgn_reply1(X, [{[1,3,7,1], endOfMibView}])
- end},
- {2,
- [[sysDescr], [1,3,7,1]],
- Exec,
- fun(X) ->
- verify_ssgn_reply1(X, [?sysDescr_instance, endOfMibView])
- end},
- {3,
- [[TCnt2, 1]],
- Exec,
- fun(X) ->
- verify_ssgn_reply1(X, [{fl([TCnt2,2]), 100}])
- end},
- {4,
- [[TCnt2, 2]],
- Exec,
- fun(X) ->
- verify_ssgn_reply1(X, [{fl([TCnt2,2]), endOfMibView}])
- end},
- {5,
- [TGenErr1],
- Exec,
- fun(X) ->
- verify_ssgn_reply2(X, {genErr, 1, [TGenErr1]})
- end},
- {6,
- [TGenErr2],
- Exec,
- fun(X) ->
- verify_ssgn_reply2(X, {genErr, 1, [TGenErr2]})
- end},
- {7,
- [[sysDescr], TGenErr3],
- Exec,
- fun(X) ->
- verify_ssgn_reply2(X, {genErr, 2, [TGenErr3]})
- end},
- {8,
- [TTooBig],
- Exec,
- fun(X) ->
- verify_ssgn_reply2(X, {tooBig, 0, []})
- end}
- ],
-
- p("manager info when starting test: ~n~p", [mgr_info(MgrNode)]),
- p("agent info when starting test: ~n~p", [agent_info(AgentNode)]),
-
- ?line ok = async_exec(Requests, []),
-
- p("manager info when ending test: ~n~p", [mgr_info(MgrNode)]),
- p("agent info when ending test: ~n~p", [agent_info(AgentNode)]),
-
- display_log(Config),
- ok.
-
-
-async_gn_exec1(Node, Addr, Port, Oids) ->
- mgr_user_async_get_next(Node, Addr, Port, Oids).
-
-
-%%======================================================================
-
simple_async_get_next2(doc) ->
["Simple (async) get_next-request - Version 2 API (TargetName)"];
simple_async_get_next2(suite) -> [];
simple_async_get_next2(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- put(tname, ssgn2),
+ ?TC_TRY(simple_async_get_next2,
+ fun() -> do_simple_async_get_next2(Config) end).
+
+do_simple_async_get_next2(Config) ->
p("starting with Config: ~p~n", [Config]),
MgrNode = ?config(manager_node, Config),
@@ -2651,8 +2345,10 @@ simple_async_get_next3(doc) ->
"Version 3 API (TargetName with send-opts)"];
simple_async_get_next3(suite) -> [];
simple_async_get_next3(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- put(tname, ssgn2),
+ ?TC_TRY(simple_async_get_next3,
+ fun() -> do_simple_async_get_next3(Config) end).
+
+do_simple_async_get_next3(Config) ->
p("starting with Config: ~p~n", [Config]),
MgrNode = ?config(manager_node, Config),
@@ -2690,66 +2386,6 @@ async_gn_exec3(Node, TargetName, Oids, SendOpts) ->
%%======================================================================
-simple_sync_set1(doc) -> ["Simple (sync) set-request - "
- "Old style (Addr & Port)"];
-simple_sync_set1(suite) -> [];
-simple_sync_set1(Config) when is_list(Config) ->
- ?SKIP(api_no_longer_supported),
-
- process_flag(trap_exit, true),
- put(tname, sss1),
- p("starting with Config: ~p~n", [Config]),
-
- Node = ?config(manager_node, Config),
- Addr = ?config(ip, Config),
- Port = ?AGENT_PORT,
-
- p("issue set-request without loading the mib"),
- Val11 = "Arne Anka",
- Val12 = "Stockholm",
- VAVs1 = [
- {?sysName_instance, s, Val11},
- {?sysLocation_instance, s, Val12}
- ],
- ?line ok = do_simple_set1(Node, Addr, Port, VAVs1),
-
- p("issue set-request after first loading the mibs"),
- ?line ok = mgr_user_load_mib(Node, std_mib()),
- Val21 = "Sune Anka",
- Val22 = "Gothenburg",
- VAVs2 = [
- {[sysName, 0], Val21},
- {[sysLocation, 0], Val22}
- ],
- ?line ok = do_simple_set1(Node, Addr, Port, VAVs2),
-
- display_log(Config),
- ok.
-
-do_simple_set1(Node, Addr, Port, VAVs) ->
- [SysName, SysLoc] = value_of_vavs(VAVs),
- ?line {ok, Reply, _Rem} = mgr_user_sync_set(Node, Addr, Port, VAVs),
-
- ?DBG("~n Reply: ~p"
- "~n Rem: ~w", [Reply, _Rem]),
-
- %% verify that the operation actually worked:
- %% The order should be the same, so no need to seach
- %% The value we get should be exactly the same as we sent
- ?line ok = case Reply of
- {noError, 0, [#varbind{oid = ?sysName_instance,
- value = SysName},
- #varbind{oid = ?sysLocation_instance,
- value = SysLoc}]} ->
- ok;
- {noError, 0, Vbs} ->
- {error, {unexpected_vbs, Vbs}};
- Else ->
- p("unexpected reply: ~n~p", [Else]),
- {error, {unexpected_response, Else}}
- end,
- ok.
-
value_of_vavs(VAVs) ->
value_of_vavs(VAVs, []).
@@ -2767,8 +2403,10 @@ simple_sync_set2(doc) ->
["Simple (sync) set-request - Version 2 API (TargetName)"];
simple_sync_set2(suite) -> [];
simple_sync_set2(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- put(tname, sss2),
+ ?TC_TRY(simple_sync_set2,
+ fun() -> do_simple_sync_set2(Config) end).
+
+do_simple_sync_set2(Config) ->
p("starting with Config: ~p~n", [Config]),
Set = fun(Node, TargetName, VAVs) ->
@@ -2837,8 +2475,10 @@ simple_sync_set3(doc) ->
["Simple (sync) set-request - Version 3 API (TargetName with send-opts)"];
simple_sync_set3(suite) -> [];
simple_sync_set3(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- put(tname, sss3),
+ ?TC_TRY(simple_sync_set3,
+ fun() -> do_simple_sync_set3(Config) end).
+
+do_simple_sync_set3(Config) ->
p("starting with Config: ~p~n", [Config]),
Self = self(),
@@ -2862,69 +2502,6 @@ simple_sync_set3(Config) when is_list(Config) ->
%%======================================================================
-simple_async_set1(doc) -> ["Simple (async) set-request - "
- "Old style (Addr & Port)"];
-simple_async_set1(suite) -> [];
-simple_async_set1(Config) when is_list(Config) ->
- ?SKIP(api_no_longer_supported),
-
- process_flag(trap_exit, true),
- put(tname, sas1),
- p("starting with Config: ~p~n", [Config]),
-
- MgrNode = ?config(manager_node, Config),
- AgentNode = ?config(agent_node, Config),
- Addr = ?config(ip, Config),
- Port = ?AGENT_PORT,
-
- ?line ok = mgr_user_load_mib(MgrNode, std_mib()),
- Test2Mib = test2_mib(Config),
- ?line ok = mgr_user_load_mib(MgrNode, Test2Mib),
- ?line ok = agent_load_mib(AgentNode, Test2Mib),
-
- Exec = fun(X) ->
- async_s_exec1(MgrNode, Addr, Port, X)
- end,
-
- Requests =
- [
- {1,
- [{?sysName_instance, s, "Arne Anka"}],
- Exec,
- fun(X) ->
- sas_verify(X, [?sysName_instance])
- end},
- {2,
- [{?sysLocation_instance, s, "Stockholm"},
- {?sysName_instance, s, "Arne Anka"}],
- Exec,
- fun(X) ->
- sas_verify(X, [?sysLocation_instance, ?sysName_instance])
- end},
- {3,
- [{[sysName, 0], "Gothenburg"},
- {[sysLocation, 0], "Sune Anka"}],
- Exec,
- fun(X) ->
- sas_verify(X, [?sysName_instance, ?sysLocation_instance])
- end}
- ],
-
- p("manager info when starting test: ~n~p", [mgr_info(MgrNode)]),
- p("agent info when starting test: ~n~p", [agent_info(AgentNode)]),
-
- ?line ok = async_exec(Requests, []),
-
- p("manager info when ending test: ~n~p", [mgr_info(MgrNode)]),
- p("agent info when ending test: ~n~p", [agent_info(AgentNode)]),
-
- display_log(Config),
- ok.
-
-
-async_s_exec1(Node, Addr, Port, VAVs) ->
- mgr_user_async_set(Node, Addr, Port, VAVs).
-
sas_verify({noError, 0, _Vbs}, any) ->
p("verified [any]"),
ok;
@@ -2961,8 +2538,10 @@ simple_async_set2(doc) ->
["Simple (async) set-request - Version 2 API (TargetName)"];
simple_async_set2(suite) -> [];
simple_async_set2(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- put(tname, sas2),
+ ?TC_TRY(simple_async_set2,
+ fun() -> do_simple_async_set2(Config) end).
+
+do_simple_async_set2(Config) ->
p("starting with Config: ~p~n", [Config]),
MgrNode = ?config(manager_node, Config),
@@ -3034,8 +2613,10 @@ simple_async_set3(doc) ->
["Simple (async) set-request - Version 3 API (TargetName with send-opts)"];
simple_async_set3(suite) -> [];
simple_async_set3(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- put(tname, sas3),
+ ?TC_TRY(simple_async_set3,
+ fun() -> do_simple_async_set3(Config) end).
+
+do_simple_async_set3(Config) ->
p("starting with Config: ~p~n", [Config]),
MgrNode = ?config(manager_node, Config),
@@ -3074,145 +2655,9 @@ async_s_exec3(Node, TargetName, VAVs, SendOpts) ->
%%======================================================================
-simple_sync_get_bulk1(doc) -> ["Simple (sync) get_bulk-request - "
- "Old style (Addr & Port)"];
-simple_sync_get_bulk1(suite) -> [];
-simple_sync_get_bulk1(Config) when is_list(Config) ->
- ?SKIP(api_no_longer_supported),
-
- process_flag(trap_exit, true),
- put(tname, ssgb1),
- p("starting with Config: ~p~n", [Config]),
-
- MgrNode = ?config(manager_node, Config),
- AgentNode = ?config(agent_node, Config),
- Addr = ?config(ip, Config),
- Port = ?AGENT_PORT,
-
- %% -- 1 --
- ?line ok = do_simple_get_bulk1(1,
- MgrNode, Addr, Port, 1, 1, [],
- fun verify_ssgb_reply1/1),
-
- %% -- 2 --
- ?line ok = do_simple_get_bulk1(2,
- MgrNode, Addr, Port, -1, 1, [],
- fun verify_ssgb_reply1/1),
-
- %% -- 3 --
- ?line ok = do_simple_get_bulk1(3,
- MgrNode, Addr, Port, -1, -1, [],
- fun verify_ssgb_reply1/1),
-
- ?line ok = mgr_user_load_mib(MgrNode, std_mib()),
- %% -- 4 --
- VF04 = fun(X) ->
- verify_ssgb_reply2(X, [?sysDescr_instance, endOfMibView])
- end,
- ?line ok = do_simple_get_bulk1(4,
- MgrNode, Addr, Port,
- 2, 0, [[sysDescr],[1,3,7,1]], VF04),
-
- %% -- 5 --
- ?line ok = do_simple_get_bulk1(5,
- MgrNode, Addr, Port,
- 1, 2, [[sysDescr],[1,3,7,1]], VF04),
-
- %% -- 6 --
- VF06 = fun(X) ->
- verify_ssgb_reply2(X,
- [?sysDescr_instance, endOfMibView,
- ?sysObjectID_instance, endOfMibView])
- end,
- ?line ok = do_simple_get_bulk1(6,
- MgrNode, Addr, Port,
- 0, 2, [[sysDescr],[1,3,7,1]], VF06),
-
- %% -- 7 --
- VF07 = fun(X) ->
- verify_ssgb_reply2(X,
- [?sysDescr_instance, endOfMibView,
- ?sysDescr_instance, endOfMibView,
- ?sysObjectID_instance, endOfMibView])
- end,
- ?line ok = do_simple_get_bulk1(7,
- MgrNode, Addr, Port,
- 2, 2,
- [[sysDescr],[1,3,7,1],[sysDescr],[1,3,7,1]],
- VF07),
-
- Test2Mib = test2_mib(Config),
- ?line ok = mgr_user_load_mib(MgrNode, Test2Mib),
- ?line ok = agent_load_mib(AgentNode, Test2Mib),
-
- %% -- 8 --
- VF08 = fun(X) ->
- verify_ssgb_reply2(X,
- [?sysDescr_instance,
- ?sysDescr_instance])
- end,
- ?line ok = do_simple_get_bulk1(8,
- MgrNode, Addr, Port,
- 1, 2,
- [[sysDescr],[sysDescr],[tTooBig]],
- VF08),
-
- %% -- 9 --
- ?line ok = do_simple_get_bulk1(9,
- MgrNode, Addr, Port,
- 1, 12,
- [[tDescr2], [sysDescr]],
- fun verify_ssgb_reply1/1),
-
- %% -- 10 --
- VF10 = fun(X) ->
- verify_ssgb_reply3(X,
- [{?sysDescr, 'NULL'},
- {?sysObjectID, 'NULL'},
- {?tGenErr1, 'NULL'},
- {?sysDescr, 'NULL'}])
- end,
- ?line ok = do_simple_get_bulk1(10,
- MgrNode, Addr, Port,
- 2, 2,
- [[sysDescr],
- [sysObjectID],
- [tGenErr1],
- [sysDescr]],
- VF10),
-
- %% -- 11 --
- ?line {ok, [TCnt2|_]} = mgr_user_name_to_oid(MgrNode, tCnt2),
- p("TCnt2: ~p", [TCnt2]),
- VF11 = fun(X) ->
- verify_ssgb_reply2(X,
- [{fl([TCnt2,2]), 100},
- {fl([TCnt2,2]), endOfMibView}])
- end,
- ?line ok = do_simple_get_bulk1(11,
- MgrNode, Addr, Port,
- 0, 2,
- [[TCnt2, 1]], VF11),
-
- display_log(Config),
- ok.
-
fl(L) ->
lists:flatten(L).
-do_simple_get_bulk1(N, Node, Addr, Port, NonRep, MaxRep, Oids, Verify) ->
- p("issue get-bulk command ~w", [N]),
- case mgr_user_sync_get_bulk(Node, Addr, Port, NonRep, MaxRep, Oids) of
- {ok, Reply, _Rem} ->
- ?DBG("get-bulk ok:"
- "~n Reply: ~p"
- "~n Rem: ~w", [Reply, _Rem]),
- Verify(Reply);
-
- Error ->
- {error, {unexpected_reply, Error}}
- end.
-
verify_ssgb_reply1({noError, 0, []}) ->
ok;
verify_ssgb_reply1(X) ->
@@ -3245,15 +2690,16 @@ check_ssgb_vbs([#varbind{oid = Oid, value = Value}|R],
check_ssgb_vbs([R|_], [E|_]) ->
{error, {unexpected_vb, R, E}}.
-
%%======================================================================
simple_sync_get_bulk2(doc) ->
["Simple (sync) get_bulk-request - Version 2 API (TargetName)"];
simple_sync_get_bulk2(suite) -> [];
simple_sync_get_bulk2(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- put(tname, ssgb2),
+ ?TC_TRY(simple_sync_get_bulk2,
+ fun() -> do_simple_sync_get_bulk2(Config) end).
+
+do_simple_sync_get_bulk2(Config) ->
p("starting with Config: ~p~n", [Config]),
MgrNode = ?config(manager_node, Config),
@@ -3408,8 +2854,10 @@ simple_sync_get_bulk3(doc) ->
"Version 3 API (TargetName with send-opts)"];
simple_sync_get_bulk3(suite) -> [];
simple_sync_get_bulk3(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- put(tname, ssgb3),
+ ?TC_TRY(simple_sync_get_bulk3,
+ fun() -> do_simple_sync_get_bulk3(Config) end).
+
+do_simple_sync_get_bulk3(Config) ->
p("starting with Config: ~p~n", [Config]),
MgrNode = ?config(manager_node, Config),
@@ -3441,158 +2889,14 @@ simple_sync_get_bulk3(Config) when is_list(Config) ->
%%======================================================================
-simple_async_get_bulk1(doc) -> ["Simple (async) get_bulk-request - "
- "Old style (Addr & Port)"];
-simple_async_get_bulk1(suite) -> [];
-simple_async_get_bulk1(Config) when is_list(Config) ->
- ?SKIP(api_no_longer_supported),
-
- process_flag(trap_exit, true),
- put(tname, sagb1),
- p("starting with Config: ~p~n", [Config]),
-
- MgrNode = ?config(manager_node, Config),
- AgentNode = ?config(agent_node, Config),
- Addr = ?config(ip, Config),
- Port = ?AGENT_PORT,
-
- ?line ok = mgr_user_load_mib(MgrNode, std_mib()),
- Test2Mib = test2_mib(Config),
- ?line ok = mgr_user_load_mib(MgrNode, Test2Mib),
- ?line ok = agent_load_mib(AgentNode, Test2Mib),
-
- Exec = fun(Data) ->
- async_gb_exec1(MgrNode, Addr, Port, Data)
- end,
-
- %% We re-use the verification functions from the ssgb test-case
- VF04 = fun(X) ->
- verify_ssgb_reply2(X, [?sysDescr_instance, endOfMibView])
- end,
- VF06 = fun(X) ->
- verify_ssgb_reply2(X,
- [?sysDescr_instance, endOfMibView,
- ?sysObjectID_instance, endOfMibView])
- end,
- VF07 = fun(X) ->
- verify_ssgb_reply2(X,
- [?sysDescr_instance, endOfMibView,
- ?sysDescr_instance, endOfMibView,
- ?sysObjectID_instance, endOfMibView])
- end,
- VF08 = fun(X) ->
- verify_ssgb_reply2(X,
- [?sysDescr_instance,
- ?sysDescr_instance])
- end,
- VF10 = fun(X) ->
- verify_ssgb_reply3(X,
- [{?sysDescr, 'NULL'},
- {?sysObjectID, 'NULL'},
- {?tGenErr1, 'NULL'},
- {?sysDescr, 'NULL'}])
- end,
- ?line {ok, [TCnt2|_]} = mgr_user_name_to_oid(MgrNode, tCnt2),
- VF11 = fun(X) ->
- verify_ssgb_reply2(X,
- [{fl([TCnt2,2]), 100},
- {fl([TCnt2,2]), endOfMibView}])
- end,
- Requests = [
- { 1,
- {1, 1, []},
- Exec,
- fun verify_ssgb_reply1/1},
- { 2,
- {-1, 1, []},
- Exec,
- fun verify_ssgb_reply1/1},
- { 3,
- {-1, -1, []},
- Exec,
- fun verify_ssgb_reply1/1},
- { 4,
- {2, 0, [[sysDescr],[1,3,7,1]]},
- Exec,
- VF04},
- { 5,
- {1, 2, [[sysDescr],[1,3,7,1]]},
- Exec,
- VF04},
- { 6,
- {0, 2, [[sysDescr],[1,3,7,1]]},
- Exec,
- VF06},
- { 7,
- {2, 2, [[sysDescr],[1,3,7,1],[sysDescr],[1,3,7,1]]},
- Exec,
- VF07},
- { 8,
- {1, 2, [[sysDescr],[sysDescr],[tTooBig]]},
- Exec,
- VF08},
- { 9,
- {1, 12, [[tDescr2], [sysDescr]]},
- Exec,
- fun verify_ssgb_reply1/1},
- {10,
- {2, 2, [[sysDescr],[sysObjectID], [tGenErr1],[sysDescr]]},
- Exec,
- VF10},
- {11,
- {0, 2, [[TCnt2, 1]]},
- Exec,
- VF11},
- {12,
- {2, 0, [[sysDescr],[1,3,7,1]]},
- Exec,
- VF04},
- {13,
- {1, 12, [[tDescr2], [sysDescr]]},
- Exec,
- fun verify_ssgb_reply1/1},
- {14,
- {2, 2, [[sysDescr],[sysObjectID],[tGenErr1],[sysDescr]]},
- Exec,
- VF10},
- {15,
- {0, 2, [[TCnt2, 1]]},
- Exec,
- VF11},
- {16,
- {2, 2, [[sysDescr],[1,3,7,1],[sysDescr],[1,3,7,1]]},
- Exec,
- VF07},
- {17,
- {2, 2, [[sysDescr],[sysObjectID], [tGenErr1],[sysDescr]]},
- Exec,
- VF10}
- ],
-
- p("manager info when starting test: ~n~p", [mgr_info(MgrNode)]),
- p("agent info when starting test: ~n~p", [agent_info(AgentNode)]),
-
- ?line ok = async_exec(Requests, []),
-
- p("manager info when ending test: ~n~p", [mgr_info(MgrNode)]),
- p("agent info when ending test: ~n~p", [agent_info(AgentNode)]),
-
- display_log(Config),
- ok.
-
-
-async_gb_exec1(Node, Addr, Port, {NR, MR, Oids}) ->
- mgr_user_async_get_bulk(Node, Addr, Port, NR, MR, Oids).
-
-
-%%======================================================================
-
simple_async_get_bulk2(doc) ->
["Simple (async) get_bulk-request - Version 2 API (TargetName)"];
simple_async_get_bulk2(suite) -> [];
simple_async_get_bulk2(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- put(tname, sagb2),
+ ?TC_TRY(simple_async_get_bulk2,
+ fun() -> do_simple_async_get_bulk2(Config) end).
+
+do_simple_async_get_bulk2(Config) ->
p("starting with Config: ~p~n", [Config]),
MgrNode = ?config(manager_node, Config),
@@ -3747,8 +3051,10 @@ simple_async_get_bulk3(doc) ->
"Version 3 API (TargetName with send-opts)"];
simple_async_get_bulk3(suite) -> [];
simple_async_get_bulk3(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- put(tname, sagb3),
+ ?TC_TRY(simple_async_get_bulk3,
+ fun() -> do_simple_async_get_bulk3(Config) end).
+
+do_simple_async_get_bulk3(Config) ->
p("starting with Config: ~p~n", [Config]),
MgrNode = ?config(manager_node, Config),
@@ -3787,204 +3093,14 @@ async_gb_exec3(Node, TargetName, {NR, MR, Oids}, SendOpts) ->
%%======================================================================
-misc_async1(doc) -> ["Misc (async) request(s) - "
- "Old style (Addr & Port)"];
-misc_async1(suite) -> [];
-misc_async1(Config) when is_list(Config) ->
- ?SKIP(api_no_longer_supported),
-
- process_flag(trap_exit, true),
- put(tname, ms1),
- p("starting with Config: ~p~n", [Config]),
-
- MgrNode = ?config(manager_node, Config),
- AgentNode = ?config(agent_node, Config),
- Addr = ?config(ip, Config),
- Port = ?AGENT_PORT,
-
- ?line ok = mgr_user_load_mib(MgrNode, std_mib()),
- Test2Mib = test2_mib(Config),
- ?line ok = mgr_user_load_mib(MgrNode, Test2Mib),
- ?line ok = agent_load_mib(AgentNode, Test2Mib),
-
- ExecG = fun(Data) ->
- async_g_exec1(MgrNode, Addr, Port, Data)
- end,
-
- ExecGN = fun(Data) ->
- async_gn_exec1(MgrNode, Addr, Port, Data)
- end,
-
- ExecS = fun(Data) ->
- async_s_exec1(MgrNode, Addr, Port, Data)
- end,
-
- ExecGB = fun(Data) ->
- async_gb_exec1(MgrNode, Addr, Port, Data)
- end,
-
- ?line {ok, [TCnt2|_]} = mgr_user_name_to_oid(MgrNode, tCnt2),
- ?line {ok, [TGenErr1|_]} = mgr_user_name_to_oid(MgrNode, tGenErr1),
- ?line {ok, [TGenErr2|_]} = mgr_user_name_to_oid(MgrNode, tGenErr2),
- ?line {ok, [TGenErr3|_]} = mgr_user_name_to_oid(MgrNode, tGenErr3),
- ?line {ok, [TTooBig|_]} = mgr_user_name_to_oid(MgrNode, tTooBig),
-
- Requests =
- [
- { 1,
- [?sysObjectID_instance],
- ExecG,
- fun(X) ->
- sag_verify(X, [?sysObjectID_instance])
- end
- },
- { 2,
- {1, 1, []},
- ExecGB,
- fun verify_ssgb_reply1/1},
- { 3,
- {-1, 1, []},
- ExecGB,
- fun verify_ssgb_reply1/1},
- { 4,
- [{?sysLocation_instance, s, "Stockholm"},
- {?sysName_instance, s, "Arne Anka"}],
- ExecS,
- fun(X) ->
- sas_verify(X, [?sysLocation_instance, ?sysName_instance])
- end},
- { 5,
- [[sysDescr], [1,3,7,1]],
- ExecGN,
- fun(X) ->
- verify_ssgn_reply1(X, [?sysDescr_instance, endOfMibView])
- end},
- { 6,
- [[sysObjectID, 0], [sysDescr, 0], [sysUpTime, 0]],
- ExecG,
- fun(X) ->
- sag_verify(X, [?sysObjectID_instance,
- ?sysDescr_instance,
- ?sysUpTime_instance])
- end},
- { 7,
- [TGenErr2],
- ExecGN,
- fun(X) ->
- verify_ssgn_reply2(X, {genErr, 1, [TGenErr2]})
- end},
- { 8,
- {2, 0, [[sysDescr],[1,3,7,1]]},
- ExecGB,
- fun(X) ->
- verify_ssgb_reply2(X, [?sysDescr_instance, endOfMibView])
- end},
- { 9,
- {1, 2, [[sysDescr],[1,3,7,1]]},
- ExecGB,
- fun(X) ->
- verify_ssgb_reply2(X, [?sysDescr_instance, endOfMibView])
- end},
- {10,
- [TGenErr1],
- ExecGN,
- fun(X) ->
- verify_ssgn_reply2(X, {genErr, 1, [TGenErr1]})
- end},
- {11,
- {0, 2, [[sysDescr],[1,3,7,1]]},
- ExecGB,
- fun(X) ->
- verify_ssgb_reply2(X,
- [?sysDescr_instance, endOfMibView,
- ?sysObjectID_instance, endOfMibView])
- end},
- {12,
- [{[sysName, 0], "Gothenburg"},
- {[sysLocation, 0], "Sune Anka"}],
- ExecS,
- fun(X) ->
- sas_verify(X, [?sysName_instance, ?sysLocation_instance])
- end},
- {13,
- {2, 2, [[sysDescr],[1,3,7,1],[sysDescr],[1,3,7,1]]},
- ExecGB,
- fun(X) ->
- verify_ssgb_reply2(X,
- [?sysDescr_instance, endOfMibView,
- ?sysDescr_instance, endOfMibView,
- ?sysObjectID_instance, endOfMibView])
- end},
- {14,
- {1, 2, [[sysDescr],[sysDescr],[tTooBig]]},
- ExecGB,
- fun(X) ->
- verify_ssgb_reply2(X,
- [?sysDescr_instance,
- ?sysDescr_instance])
- end},
- {15,
- {1, 12, [[tDescr2], [sysDescr]]},
- ExecGB,
- fun verify_ssgb_reply1/1},
- {16,
- {2, 2, [[sysDescr],[sysObjectID], [tGenErr1],[sysDescr]]},
- ExecGB,
- fun(X) ->
- verify_ssgb_reply3(X,
- [{?sysDescr, 'NULL'},
- {?sysObjectID, 'NULL'},
- {?tGenErr1, 'NULL'},
- {?sysDescr, 'NULL'}])
- end},
- {17,
- [[sysDescr], TGenErr3],
- ExecGN,
- fun(X) ->
- verify_ssgn_reply2(X, {genErr, 2, [TGenErr3]})
- end},
- {18,
- {0, 2, [[TCnt2, 1]]},
- ExecGB,
- fun(X) ->
- verify_ssgb_reply2(X,
- [{fl([TCnt2,2]), 100},
- {fl([TCnt2,2]), endOfMibView}])
- end},
- {19,
- [TTooBig],
- ExecGN,
- fun(X) ->
- verify_ssgn_reply2(X, {tooBig, 0, []})
- end},
- {20,
- [TTooBig],
- ExecGN,
- fun(X) ->
- verify_ssgn_reply2(X, {tooBig, 0, []})
- end}
- ],
-
- p("manager info when starting test: ~n~p", [mgr_info(MgrNode)]),
- p("agent info when starting test: ~n~p", [agent_info(AgentNode)]),
-
- ?line ok = async_exec(Requests, []),
-
- p("manager info when ending test: ~n~p", [mgr_info(MgrNode)]),
- p("agent info when ending test: ~n~p", [agent_info(AgentNode)]),
-
- display_log(Config),
- ok.
-
-
-%%======================================================================
-
misc_async2(doc) ->
["Misc (async) request(s) - Version 2 API (TargetName)"];
misc_async2(suite) -> [];
misc_async2(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- put(tname, ms2),
+ ?TC_TRY(misc_async2,
+ fun() -> do_misc_async2(Config) end).
+
+do_misc_async2(Config) ->
p("starting with Config: ~p~n", [Config]),
MgrNode = ?config(manager_node, Config),
@@ -4233,8 +3349,10 @@ verify_trap(Trap, [{Id, Verifier}|Verifiers]) ->
trap1(suite) -> [];
trap1(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- put(tname,t1),
+ ?TC_TRY(trap1,
+ fun() -> do_trap1(Config) end).
+
+do_trap1(Config) ->
p("starting with Config: ~p~n", [Config]),
MgrNode = ?config(manager_node, Config),
@@ -4386,8 +3504,10 @@ trap1(Config) when is_list(Config) ->
trap2(suite) -> [];
trap2(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- put(tname,t2),
+ ?TC_TRY(trap2,
+ fun() -> do_trap2(Config) end).
+
+do_trap2(Config) ->
p("starting with Config: ~p~n", [Config]),
MgrNode = ?config(manager_node, Config),
@@ -4579,8 +3699,10 @@ trap2(Config) when is_list(Config) ->
inform1(suite) -> [];
inform1(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- put(tname,i1),
+ ?TC_TRY(inform1,
+ fun() -> do_inform1(Config) end).
+
+do_inform1(Config) ->
p("starting with Config: ~p~n", [Config]),
MgrNode = ?config(manager_node, Config),
@@ -4706,8 +3828,10 @@ inform1(Config) when is_list(Config) ->
inform2(suite) -> [];
inform2(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- put(tname, i2),
+ ?TC_TRY(inform2,
+ fun() -> do_inform2(Config) end).
+
+do_inform2(Config) ->
p("starting with Config: ~p~n", [Config]),
MgrNode = ?config(manager_node, Config),
@@ -4878,8 +4002,10 @@ inform2(Config) when is_list(Config) ->
inform3(suite) -> [];
inform3(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- put(tname,i3),
+ ?TC_TRY(inform3,
+ fun() -> do_inform3(Config) end).
+
+do_inform3(Config) ->
p("starting with Config: ~p~n", [Config]),
MgrNode = ?config(manager_node, Config),
@@ -5014,8 +4140,10 @@ inform3(Config) when is_list(Config) ->
inform4(suite) -> [];
inform4(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- put(tname,i4),
+ ?TC_TRY(inform4,
+ fun() -> do_inform4(Config) end).
+
+do_inform4(Config) ->
p("starting with Config: ~p~n", [Config]),
MgrNode = ?config(manager_node, Config),
@@ -5134,8 +4262,10 @@ inform4(Config) when is_list(Config) ->
inform_swarm(suite) -> [];
inform_swarm(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- put(tname, is),
+ ?TC_TRY(inform_swarm,
+ fun() -> do_inform_swarm(Config) end).
+
+do_inform_swarm(Config) ->
p("starting with Config: ~p~n", [Config]),
MgrNode = ?config(manager_node, Config),
@@ -5306,8 +4436,10 @@ report(Config) when is_list(Config) ->
otp8015_1(doc) -> ["OTP-8015:1 - testing the new api-function."];
otp8015_1(suite) -> [];
otp8015_1(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- put(tname, otp8015_1),
+ ?TC_TRY(otp8015_1,
+ fun() -> do_otp8015_1(Config) end).
+
+do_otp8015_1(Config) ->
p("starting with Config: ~p~n", [Config]),
ConfDir = ?config(manager_conf_dir, Config),
@@ -5354,8 +4486,10 @@ otp8015_1(Config) when is_list(Config) ->
otp8395_1(doc) -> ["OTP-8395:1 - simple get with ATL sequence numbering."];
otp8395_1(suite) -> [];
otp8395_1(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- put(tname, otp8395_1),
+ ?TC_TRY(otp8395_1,
+ fun() -> do_otp8395_1(Config) end).
+
+do_otp8395_1(Config) ->
do_simple_sync_get2(Config).
@@ -6016,40 +5150,21 @@ mgr_user_stop(Node) ->
mgr_user_register_agent(Node, TargetName, Conf)
when is_list(TargetName) andalso is_list(Conf) ->
rcall(Node, snmp_manager_user, register_agent, [TargetName, Conf]).
-%% <REMOVED-IN-R16B>
-%% mgr_user_register_agent(Node, Addr, Port) ->
-%% mgr_user_register_agent(Node, Addr, Port, []).
-%% mgr_user_register_agent(Node, Addr, Port, Conf) ->
-%% rcall(Node, snmp_manager_user, register_agent, [Addr, Port, Conf]).
-%% </REMOVED-IN-R16B>
%% mgr_user_unregister_agent(Node) ->
%% mgr_user_unregister_agent(Node, ?LOCALHOST(), ?AGENT_PORT).
mgr_user_unregister_agent(Node, TargetName) when is_list(TargetName) ->
rcall(Node, snmp_manager_user, unregister_agent, [TargetName]).
-%% <REMOVED-IN-R16B>
-%% mgr_user_unregister_agent(Node, Addr, Port) ->
-%% rcall(Node, snmp_manager_user, unregister_agent, [Addr, Port]).
-%% </REMOVED-IN-R16B>
mgr_user_agent_info(Node, TargetName, Item)
when is_list(TargetName) andalso is_atom(Item) ->
rcall(Node, snmp_manager_user, agent_info, [TargetName, Item]).
-%% <REMOVED-IN-R16B>
-%% mgr_user_agent_info(Node, Addr, Port, Item) when is_atom(Item) ->
-%% rcall(Node, snmp_manager_user, agent_info, [Addr, Port, Item]).
-%% </REMOVED-IN-R16B>
%% mgr_user_update_agent_info(Node, Item, Val) when atom(Item) ->
%% mgr_user_update_agent_info(Node, ?LOCALHOST(), ?AGENT_PORT, Item, Val).
mgr_user_update_agent_info(Node, TargetName, Item, Val)
when is_list(TargetName) andalso is_atom(Item) ->
rcall(Node, snmp_manager_user, update_agent_info, [TargetName, Item, Val]).
-%% <REMOVED-IN-R16B>
-%% mgr_user_update_agent_info(Node, Addr, Port, Item, Val) when is_atom(Item) ->
-%% rcall(Node, snmp_manager_user, update_agent_info,
-%% [Addr, Port, Item, Val]).
-%% </REMOVED-IN-R16B>
%% mgr_user_which_all_agents(Node) ->
%% rcall(Node, snmp_manager_user, which_all_agents, []).
@@ -6064,10 +5179,6 @@ mgr_user_load_mib(Node, Mib) ->
%% mgr_user_sync_get(Node, ?LOCALHOST(), ?AGENT_PORT, Oids).
mgr_user_sync_get(Node, TargetName, Oids) when is_list(TargetName) ->
rcall(Node, snmp_manager_user, sync_get, [TargetName, Oids]).
-%% <REMOVED-IN-R16B>
-mgr_user_sync_get(Node, Addr, Port, Oids) ->
- rcall(Node, snmp_manager_user, sync_get, [Addr, Port, Oids]).
-%% </REMOVED-IN-R16B>
mgr_user_sync_get2(Node, TargetName, Oids, SendOpts) when is_list(TargetName) ->
rcall(Node, snmp_manager_user, sync_get2, [TargetName, Oids, SendOpts]).
@@ -6076,10 +5187,6 @@ mgr_user_sync_get2(Node, TargetName, Oids, SendOpts) when is_list(TargetName) ->
%% mgr_user_async_get(Node, ?LOCALHOST(), ?AGENT_PORT, Oids).
mgr_user_async_get(Node, TargetName, Oids) when is_list(TargetName) ->
rcall(Node, snmp_manager_user, async_get, [TargetName, Oids]).
-%% <REMOVED-IN-R16B>
-mgr_user_async_get(Node, Addr, Port, Oids) ->
- rcall(Node, snmp_manager_user, async_get, [Addr, Port, Oids]).
-%% </REMOVED-IN-R16B>
mgr_user_async_get2(Node, TargetName, Oids, SendOpts)
when is_list(TargetName) ->
@@ -6089,10 +5196,6 @@ mgr_user_async_get2(Node, TargetName, Oids, SendOpts)
%% mgr_user_sync_get_next(Node, ?LOCALHOST(), ?AGENT_PORT, Oids).
mgr_user_sync_get_next(Node, TargetName, Oids) when is_list(TargetName) ->
rcall(Node, snmp_manager_user, sync_get_next, [TargetName, Oids]).
-%% <REMOVED-IN-R16B>
-mgr_user_sync_get_next(Node, Addr, Port, Oids) ->
- rcall(Node, snmp_manager_user, sync_get_next, [Addr, Port, Oids]).
-%% </REMOVED-IN-R16B>
mgr_user_sync_get_next2(Node, TargetName, Oids, SendOpts)
when is_list(TargetName) ->
@@ -6102,10 +5205,6 @@ mgr_user_sync_get_next2(Node, TargetName, Oids, SendOpts)
%% mgr_user_async_get_next(Node, ?LOCALHOST(), ?AGENT_PORT, Oids).
mgr_user_async_get_next(Node, TargetName, Oids) when is_list(TargetName) ->
rcall(Node, snmp_manager_user, async_get_next, [TargetName, Oids]).
-%% <REMOVED-IN-R16B>
-mgr_user_async_get_next(Node, Addr, Port, Oids) ->
- rcall(Node, snmp_manager_user, async_get_next, [Addr, Port, Oids]).
-%% </REMOVED-IN-R16B>
mgr_user_async_get_next2(Node, TargetName, Oids, SendOpts)
when is_list(TargetName) ->
@@ -6115,10 +5214,6 @@ mgr_user_async_get_next2(Node, TargetName, Oids, SendOpts)
%% mgr_user_sync_set(Node, ?LOCALHOST(), ?AGENT_PORT, VAV).
mgr_user_sync_set(Node, TargetName, VAV) when is_list(TargetName) ->
rcall(Node, snmp_manager_user, sync_set, [TargetName, VAV]).
-%% <REMOVED-IN-R16B>
-mgr_user_sync_set(Node, Addr, Port, VAV) ->
- rcall(Node, snmp_manager_user, sync_set, [Addr, Port, VAV]).
-%% </REMOVED-IN-R16B>
mgr_user_sync_set2(Node, TargetName, VAV, SendOpts) when is_list(TargetName) ->
rcall(Node, snmp_manager_user, sync_set2, [TargetName, VAV, SendOpts]).
@@ -6127,10 +5222,6 @@ mgr_user_sync_set2(Node, TargetName, VAV, SendOpts) when is_list(TargetName) ->
%% mgr_user_async_set(Node, ?LOCALHOST(), ?AGENT_PORT, VAV).
mgr_user_async_set(Node, TargetName, VAV) when is_list(TargetName) ->
rcall(Node, snmp_manager_user, async_set, [TargetName, VAV]).
-%% <REMOVED-IN-R16B>
-mgr_user_async_set(Node, Addr, Port, VAV) ->
- rcall(Node, snmp_manager_user, async_set, [Addr, Port, VAV]).
-%% </REMOVED-IN-R16B>
mgr_user_async_set2(Node, TargetName, VAV, SendOpts) when is_list(TargetName) ->
rcall(Node, snmp_manager_user, async_set2, [TargetName, VAV, SendOpts]).
@@ -6142,11 +5233,6 @@ mgr_user_sync_get_bulk(Node, TargetName, NonRep, MaxRep, Oids)
when is_list(TargetName) ->
rcall(Node, snmp_manager_user, sync_get_bulk,
[TargetName, NonRep, MaxRep, Oids]).
-%% <REMOVED-IN-R16B>
-mgr_user_sync_get_bulk(Node, Addr, Port, NonRep, MaxRep, Oids) ->
- rcall(Node, snmp_manager_user, sync_get_bulk,
- [Addr, Port, NonRep, MaxRep, Oids]).
-%% </REMOVED-IN-R16B>
mgr_user_sync_get_bulk2(Node, TargetName, NonRep, MaxRep, Oids, SendOpts)
when is_list(TargetName) ->
@@ -6160,11 +5246,6 @@ mgr_user_async_get_bulk(Node, TargetName, NonRep, MaxRep, Oids)
when is_list(TargetName) ->
rcall(Node, snmp_manager_user, async_get_bulk,
[TargetName, NonRep, MaxRep, Oids]).
-%% <REMOVED-IN-R16B>
-mgr_user_async_get_bulk(Node, Addr, Port, NonRep, MaxRep, Oids) ->
- rcall(Node, snmp_manager_user, async_get_bulk,
- [Addr, Port, NonRep, MaxRep, Oids]).
-%% </REMOVED-IN-R16B>
mgr_user_async_get_bulk2(Node, TargetName, NonRep, MaxRep, Oids, SendOpts)
when is_list(TargetName) ->
@@ -6338,9 +5419,12 @@ start_node(Name, Retry) ->
error ->
""
end,
- A = Args ++ " -pa " ++ Pa,
+ A = Args ++ " -pa " ++ Pa ++
+ " -s " ++ atom_to_list(snmp_test_sys_monitor) ++ " start" ++
+ " -s global sync",
try ?START_NODE(Name, A) of
{ok, Node} ->
+ global:sync(),
Node;
{error, timeout} ->
e("Failed starting node ~p: timeout", [Name]),
diff --git a/lib/snmp/test/snmp_test_lib.erl b/lib/snmp/test/snmp_test_lib.erl
index 35bc535a80..a040c4f39c 100644
--- a/lib/snmp/test/snmp_test_lib.erl
+++ b/lib/snmp/test/snmp_test_lib.erl
@@ -23,6 +23,7 @@
-include_lib("kernel/include/file.hrl").
+-export([tc_try/2, tc_try/3]).
-export([hostname/0, hostname/1, localhost/0, localhost/1, os_type/0, sz/1,
display_suite_info/1]).
-export([non_pc_tc_maybe_skip/4, os_based_skip/1,
@@ -43,10 +44,127 @@
-export([watchdog/3, watchdog_start/1, watchdog_start/2, watchdog_stop/1]).
-export([del_dir/1]).
-export([cover/1]).
--export([p/2, print1/2, print2/2, print/5, formated_timestamp/0]).
+-export([f/2, p/2, print1/2, print2/2, print/5, formated_timestamp/0]).
%% ----------------------------------------------------------------------
+%% Run test-case
+%%
+
+%% *** tc_try/2,3 ***
+%% Case: Basically the test case name
+%% TCCondFun: A fun that is evaluated before the actual test case
+%% The point of this is that it can performs checks to
+%% see if we shall run the test case at all.
+%% For instance, the test case may only work in specific
+%% conditions.
+%% FCFun: The test case fun
+tc_try(Case, TCFun) ->
+ tc_try(Case, fun() -> ok end, TCFun).
+
+tc_try(Case, TCCondFun, TCFun)
+ when is_atom(Case) andalso
+ is_function(TCCondFun, 0) andalso
+ is_function(TCFun, 0) ->
+ tc_begin(Case),
+ try TCCondFun() of
+ ok ->
+ try
+ begin
+ TCFun(),
+ sleep(seconds(1)),
+ tc_end("ok")
+ end
+ catch
+ C:{skip, _} = SKIP when ((C =:= throw) orelse (C =:= exit)) ->
+ tc_end( f("skipping(catched,~w,tc)", [C]) ),
+ SKIP;
+ C:E:S ->
+ %% We always check the system events before we accept a failure
+ case snmp_test_global_sys_monitor:events() of
+ [] ->
+ tc_end( f("failed(catched,~w,tc)", [C]) ),
+ erlang:raise(C, E, S);
+ SysEvs ->
+ tc_print("System Events received: "
+ "~n ~p", [SysEvs], "", ""),
+ tc_end( f("skipping(catched-sysevs,~w,tc)", [C]) ),
+ SKIP = {skip, "TC failure with system events"},
+ SKIP
+ end
+ end;
+ {skip, _} = SKIP ->
+ tc_end("skipping(cond)"),
+ SKIP;
+ {error, Reason} ->
+ tc_end("failed(cond)"),
+ exit({tc_cond_failed, Reason})
+ catch
+ C:{skip, _} = SKIP when ((C =:= throw) orelse (C =:= exit)) ->
+ tc_end( f("skipping(catched,~w,cond)", [C]) ),
+ SKIP;
+ C:E:S ->
+ %% We always check the system events before we accept a failure
+ case snmp_test_global_sys_monitor:events() of
+ [] ->
+ tc_end( f("failed(catched,~w,cond)", [C]) ),
+ erlang:raise(C, E, S);
+ SysEvs ->
+ tc_print("System Events received: "
+ "~n ~p", [SysEvs], "", ""),
+ tc_end( f("skipping(catched-sysevs,~w,cond)", [C]) ),
+ SKIP = {skip, "TC cond failure with system events"},
+ SKIP
+ end
+ end.
+
+
+tc_set_name(N) when is_atom(N) ->
+ tc_set_name(atom_to_list(N));
+tc_set_name(N) when is_list(N) ->
+ put(tc_name, N).
+
+tc_get_name() ->
+ get(tc_name).
+
+tc_begin(TC) ->
+ OldVal = process_flag(trap_exit, true),
+ put(old_trap_exit, OldVal),
+ tc_set_name(TC),
+ tc_print("begin ***",
+ "~n----------------------------------------------------~n", "").
+
+tc_end(Result) when is_list(Result) ->
+ OldVal = erase(old_trap_exit),
+ process_flag(trap_exit, OldVal),
+ tc_print("done: ~s", [Result],
+ "", "----------------------------------------------------~n~n"),
+ ok.
+
+tc_print(F, Before, After) ->
+ tc_print(F, [], Before, After).
+
+tc_print(F, A, Before, After) ->
+ Name = tc_which_name(),
+ FStr = f("*** [~s][~s][~p] " ++ F ++ "~n",
+ [formated_timestamp(),Name,self()|A]),
+ io:format(user, Before ++ FStr ++ After, []).
+
+tc_which_name() ->
+ case tc_get_name() of
+ undefined ->
+ case get(sname) of
+ undefined ->
+ "";
+ SName when is_list(SName) ->
+ SName
+ end;
+ Name when is_list(Name) ->
+ Name
+ end.
+
+
+%% ----------------------------------------------------------------------
%% Misc functions
%%
@@ -202,11 +320,14 @@ non_pc_tc_maybe_skip(Config, Condition, File, Line)
%% test-server...
ok;
_ ->
- case Condition() of
+ try Condition() of
true ->
skip(non_pc_testcase, File, Line);
false ->
ok
+ catch
+ C:E:S ->
+ skip({condition, C, E, S}, File, Line)
end
end
end.
@@ -712,6 +833,9 @@ cover([Suite, Case] = Args) when is_atom(Suite) andalso is_atom(Case) ->
%% (debug) Print functions
%%
+f(F, A) ->
+ lists:flatten(io_lib:format(F, A)).
+
p(Mod, Case) when is_atom(Mod) andalso is_atom(Case) ->
case get(test_case) of
undefined ->
diff --git a/lib/snmp/test/snmp_test_lib.hrl b/lib/snmp/test/snmp_test_lib.hrl
index 99b86a928c..f077f15d3e 100644
--- a/lib/snmp/test/snmp_test_lib.hrl
+++ b/lib/snmp/test/snmp_test_lib.hrl
@@ -18,15 +18,11 @@
%% The Initial Developer of the Original Code is Ericsson AB.
%% %CopyrightEnd%
%%
+
%%----------------------------------------------------------------------
-%% Purpose: Define common macros for testing
+%% Purpose: Define common macros for (the snmp) testing
%%----------------------------------------------------------------------
-%% - (some of the) Macros stolen from the test server -
-
-%% -define(line,put(test_server_loc,{?MODULE,?LINE}),).
-
-
%% - Misc macros -
-ifndef(APPLICATION).
@@ -45,6 +41,8 @@
%% - Test case macros -
+-define(TC_TRY(C, TC), snmp_test_lib:tc_try(C, TC)).
+-define(TC_TRY(C, TCCond, TC), snmp_test_lib:tc_try(C, TCCond, TC)).
-define(OS_BASED_SKIP(Skippable),
snmp_test_lib:os_based_skip(Skippable)).
-define(NON_PC_TC_MAYBE_SKIP(Config, Condition),
diff --git a/lib/snmp/test/snmp_test_mgr.erl b/lib/snmp/test/snmp_test_mgr.erl
index 9d6be65088..f50147a852 100644
--- a/lib/snmp/test/snmp_test_mgr.erl
+++ b/lib/snmp/test/snmp_test_mgr.erl
@@ -700,18 +700,30 @@ echo_errors({error, Id, {ExpectedFormat, ExpectedData}, {Format, Data}})->
echo_errors(ok) -> ok;
echo_errors({ok, Val}) -> {ok, Val}.
-get_response_impl(Id, Vars) ->
+get_response_impl(Id, ExpVars) ->
+ ?PRINT2("await response ~w with"
+ "~n Expected Varbinds: ~p",
+ [Id, ExpVars]),
+ PureVars = find_pure_oids2(ExpVars),
case receive_response() of
#pdu{type = 'get-response',
error_status = noError,
error_index = 0,
varbinds = VBs} ->
- match_vars(Id, find_pure_oids2(Vars), VBs, []);
+ ?PRINT2("received expected response pdu (~w) - match vars"
+ "~n Expected VBs: ~p"
+ "~n Received VBs: ~p",
+ [Id, PureVars, VBs]),
+ match_vars(Id, PureVars, VBs, []);
#pdu{type = Type2,
request_id = ReqId,
error_status = Err2,
error_index = Index2} ->
+ ?EPRINT2("received unexpected response pdu: ~w, ~w, ~w"
+ "~n Received Error: ~p"
+ "~n Received Index: ~p",
+ [Type2, Id, ReqId, Err2, Index2]),
{error,
Id,
{"Type: ~w, ErrStat: ~w, Idx: ~w, RequestId: ~w",
@@ -720,6 +732,8 @@ get_response_impl(Id, Vars) ->
[Type2, Err2, Index2]}};
{error, Reason} ->
+ ?EPRINT2("unexpected receive pdu error: ~w"
+ "~n ~p", [Id, Reason]),
format_reason(Id, Reason)
end.
@@ -729,171 +743,208 @@ get_response_impl(Id, Vars) ->
%% Returns: ok | {error, Id, {ExpectedFormat, ExpectedData}, {Format, Data}}
%%----------------------------------------------------------------------
expect_impl(Id, any) ->
- io:format("expect_impl(~w, any) -> entry ~n", [Id]),
+ ?PRINT2("await ~w pdu (any)", [Id]),
case receive_response() of
- PDU when is_record(PDU, pdu) -> ok;
- {error, Reason} -> format_reason(Id, Reason)
+ PDU when is_record(PDU, pdu) ->
+ ?PRINT2("received expected pdu (~w)", [Id]),
+ ok;
+ {error, Reason} ->
+ ?EPRINT1("unexpected receive error: ~w"
+ "~n ~p", [Id, Reason]),
+ format_reason(Id, Reason)
end;
expect_impl(Id, return) ->
- io:format("expect_impl(~w, return) -> entry ~n", [Id]),
+ ?PRINT2("await ~w pdu", [Id]),
case receive_response() of
- PDU when is_record(PDU, pdu) -> {ok, PDU};
- {error, Reason} -> format_reason(Id, Reason)
+ PDU when is_record(PDU, pdu) ->
+ ?PRINT2("received expected pdu (~w)", [Id]),
+ {ok, PDU};
+ {error, Reason} ->
+ ?EPRINT1("unexpected receive error: ~w"
+ "~n ~p", [Id, Reason]),
+ format_reason(Id, Reason)
end;
expect_impl(Id, trap) ->
- io:format("expect_impl(~w, trap) -> entry ~n", [Id]),
+ ?PRINT2("await ~w trap", [Id]),
case receive_trap(3500) of
- PDU when is_record(PDU, trappdu) -> ok;
- {error, Reason} -> format_reason(Id, Reason)
+ PDU when is_record(PDU, trappdu) ->
+ ?PRINT2("received expected trap (~w)", [Id]),
+ ok;
+ {error, Reason} ->
+ ?EPRINT1("unexpected receive error: ~w"
+ "~n ~p", [Id, Reason]),
+ format_reason(Id, Reason)
end;
expect_impl(Id, timeout) ->
- io:format("expect_impl(~w, timeout) -> entry ~n", [Id]),
+ ?PRINT2("await ~w nothing", [Id]),
receive
X ->
- io:format("expect_impl(~w, timeout) -> "
- "received unexpected message: ~n~p~n", [Id, X]),
+ ?EPRINT1("received unexpected message: ~w"
+ "~n ~p",
+ [Id, X]),
{error, Id, {"Timeout", []}, {"Message ~w", [X]}}
after 3500 ->
ok
end;
expect_impl(Id, Err) when is_atom(Err) ->
- io:format("expect_impl(~w, ~w) -> entry ~n", [Id, Err]),
+ ?PRINT2("await ~w with"
+ "~n Err: ~p",
+ [Id, Err]),
case receive_response() of
#pdu{error_status = Err} ->
+ ?PRINT2("received pdu with expected error status (~w, ~w)",
+ [Id, Err]),
ok;
- #pdu{request_id = ReqId,
- error_status = OtherErr} ->
- io:format("expect_impl(~w, ~w) -> "
- "received pdu (~w) with unexpected error-status: "
- "~n~p~n", [Id, Err, ReqId, OtherErr]),
+ #pdu{type = Type2,
+ request_id = ReqId,
+ error_status = Err2} ->
+ ?EPRINT1("received pdu with unexpected error status: ~w, ~w, ~w"
+ "~n Expected Error: ~p"
+ "~n Received Error: ~p",
+ [Type2, Id, ReqId, Err, Err2]),
{error, Id, {"ErrorStatus: ~w, RequestId: ~w", [Err,ReqId]},
- {"ErrorStatus: ~w", [OtherErr]}};
+ {"ErrorStatus: ~w", [Err2]}};
{error, Reason} ->
+ ?EPRINT1("unexpected receive error: ~w"
+ "~n ~p", [Id, Reason]),
format_reason(Id, Reason)
end;
expect_impl(Id, ExpectedVarbinds) when is_list(ExpectedVarbinds) ->
- io:format("expect_impl(~w) -> entry with"
- "~n ExpectedVarbinds: ~p~n", [Id, ExpectedVarbinds]),
+ ?PRINT2("await ~w with"
+ "~n ExpectedVarbinds: ~p",
+ [Id, ExpectedVarbinds]),
+ PureVars = find_pure_oids(ExpectedVarbinds),
case receive_response() of
#pdu{type = 'get-response',
error_status = noError,
error_index = 0,
varbinds = VBs} ->
- io:format("expect_impl(~w) -> received pdu with"
- "~n VBs: ~p~n", [Id, VBs]),
- check_vars(Id, find_pure_oids(ExpectedVarbinds), VBs);
+ ?PRINT2("received expected response pdu (~w) - check varbinds"
+ "~n Expected VBs: ~p"
+ "~n Received VBs: ~p",
+ [Id, PureVars, VBs]),
+ check_vars(Id, PureVars, VBs);
#pdu{type = Type2,
request_id = ReqId,
error_status = Err2,
error_index = Index2} ->
- io:format("expect_impl(~w) -> received unexpected pdu with"
- "~n Type2: ~p"
- "~n ReqId: ~p"
- "~n Err2: ~p"
- "~n Index2: ~p"
- "~n", [Id, Type2, ReqId, Err2, Index2]),
+ ?EPRINT1("received unexpected pdu: ~w, ~w, ~w"
+ "~n Received Error: ~p"
+ "~n Received Index: ~p",
+ [Type2, Id, ReqId, Err2, Index2]),
{error, Id, {"Type: ~w, ErrStat: ~w, Idx: ~w, RequestId: ~w",
['get-response', noError, 0, ReqId]},
{"Type: ~w, ErrStat: ~w, Idx: ~w", [Type2, Err2, Index2]}};
{error, Reason} ->
+ ?EPRINT1("unexpected receive error: ~w"
+ "~n ~p", [Id, Reason]),
format_reason(Id, Reason)
end.
expect_impl(Id, v2trap, ExpectedVarbinds) when is_list(ExpectedVarbinds) ->
- io:format("expect_impl(~w, v2trap) -> entry with"
- "~n ExpectedVarbinds: ~p~n", [Id, ExpectedVarbinds]),
+ ?PRINT2("await v2 trap ~w with"
+ "~n ExpectedVarbinds: ~p",
+ [Id, ExpectedVarbinds]),
+ PureVars = find_pure_oids(ExpectedVarbinds),
case receive_response() of
#pdu{type = 'snmpv2-trap',
error_status = noError,
error_index = 0,
varbinds = VBs} ->
- io:format("expect_impl(~w, v2trap) -> received pdu with"
- "~n VBs: ~p~n", [Id, VBs]),
- check_vars(Id, find_pure_oids(ExpectedVarbinds), VBs);
+ ?PRINT2("received expected v2 trap (~w) - check varbinds"
+ "~n Expected VBs: ~p"
+ "~n Received VBs: ~p",
+ [Id, PureVars, VBs]),
+ check_vars(Id, PureVars, VBs);
#pdu{type = Type2,
request_id = ReqId,
error_status = Err2,
error_index = Index2} ->
- io:format("expect_impl(~w, v2trap) -> received unexpected pdu with"
- "~n Type2: ~p"
- "~n ReqId: ~p"
- "~n Err2: ~p"
- "~n Index2: ~p"
- "~n", [Id, Type2, ReqId, Err2, Index2]),
+ ?EPRINT1("received unexpected pdu: ~w, ~w, ~w"
+ "~n Received Error: ~p"
+ "~n Received Index: ~p",
+ [Type2, Id, ReqId, Err2, Index2]),
{error, Id, {"Type: ~w, ErrStat: ~w, Idx: ~w, RequestId: ~w",
['snmpv2-trap', noError, 0, ReqId]},
{"Type: ~w, ErrStat: ~w, Idx: ~w", [Type2, Err2, Index2]}};
{error, Reason} ->
+ ?EPRINT1("unexpected receive error: ~w"
+ "~n ~p", [Id, Reason]),
format_reason(Id, Reason)
end;
expect_impl(Id, report, ExpectedVarbinds) when is_list(ExpectedVarbinds) ->
- io:format("expect_impl(~w, report) -> entry with"
- "~n ExpectedVarbinds: ~p~n", [Id, ExpectedVarbinds]),
+ ?PRINT2("await report ~w with"
+ "~n ExpectedVarbinds: ~p",
+ [Id, ExpectedVarbinds]),
+ PureVBs = find_pure_oids(ExpectedVarbinds),
case receive_response() of
#pdu{type = 'report',
error_status = noError,
error_index = 0,
varbinds = VBs} ->
- io:format("expect_impl(~w, report) -> received pdu with"
- "~n VBs: ~p~n", [Id, VBs]),
- check_vars(Id, find_pure_oids(ExpectedVarbinds), VBs);
+ ?PRINT2("received expected report (~w) - check varbinds"
+ "~n Expected VBs: ~p"
+ "~n Received VBs: ~p",
+ [Id, PureVBs, VBs]),
+ check_vars(Id, PureVBs, VBs);
#pdu{type = Type2,
request_id = ReqId,
error_status = Err2,
error_index = Index2} ->
- io:format("expect_impl(~w, report) -> received unexpected pdu with"
- "~n Type2: ~p"
- "~n ReqId: ~p"
- "~n Err2: ~p"
- "~n Index2: ~p"
- "~n", [Id, Type2, ReqId, Err2, Index2]),
+ ?EPRINT1("received unexpected pdu: ~w, ~w, ~w"
+ "~n Received Error: ~p"
+ "~n Received Index: ~p",
+ [Type2, Id, ReqId, Err2, Index2]),
{error, Id, {"Type: ~w, ErrStat: ~w, Idx: ~w, RequestId: ~w",
[report, noError, 0, ReqId]},
{"Type: ~w, ErrStat: ~w, Idx: ~w", [Type2, Err2, Index2]}};
{error, Reason} ->
+ ?EPRINT1("unexpected receive error: ~w"
+ "~n ~p", [Id, Reason]),
format_reason(Id, Reason)
end;
expect_impl(Id, {inform, Reply}, ExpectedVarbinds)
when is_list(ExpectedVarbinds) ->
- io:format("expect_impl(~w, inform) -> entry with"
- "~n Reply: ~p"
- "~n ExpectedVarbinds: ~p"
- "~n", [Id, Reply, ExpectedVarbinds]),
- Resp = receive_response(),
+ ?PRINT2("await inform ~w with"
+ "~n Reply: ~p"
+ "~n ExpectedVarbinds: ~p",
+ [Id, Reply, ExpectedVarbinds]),
+ PureVBs = find_pure_oids(ExpectedVarbinds),
+ Resp = receive_response(),
case Resp of
#pdu{type = 'inform-request',
error_status = noError,
error_index = 0,
varbinds = VBs} ->
- io:format("expect_impl(~w, inform) -> received pdu with"
- "~n VBs: ~p~n", [Id, VBs]),
- case check_vars(Id, find_pure_oids(ExpectedVarbinds), VBs) of
+ ?PRINT2("received inform (~w) - check varbinds"
+ "~n Expected VBs: ~p"
+ "~n Received VBs: ~p",
+ [Id, PureVBs, VBs]),
+ case check_vars(Id, PureVBs, VBs) of
ok when (Reply == true) ->
- io:format("expect_impl(~w, inform) -> send ok response"
- "~n", [Id]),
+ ?PRINT2("varbinds ok (~w) - send ok inform response", [Id]),
RespPDU = Resp#pdu{type = 'get-response',
error_status = noError,
error_index = 0},
?MODULE:rpl(RespPDU),
ok;
ok when (element(1, Reply) == error) ->
- io:format("expect_impl(~w, inform) -> send error response"
- "~n", [Id]),
+ ?PRINT2("varbinds ok (~w) - send error inform response", [Id]),
{error, Status, Index} = Reply,
RespPDU = Resp#pdu{type = 'get-response',
error_status = Status,
@@ -901,10 +952,10 @@ expect_impl(Id, {inform, Reply}, ExpectedVarbinds)
?MODULE:rpl(RespPDU),
ok;
ok when (Reply == false) ->
- io:format("expect_impl(~w, inform) -> no response sent"
- "~n", [Id]),
+ ?PRINT2("varbinds ok (~w) - don't send inform response", [Id]),
ok;
Else ->
+ ?EPRINT1("unexpected varbinds (~w)", [Id]),
io:format("expect_impl(~w, inform) -> "
"~n Else: ~p"
"~n", [Id, Else]),
@@ -915,54 +966,54 @@ expect_impl(Id, {inform, Reply}, ExpectedVarbinds)
request_id = ReqId,
error_status = Err2,
error_index = Index2} ->
- io:format("expect_impl(~w, inform) -> received unexpected pdu with"
- "~n Type2: ~p"
- "~n ReqId: ~p"
- "~n Err2: ~p"
- "~n Index2: ~p"
- "~n", [Id, Type2, ReqId, Err2, Index2]),
+ ?EPRINT1("received unexpected pdu: ~w, ~w, ~w"
+ "~n Received Error: ~p"
+ "~n Received Index: ~p",
+ [Type2, Id, ReqId, Err2, Index2]),
{error, Id, {"Type: ~w, ErrStat: ~w, Idx: ~w, RequestId: ~w",
['inform-request', noError, 0, ReqId]},
{"Type: ~w, ErrStat: ~w, Idx: ~w", [Type2, Err2, Index2]}};
{error, Reason} ->
- io:format("expect_impl(~w, inform) -> receive failed"
- "~n Reason: ~p"
- "~n", [Id, Reason]),
+ ?EPRINT1("unexpected receive error: ~w"
+ "~n ~p", [Id, Reason]),
format_reason(Id, Reason)
end.
-expect_impl(Id, Err, Index, any) ->
- io:format("expect_impl(~w, any) -> entry with"
- "~n Err: ~p"
- "~n Index: ~p"
- "~n", [Id, Err, Index]),
+expect_impl(Id, Err, Index, any = _ExpectedVarbinds) ->
+ ?PRINT2("await response ~w with"
+ "~n Err: ~p"
+ "~n Index: ~p"
+ "~n ExpectedVarbinds: ~p",
+ [Id, Err, Index, _ExpectedVarbinds]),
case receive_response() of
#pdu{type = 'get-response',
error_status = Err,
error_index = Index} ->
- io:format("expect_impl(~w, any) -> received expected pdu"
- "~n", [Id]),
+ ?PRINT2("received expected response pdu (~w, ~w, ~w)",
+ [Id, Err, Index]),
ok;
- #pdu{type = 'get-response', error_status = Err} when (Index == any) ->
- io:format("expect_impl(~w, any) -> received expected pdu (any)"
- "~n", [Id]),
+ #pdu{type = 'get-response',
+ error_status = Err} when (Index == any) ->
+ ?PRINT2("received expected response pdu (~w, ~w)",
+ [Id, Err]),
ok;
#pdu{type = 'get-response',
request_id = ReqId,
error_status = Err,
error_index = Idx} when is_list(Index) ->
- io:format("expect_impl(~w, any) -> received pdu: "
- "~n ReqId: ~p"
- "~n Err: ~p"
- "~n Idx: ~p"
- "~n", [Id, ReqId, Err, Idx]),
case lists:member(Idx, Index) of
true ->
+ ?PRINT2("received expected response pdu (~w, ~w, ~w)",
+ [Id, Err, Idx]),
ok;
false ->
+ ?EPRINT1("received response pdu with unexpected index (~w, ~w):"
+ "~n Expected Index: ~p"
+ "~n Received Index: ~p",
+ [Id, Err, Index, Idx]),
{error, Id, {"ErrStat: ~w, Idx: ~w, RequestId: ~w",
[Err, Index, ReqId]},
{"ErrStat: ~w, Idx: ~w", [Err, Idx]}}
@@ -972,12 +1023,12 @@ expect_impl(Id, Err, Index, any) ->
request_id = ReqId,
error_status = Err2,
error_index = Index2} ->
- io:format("expect_impl(~w, any) -> received unexpected pdu: "
- "~n Type2: ~p"
- "~n ReqId: ~p"
- "~n Err2: ~p"
- "~n Index2: ~p"
- "~n", [Id, Type2, ReqId, Err2, Index2]),
+ ?EPRINT1("received unexpected response pdu: ~w, ~w, ~w"
+ "~n Expected Error: ~p"
+ "~n Received Error: ~p"
+ "~n Expected Index: ~p"
+ "~n Received Index: ~p",
+ [Type2, Id, ReqId, Err, Err2, Index, Index2]),
{error, Id, {"Type: ~w, ErrStat: ~w, Idx: ~w, RequestId: ~w",
['get-response', Err, Index, ReqId]},
{"Type: ~w, ErrStat: ~w, Idx: ~w", [Type2, Err2, Index2]}};
@@ -987,22 +1038,30 @@ expect_impl(Id, Err, Index, any) ->
end;
expect_impl(Id, Err, Index, ExpectedVarbinds) ->
- io:format("expect_impl(~w) -> entry with"
- "~n Err: ~p"
- "~n Index: ~p"
- "~n ExpectedVarbinds: ~p"
- "~n", [Id, Err, Index, ExpectedVarbinds]),
+ ?PRINT2("await response ~w with"
+ "~n Err: ~p"
+ "~n Index: ~p"
+ "~n ExpectedVarbinds: ~p",
+ [Id, Err, Index, ExpectedVarbinds]),
PureVBs = find_pure_oids(ExpectedVarbinds),
case receive_response() of
#pdu{type = 'get-response',
error_status = Err,
error_index = Index,
varbinds = VBs} ->
+ ?PRINT2("received expected response pdu (~w, ~w, ~w) - check varbinds"
+ "~n Expected VBs: ~p"
+ "~n Received VBs: ~p",
+ [Id, Err, Index, PureVBs, VBs]),
check_vars(Id, PureVBs, VBs);
#pdu{type = 'get-response',
error_status = Err,
varbinds = VBs} when (Index == any) ->
+ ?PRINT2("received expected response pdu (~w, ~w) - check varbinds"
+ "~n Expected VBs: ~p"
+ "~n Received VBs: ~p",
+ [Id, Err, PureVBs, VBs]),
check_vars(Id, PureVBs, VBs);
#pdu{type = 'get-response',
@@ -1012,8 +1071,18 @@ expect_impl(Id, Err, Index, ExpectedVarbinds) ->
varbinds = VBs} when is_list(Index) ->
case lists:member(Idx, Index) of
true ->
+ ?PRINT2("received expected pdu (~w, ~w, ~w) - check varbinds"
+ "~n Expected VBs: ~p"
+ "~n Received VBs: ~p",
+ [Id, Err, Idx, PureVBs, VBs]),
check_vars(Id, PureVBs, VBs);
false ->
+ ?EPRINT1("received response pdu with unexpected index (~w, ~w):"
+ "~n Expected Index: ~p"
+ "~n Received Index: ~p"
+ "~n Expected VBs: ~p"
+ "~n Received VBs: ~p",
+ [Id, Err, Index, Idx, PureVBs, VBs]),
{error,Id,
{"ErrStat: ~w, Idx: ~w, Varbinds: ~w, RequestId: ~w",
[Err,Index,PureVBs,ReqId]},
@@ -1026,29 +1095,65 @@ expect_impl(Id, Err, Index, ExpectedVarbinds) ->
error_status = Err2,
error_index = Index2,
varbinds = VBs} ->
+ ?EPRINT1("received unexpected response pdu: ~w, ~w, ~w"
+ "~n Expected Error: ~p"
+ "~n Received Error: ~p"
+ "~n Expected Index: ~p"
+ "~n Received Index: ~p"
+ "~n Expected VBs: ~p"
+ "~n Received VBs: ~p",
+ [Type2, Id, ReqId,
+ Err, Err2, Index, Index2, PureVBs, VBs]),
{error,Id,
{"Type: ~w, ErrStat: ~w, Idx: ~w, Varbinds: ~w, RequestId: ~w",
['get-response',Err,Index,PureVBs,ReqId]},
{"Type: ~w, ErrStat: ~w Idx: ~w Varbinds: ~w",
[Type2,Err2,Index2,VBs]}};
- {error, Reason} ->
+ {error, Reason} ->
+ ?EPRINT1("unexpected receive pdu error: ~w"
+ "~n ~p", [Id, Reason]),
format_reason(Id, Reason)
end.
expect_impl(Id, trap, Enterp, Generic, Specific, ExpectedVarbinds) ->
- PureE = find_pure_oid(Enterp),
+ ?PRINT2("await trap pdu ~w with"
+ "~n Enterprise: ~p"
+ "~n Generic: ~p"
+ "~n Specific: ~p"
+ "~n ExpectedVarbinds: ~p",
+ [Id, Enterp, Generic, Specific, ExpectedVarbinds]),
+ PureE = find_pure_oid(Enterp),
+ PureVBs = find_pure_oids(ExpectedVarbinds),
case receive_trap(3500) of
#trappdu{enterprise = PureE,
generic_trap = Generic,
specific_trap = Specific,
varbinds = VBs} ->
- check_vars(Id, find_pure_oids(ExpectedVarbinds), VBs);
+ ?PRINT2("received expected trap pdu - check varbinds"
+ "~n Expected VBs: ~p"
+ "~n Received VBs: ~p",
+ [PureVBs, VBs]),
+ check_vars(Id, PureVBs, VBs);
#trappdu{enterprise = Ent2,
generic_trap = G2,
specific_trap = Spec2,
varbinds = VBs} ->
+ ?EPRINT1("received unexpected trap pdu: ~w"
+ "~n Expected Enterprise: ~p"
+ "~n Received Enterprise: ~p"
+ "~n Expected Generic: ~p"
+ "~n Received Generic: ~p"
+ "~n Expected Specific: ~p"
+ "~n Received Specific: ~p"
+ "~n Expected VBs: ~p"
+ "~n Received VBs: ~p",
+ [Id,
+ PureE, Ent2,
+ Generic, G2,
+ Specific, Spec2,
+ PureVBs, VBs]),
{error, Id,
{"Enterprise: ~w, Generic: ~w, Specific: ~w, Varbinds: ~w",
[PureE, Generic, Specific, ExpectedVarbinds]},
@@ -1056,12 +1161,15 @@ expect_impl(Id, trap, Enterp, Generic, Specific, ExpectedVarbinds) ->
[Ent2, G2, Spec2, VBs]}};
{error, Reason} ->
+ ?EPRINT1("unexpected receive trap pdu error: ~w"
+ "~n ~p", [Id, Reason]),
format_reason(Id, Reason)
end.
format_reason(Id, Reason) ->
{error, Id, {"?", []}, {"~w", [Reason]}}.
+
%%----------------------------------------------------------------------
%% Args: Id, ExpectedVarbinds, GotVarbinds
%% Returns: ok
diff --git a/lib/snmp/vsn.mk b/lib/snmp/vsn.mk
index f305497cd3..2450b771f7 100644
--- a/lib/snmp/vsn.mk
+++ b/lib/snmp/vsn.mk
@@ -19,6 +19,6 @@
# %CopyrightEnd%
APPLICATION = snmp
-SNMP_VSN = 5.3
+SNMP_VSN = 5.4
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(SNMP_VSN)$(PRE_VSN)"
diff --git a/lib/ssh/Makefile b/lib/ssh/Makefile
index ab3948df75..b96cc2bbaa 100644
--- a/lib/ssh/Makefile
+++ b/lib/ssh/Makefile
@@ -37,5 +37,6 @@ SPECIAL_TARGETS =
#
include $(ERL_TOP)/make/otp_subdir.mk
+DIA_PLT_APPS=crypto runtime_tools public_key
include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml
index 60f20c7c3f..b9cb80806e 100644
--- a/lib/ssh/doc/src/notes.xml
+++ b/lib/ssh/doc/src/notes.xml
@@ -30,6 +30,86 @@
<file>notes.xml</file>
</header>
+<section><title>Ssh 4.8</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed wrong type definition for the daemon option
+ <c>subsystems</c>.</p>
+ <p>
+ Own Id: OTP-15820</p>
+ </item>
+ <item>
+ <p>
+ Fixed a possible SSH logging crash if there was a problem
+ in an early stage of session setup.</p>
+ <p>
+ Own Id: OTP-15962 Aux Id: ERL-990 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ The documentation for the modules ssh_connection,
+ ssh_sftp and ssh_sftpd are now generated from the
+ -spec:s.</p>
+ <p>
+ Own Id: OTP-15395</p>
+ </item>
+ <item>
+ <p>
+ Internal cleanup including removal of the internal file
+ <c>ssh_userauth.hrl</c>.</p>
+ <p>
+ Own Id: OTP-15876 Aux Id: PR-2255, PR-2256 </p>
+ </item>
+ <item>
+ <p>
+ Removed unused definitions in <c>ssh.hrl</c>.</p>
+ <p>
+ Own Id: OTP-15929 Aux Id: PR-2297 </p>
+ </item>
+ <item>
+ <p>
+ Removed unused fields in the internal
+ <c>#connection{}</c> record.</p>
+ <p>
+ Own Id: OTP-15984</p>
+ </item>
+ <item>
+ <p>
+ To get information of a <c>connection_ref()</c> from for
+ example <c>ssh:connect/3</c>, there was previously one
+ function available namely <c>ssh:connection_info/2</c>.
+ This ticket adds <c>ssh:connection_info/1</c> which
+ returns all information.</p>
+ <p>
+ For daemons (servers) started with for example
+ <c>ssh:daemon/2</c> the function <c>ssh:daemon_info/1</c>
+ returning all information was available. This ticket adds
+ <c>ssh:daemon_info/2</c> which returns only the
+ information specified in the second argument.</p>
+ <p>
+ The info of connections and of daemons now also includes
+ the item '<c>options</c>'. Only those options that does
+ not have their default values are returned.</p>
+ <p>
+ For a connection also the items '<c>algorithms</c>' and
+ '<c>channels</c>' are added.</p>
+ <p>
+ Own Id: OTP-16040</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Ssh 4.7.7</title>
<section><title>Improvements and New Features</title>
@@ -45,6 +125,22 @@
</section>
+<section><title>Ssh 4.7.6.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed a possible SSH logging crash if there was a problem
+ in an early stage of session setup.</p>
+ <p>
+ Own Id: OTP-15962 Aux Id: ERL-990 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Ssh 4.7.6</title>
<section><title>Improvements and New Features</title>
diff --git a/lib/ssh/doc/src/ssh.xml b/lib/ssh/doc/src/ssh.xml
index 8b7cb4dcd4..afcae71418 100644
--- a/lib/ssh/doc/src/ssh.xml
+++ b/lib/ssh/doc/src/ssh.xml
@@ -1036,6 +1036,34 @@
</desc>
</datatype>
+ <datatype>
+ <name name="connection_info_tuple"/>
+ <name name="version"/>
+ <name name="protocol_version"/>
+ <name name="software_version"/>
+ <name name="conn_info_algs"/>
+ <name name="conn_info_channels"/>
+ <desc>
+ <p>Return values from the
+ <seealso marker="#connection_info/1">connection_info/1</seealso> and
+ <seealso marker="#connection_info/2">connection_info/2</seealso> functions.
+ </p>
+ <p>In the <c>option</c> info tuple are only the options included that differs from the default values.
+ </p>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="daemon_info_tuple"/>
+ <desc>
+ <p>Return values from the
+ <seealso marker="#daemon_info/1">daemon_info/1</seealso> and
+ <seealso marker="#daemon_info/2">daemon_info/2</seealso> functions.
+ </p>
+ <p>In the <c>option</c> info tuple are only the options included that differs from the default values.
+ </p>
+ </desc>
+ </datatype>
<datatype>
<name>opaque_client_options</name>
@@ -1099,12 +1127,15 @@
<!-- CONNECTION_INFO/1, CONNECTION_INFO/2 -->
<func>
+ <name name="connection_info" arity="1" since=""/>
<name name="connection_info" arity="2" since=""/>
<fsummary>Retrieves information about a connection.</fsummary>
<desc>
- <p>Retrieves information about a connection. The list <c>Keys</c> defines which information that
- is returned.</p>
- </desc>
+ <p>Returns information about a connection intended for e.g debugging or logging.
+ </p>
+ <p>When the <c>Key</c> is a single <c>Item</c>, the result is a single <c>InfoTuple</c>
+ </p>
+ </desc>
</func>
<!-- DEAMON/1,2,3 -->
@@ -1156,9 +1187,16 @@
<!-- DAEMON_INFO/1 -->
<func>
<name name="daemon_info" arity="1" since="OTP 19.0"/>
+ <name name="daemon_info" arity="2" since=""/>
<fsummary>Get info about a daemon</fsummary>
<desc>
- <p>Returns a key-value list with information about the daemon.</p>
+ <p>Returns information about a daemon intended for e.g debugging or logging.
+ </p>
+ <p>When the <c>Key</c> is a single <c>Item</c>, the result is a single <c>InfoTuple</c>
+ </p>
+ <p>Note that <c>daemon_info/1</c> and <c>daemon_info/2</c> returns different types
+ due to compatibility reasons.
+ </p>
</desc>
</func>
diff --git a/lib/ssh/doc/src/ssh_client_channel.xml b/lib/ssh/doc/src/ssh_client_channel.xml
index cd28b95fd3..e6683dbd0b 100644
--- a/lib/ssh/doc/src/ssh_client_channel.xml
+++ b/lib/ssh/doc/src/ssh_client_channel.xml
@@ -150,12 +150,12 @@
<tag><c>{init_args(), list()}</c></tag>
<item><p>The list of arguments to the <c>init</c> function of the callback module.</p></item>
- <tag><c>{cm, ssh:connection_ref()}</c></tag>
+ <tag><c>{cm, </c><seealso marker="ssh:ssh#type-connection_ref">ssh:connection_ref()</seealso><c>}</c></tag>
<item><p>Reference to the <c>ssh</c> connection as returned by
<seealso marker="ssh#connect-3">ssh:connect/3</seealso>.
</p></item>
- <tag><c>{channel_id, ssh:channel_id()}</c></tag>
+ <tag><c>{channel_id, </c><seealso marker="ssh:ssh#type-channel_id">ssh:channel_id()</seealso><c>}</c></tag>
<item><p>Id of the <c>ssh</c> channel as returned by
<seealso marker="ssh_connection#session_channel/2">ssh_connection:session_channel/2,4</seealso>.
</p></item>
@@ -198,7 +198,7 @@
{ok, ChannelRef} | {error, Reason}</name>
<fsummary>Starts a process that handles an SSH channel.</fsummary>
<type>
- <v>SshConnection = ssh:connection_ref()</v>
+ <v>SshConnection = <seealso marker="ssh:ssh#type-connection_ref">ssh:connection_ref()</seealso></v>
<d>As returned by <seealso marker="ssh#connect-3">ssh:connect/3</seealso></d>
<v>ChannelId = <seealso marker="ssh#type-channel_id">ssh:channel_id()</seealso></v>
@@ -374,7 +374,7 @@
function and all channels are to handle the following message.</p>
<taglist>
- <tag><c>{ssh_channel_up, ssh:channel_id(), ssh:connection_ref()}</c></tag>
+ <tag><c>{ssh_channel_up, </c><seealso marker="ssh:ssh#type-channel_id">ssh:channel_id()</seealso><c>, </c><seealso marker="ssh:ssh#type-connection_ref">ssh:connection_ref()</seealso><c>}</c></tag>
<item><p>This is the first message that the channel receives.
It is sent just before the <seealso
marker="#init-1">init/1</seealso> function
@@ -393,21 +393,21 @@
ChannelId, State}</name>
<fsummary>Handles <c>ssh</c> connection protocol messages.</fsummary>
<type>
- <v>Msg = ssh_connection:event()</v>
+ <v>Msg = <seealso marker="ssh_connection#type-event">ssh_connection:event()</seealso></v>
<v>ChannelId = <seealso marker="ssh#type-channel_id">ssh:channel_id()</seealso></v>
<v>State = term()</v>
</type>
<desc>
<p>Handles SSH Connection Protocol messages that may need
service-specific attention. For details,
- see <seealso marker="ssh_connection"> ssh_connection:event()</seealso>.
+ see <seealso marker="ssh_connection#type-event">ssh_connection:event()</seealso>.
</p>
<p>The following message is taken care of by the
<c>ssh_client_channel</c> behavior.</p>
<taglist>
- <tag><c>{closed, ssh:channel_id()}</c></tag>
+ <tag><c>{closed, </c><seealso marker="ssh:ssh#type-channel_id">ssh:channel_id()</seealso><c>}</c></tag>
<item><p>The channel behavior sends a close message to the
other side, if such a message has not already been sent.
Then it terminates the channel with reason <c>normal</c>.</p></item>
diff --git a/lib/ssh/doc/src/ssh_connection.xml b/lib/ssh/doc/src/ssh_connection.xml
index 2a701929f6..9fa1da659c 100644
--- a/lib/ssh/doc/src/ssh_connection.xml
+++ b/lib/ssh/doc/src/ssh_connection.xml
@@ -40,128 +40,119 @@
<p>The <url href="http://www.ietf.org/rfc/rfc4254.txt">SSH Connection Protocol</url>
is used by clients and servers, that is, SSH channels, to communicate over the
SSH connection. The API functions in this module send SSH Connection Protocol events,
- which are received as messages by the remote channel.
- If the receiving channel is an Erlang process, the
- messages have the format
- <c><![CDATA[{ssh_cm, connection_ref(), ssh_event_msg()}]]></c>.
+ which are received as messages by the remote channel handling the remote channel.
+ The Erlang format of thoose messages is
+ (see also <seealso marker="#type-event">below</seealso>):
+ </p>
+ <p><c>{ssh_cm, </c><seealso marker="ssh:ssh#type-connection_ref">ssh:connection_ref()</seealso><c>, </c><seealso marker="#type-channel_msg"><c>channel_msg()</c></seealso><c>}</c>
+ </p>
+ <p>
If the <seealso marker="ssh_client_channel">ssh_client_channel</seealso> behavior is used to
implement the channel process, these messages are handled by
<seealso marker="ssh_client_channel#Module:handle_ssh_msg-2">handle_ssh_msg/2</seealso>.</p>
</description>
- <section>
- <title>DATA TYPES</title>
-
- <p>Type definitions that are used more than once in this module,
- or abstractions to indicate the intended use of the data
- type, or both:</p>
-
- <taglist>
- <tag><c>boolean() =</c></tag>
- <item><p><c>true | false </c></p></item>
- <tag><c>string() =</c></tag>
- <item><p>list of ASCII characters</p></item>
- <tag><c>timeout() =</c></tag>
- <item><p><c>infinity | integer()</c> in milliseconds</p></item>
- <tag><c>connection_ref() =</c></tag>
- <item><p>opaque() -as returned by
- <c>ssh:connect/3</c> or sent to an SSH channel processes</p></item>
- <tag><c>channel_id() =</c></tag>
- <item><p><c>integer()</c></p></item>
- <tag><c>ssh_data_type_code() =</c></tag>
- <item><p><c>1</c> ("stderr") | <c>0</c> ("normal") are
- valid values, see
- <url href="http://www.ietf.org/rfc/rfc4254.txt">RFC 4254</url> Section 5.2.</p></item>
- <tag><c>ssh_request_status() =</c></tag>
- <item><p> <c>success | failure</c></p></item>
- <tag><c>event() =</c></tag>
- <item><p><c>{ssh_cm, connection_ref(), ssh_event_msg()}</c></p></item>
- <tag><c>ssh_event_msg() =</c></tag>
- <item><p><c>data_events() | status_events() | terminal_events()</c></p></item>
- <tag><c>reason() =</c></tag>
- <item><p><c>timeout | closed</c></p></item>
- </taglist>
-
- <taglist>
- <tag><em>data_events()</em></tag>
- <item>
- <taglist>
- <tag><c><![CDATA[{data, channel_id(), ssh_data_type_code(), Data :: binary()}]]></c></tag>
- <item><p>Data has arrived on the channel. This event is sent as a
- result of calling <seealso marker="ssh_connection#send-3">
- ssh_connection:send/[3,4,5]</seealso>.</p></item>
-
- <tag><c><![CDATA[{eof, channel_id()}]]></c></tag>
- <item><p>Indicates that the other side sends no more data.
- This event is sent as a result of calling <seealso
- marker="ssh_connection#send_eof-2"> ssh_connection:send_eof/2</seealso>.
- </p></item>
- </taglist>
- </item>
+ <datatypes>
+ <datatype>
+ <name name="ssh_data_type_code"/>
+ <desc>
+ <p>The valid values are <c>0</c> ("normal") and <c>1</c> ("stderr"), see
+ <url href="https://tools.ietf.org/html/rfc4254#page-8">RFC 4254, Section 5.2</url>.</p>
+ </desc>
+ </datatype>
- <tag><em>status_events()</em></tag>
- <item>
+ <datatype>
+ <name name="result"/>
+ <name name="reason"/>
+ <desc>
+ <p>The result of a call.</p>
+ <p>If the request reached the peer, was handled and the response
+ reached the requesting node the <seealso marker="#type-req_status">req_status()</seealso>
+ is the status reported from the peer.</p>
+ <p>If not, the <seealso marker="#type-reason">reason()</seealso> indicates what went wrong:</p>
+ <taglist>
+ <tag><c>closed</c></tag>
+ <item>indicates that the channel or connection was closed when trying to send the request
+ </item>
+ <tag><c>timeout</c></tag>
+ <item>indicates that the operation exceeded a time limit
+ </item>
+ </taglist>
+ </desc>
+ </datatype>
- <taglist>
- <tag><c><![CDATA[{signal, channel_id(), ssh_signal()}]]></c></tag>
- <item><p>A signal can be delivered to the remote process/service
- using the following message. Some systems do not support
- signals, in which case they are to ignore this message. There is
- currently no function to generate this event as the signals
- referred to are on OS-level and not something generated by an
- Erlang program.</p></item>
+ <datatype>
+ <name name="req_status"/>
+ <desc>
+ <p>The status of a request.
+ Coresponds to the <c>SSH_MSG_CHANNEL_SUCCESS</c> and <c>SSH_MSG_CHANNEL_FAILURE</c> values in
+ <url href="https://tools.ietf.org/html/rfc4254#section-5.4">RFC 4254, Section 5.4</url>.
+ </p>
+ </desc>
+ </datatype>
- <tag><c><![CDATA[{exit_signal, channel_id(), ExitSignal :: string(), ErrorMsg ::string(),
- LanguageString :: string()}]]></c></tag>
+ <datatype_title>SSH Connection Protocol: General</datatype_title>
+ <datatype>
+ <name name="event"/>
+ <name name="channel_msg"/>
+ <desc>
+ <p>As mentioned in the introduction, the
+ <url href="https://tools.ietf.org/html/rfc4254">SSH Connection Protocol</url>
+ events are handled as messages. When writing a channel handling process without using
+ the support by the <seealso marker="ssh_client_channel">ssh_client_channel</seealso>
+ behavior the process must handle thoose messages.
+ </p>
+ </desc>
+ </datatype>
- <item><p>A remote execution can terminate violently because of a signal.
- Then this message can be received. For details on valid string
- values, see <url href="http://www.ietf.org/rfc/rfc4254.txt">RFC 4254</url>
- Section 6.10, which shows a special case of these signals.</p></item>
+ <datatype>
+ <name name="want_reply"/>
+ <desc>
+ <p>Messages that include a <c>WantReply</c> expect the channel handling
+ process to call <seealso marker="ssh_connection#reply_request-4">
+ ssh_connection:reply_request/4</seealso>
+ with the boolean value of <c>WantReply</c> as the second argument.</p>
+ </desc>
+ </datatype>
- <tag><c><![CDATA[{exit_status, channel_id(), ExitStatus :: integer()}]]></c></tag>
- <item><p>When the command running at the other end terminates, the
- following message can be sent to return the exit status of the
- command. A zero <c>exit_status</c> usually means that the command
- terminated successfully. This event is sent as a result of calling
- <seealso marker="ssh_connection#exit_status-3">
- ssh_connection:exit_status/3</seealso>.</p></item>
- <tag><c><![CDATA[{closed, channel_id()}]]></c></tag>
- <item><p>This event is sent as a result of calling
- <seealso marker="ssh_connection#close-2">ssh_connection:close/2</seealso>.
- Both the handling of this event and sending it are taken care of by the
- <seealso marker="ssh_client_channel">ssh_client_channel</seealso> behavior.</p></item>
-
- </taglist>
- </item>
+ <datatype_title>Data Transfer (RFC 4254, section 5.2)</datatype_title>
+ <datatype>
+ <name name="data_ch_msg"/>
+ <desc>
+ <p>Data has arrived on the channel. This event is sent as a result of calling
+ <seealso marker="ssh_connection#send-3"> ssh_connection:send/[3,4,5]</seealso>.
+ </p>
+ </desc>
+ </datatype>
- <tag><em>terminal_events()</em></tag>
- <item>
- <p>Channels implementing a shell and command execution on the
- server side are to handle the following messages that can be sent by client-
- channel processes.</p>
+ <datatype_title>Closing a Channel (RFC 4254, section 5.3)</datatype_title>
+ <datatype>
+ <name name="eof_ch_msg"/>
+ <desc>
+ <p>Indicates that the other side sends no more data. This event is sent as a result of calling
+ <seealso marker="ssh_connection#send_eof-2"> ssh_connection:send_eof/2</seealso>.
+ </p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="closed_ch_msg"/>
+ <desc>
+ <p>This event is sent as a result of calling
+ <seealso marker="ssh_connection#close-2">ssh_connection:close/2</seealso>.
+ Both the handling of this event and sending it are taken care of by the
+ <seealso marker="ssh_client_channel">ssh_client_channel</seealso> behavior.</p>
+ </desc>
+ </datatype>
- <p>Events that include a <c>WantReply</c> expect the event handling
- process to call <seealso marker="ssh_connection#reply_request-4">
- ssh_connection:reply_request/4</seealso>
- with the boolean value of <c>WantReply</c> as the second argument.</p>
- <taglist>
- <tag><c><![CDATA[{env, channel_id(), WantReply :: boolean(),
- Var ::string(), Value :: string()}]]></c></tag>
- <item><p>Environment variables can be passed to the shell/command
- to be started later. This event is sent as a result of calling <seealso
- marker="ssh_connection#setenv-5"> ssh_connection:setenv/5</seealso>.
- </p></item>
-
- <tag><c><![CDATA[{pty, channel_id(),
- WantReply :: boolean(), {Terminal :: string(), CharWidth :: integer(),
- RowHeight :: integer(), PixelWidth :: integer(), PixelHeight :: integer(),
- TerminalModes :: [{Opcode :: atom() | integer(),
- Value :: integer()}]}}]]></c></tag>
- <item><p>A pseudo-terminal has been requested for the
+ <datatype_title>Requesting a Pseudo-Terminal (RFC 4254, section 6.2)</datatype_title>
+ <datatype>
+ <name name="pty_ch_msg"/>
+ <name name="term_mode"/>
+ <desc>
+ <p>A pseudo-terminal has been requested for the
session. <c>Terminal</c> is the value of the TERM environment
variable value, that is, <c>vt100</c>. Zero dimension parameters must
be ignored. The character/row dimensions override the pixel
@@ -169,46 +160,103 @@
drawable area of the window. <c>Opcode</c> in the
<c>TerminalModes</c> list is the mnemonic name, represented
as a lowercase Erlang atom, defined in
- <url href="http://www.ietf.org/rfc/rfc4254.txt">RFC 4254</url>, Section 8.
+ <url href="https://tools.ietf.org/html/rfc4254#section-8">RFC 4254</url>, Section 8.
It can also be an <c>Opcode</c> if the mnemonic name is not listed in the
RFC. Example: <c>OP code: 53, mnemonic name ECHO erlang atom:
echo</c>. This event is sent as a result of calling <seealso
- marker="ssh_connection#ptty_alloc/4">ssh_connection:ptty_alloc/4</seealso>.</p></item>
+ marker="ssh_connection#ptty_alloc/4">ssh_connection:ptty_alloc/4</seealso>.</p>
+ </desc>
+ </datatype>
+
- <tag><c><![CDATA[{shell, WantReply :: boolean()}]]></c></tag>
- <item><p>This message requests that the user default shell
+ <datatype_title>Environment Variable Passing (RFC 4254, section 6.4)</datatype_title>
+ <datatype>
+ <name name="env_ch_msg"/>
+ <desc>
+ <p>Environment variables can be passed to the shell/command
+ to be started later. This event is sent as a result of calling <seealso
+ marker="ssh_connection#setenv-5"> ssh_connection:setenv/5</seealso>.
+ </p>
+ </desc>
+ </datatype>
+
+
+ <datatype_title>Starting a Shell or Command (RFC 4254, section 6.5)</datatype_title>
+ <datatype>
+ <name name="shell_ch_msg"/>
+ <desc>
+ <p>This message requests that the user default shell
is started at the other end. This event is sent as a result of calling
<seealso marker="ssh_connection#shell-2"> ssh_connection:shell/2</seealso>.
- </p></item>
+ </p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="exec_ch_msg"/>
+ <desc>
+ <p>This message requests that the server starts
+ execution of the given command. This event is sent as a result of calling <seealso
+ marker="ssh_connection#exec-4">ssh_connection:exec/4 </seealso>.
+ </p>
+ </desc>
+ </datatype>
+
- <tag><c><![CDATA[{window_change, channel_id(), CharWidth() :: integer(),
- RowHeight :: integer(), PixWidth :: integer(), PixHeight :: integer()}]]></c></tag>
- <item><p>When the window (terminal) size changes on the client
+ <datatype_title>Window Dimension Change Message (RFC 4254, section 6.7)</datatype_title>
+ <datatype>
+ <name name="window_change_ch_msg"/>
+ <desc>
+ <p>When the window (terminal) size changes on the client
side, it <em>can</em> send a message to the server side to inform it of
- the new dimensions. No API function generates this event.</p></item>
+ the new dimensions. No API function generates this event.</p>
+ </desc>
+ </datatype>
- <tag><c><![CDATA[{exec, channel_id(),
- WantReply :: boolean(), Cmd :: string()}]]></c></tag>
- <item><p>This message requests that the server starts
- execution of the given command. This event is sent as a result of calling <seealso
- marker="ssh_connection#exec-4">ssh_connection:exec/4 </seealso>.
- </p></item>
- </taglist>
- </item>
- </taglist>
- </section>
+ <datatype_title>Signals (RFC 4254, section 6.9)</datatype_title>
+ <datatype>
+ <name name="signal_ch_msg"/>
+ <desc>
+ <p>A signal can be delivered to the remote process/service
+ using the following message. Some systems do not support
+ signals, in which case they are to ignore this message. There is
+ currently no function to generate this event as the signals
+ referred to are on OS-level and not something generated by an
+ Erlang program.</p>
+ </desc>
+ </datatype>
+
+
+ <datatype_title>Returning Exit Status (RFC 4254, section 6.10)</datatype_title>
+ <datatype>
+ <name name="exit_status_ch_msg"/>
+ <desc>
+ <p>When the command running at the other end terminates, the
+ following message can be sent to return the exit status of the
+ command. A zero <c>exit_status</c> usually means that the command
+ terminated successfully. This event is sent as a result of calling
+ <seealso marker="ssh_connection#exit_status-3">
+ ssh_connection:exit_status/3</seealso>.</p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="exit_signal_ch_msg"/>
+ <desc>
+ <p>A remote execution can terminate violently because of a signal.
+ Then this message can be received. For details on valid string
+ values, see <url href="https://tools.ietf.org/html/rfc4254#section-6.10">RFC 4254</url>
+ Section 6.10, which shows a special case of these signals.</p>
+ </desc>
+ </datatype>
+
+ </datatypes>
+
<funcs>
<func>
- <name since="">adjust_window(ConnectionRef, ChannelId, NumOfBytes) -> ok</name>
+ <name since="" name="adjust_window" arity="3"/>
<fsummary>Adjusts the SSH flow control window.</fsummary>
- <type>
- <v>ConnectionRef = connection_ref()</v>
- <v>ChannelId = channel_id()</v>
- <v>NumOfBytes = integer()</v>
- </type>
- <desc>
+ <desc>
<p>Adjusts the SSH flow control window. This is to be done by both the
client- and server-side channel processes.</p>
@@ -221,17 +269,12 @@
</func>
<func>
- <name since="">close(ConnectionRef, ChannelId) -> ok</name>
+ <name since="" name="close" arity="2"/>
<fsummary>Sends a close message on the channel <c>ChannelId</c>.</fsummary>
- <type>
- <v>ConnectionRef = connection_ref()</v>
- <v>ChannelId = channel_id()</v>
- </type>
<desc>
<p>A server- or client-channel process can choose to close their session by
sending a close event.
</p>
-
<note><p>This function is called by the <c>ssh_client_channel</c>
behavior when the channel is terminated, see <seealso
marker="ssh_client_channel"> ssh_client_channel(3)</seealso>. Thus, channels implemented
@@ -240,57 +283,61 @@
</func>
<func>
- <name since="">exec(ConnectionRef, ChannelId, Command, TimeOut) -> ssh_request_status() |
- {error, reason()}</name>
+ <name since="" name="exec" arity="4"/>
<fsummary>Requests that the server starts the execution of the given command.</fsummary>
- <type>
- <v>ConnectionRef = connection_ref()</v>
- <v>ChannelId = channel_id()</v>
- <v>Command = string()</v>
- <v>Timeout = timeout()</v>
- </type>
<desc>
<p>Is to be called by a client-channel process to request that the server starts
executing the given command. The result is several messages according to the
following pattern. The last message is a channel close message, as the <c>exec</c>
request is a one-time execution that closes the channel when it is done.</p>
- <taglist>
- <tag><c>N x {ssh_cm, connection_ref(),
- {data, channel_id(), ssh_data_type_code(), Data :: binary()}}</c></tag>
+ <!--taglist>
+ <tag><c>N x {ssh_cm, </c><seealso marker="ssh:ssh#type-connection_ref">ssh:connection_ref()</seealso><c>, {data, </c><seealso marker="ssh:ssh#type-channel_id">ssh:channel_id()</seealso><c>, </c><seealso marker="#type-ssh_data_type_code">ssh_data_type_code()</seealso><c>, Data :: binary()}}</c></tag>
<item><p>The result of executing the command can be only one line
or thousands of lines depending on the command.</p></item>
- <tag><c>0 or 1 x {ssh_cm, connection_ref(), {eof, channel_id()}}</c></tag>
+ <tag><c>0 or 1 x {ssh_cm, </c><seealso marker="ssh:ssh#type-connection_ref">ssh:connection_ref()</seealso><c>, {eof, </c><seealso marker="ssh:ssh#type-channel_id">ssh:channel_id()</seealso><c>}}</c></tag>
<item><p>Indicates that no more data is to be sent.</p></item>
- <tag><c>0 or 1 x {ssh_cm,
- connection_ref(), {exit_signal,
- channel_id(), ExitSignal :: string(), ErrorMsg :: string(), LanguageString :: string()}}</c></tag>
+ <tag><c>0 or 1 x {ssh_cm, </c><seealso marker="ssh:ssh#type-connection_ref">ssh:connection_ref()</seealso><c>, {exit_signal, </c><seealso marker="ssh:ssh#type-channel_id">ssh:channel_id()</seealso><c>, ExitSignal :: string(), ErrorMsg :: string(), LanguageString :: string()}}</c></tag>
<item><p>Not all systems send signals. For details on valid string
values, see RFC 4254, Section 6.10</p></item>
- <tag><c>0 or 1 x {ssh_cm, connection_ref(), {exit_status,
- channel_id(), ExitStatus :: integer()}}</c></tag>
+ <tag><c>0 or 1 x {ssh_cm, </c><seealso marker="ssh:ssh#type-connection_ref">ssh:connection_ref()</seealso><c>, {exit_status, </c><seealso marker="ssh:ssh#type-channel_id">ssh:channel_id()</seealso><c>, ExitStatus :: integer()}}</c></tag>
<item><p>It is recommended by the SSH Connection Protocol to send this
message, but that is not always the case.</p></item>
- <tag><c>1 x {ssh_cm, connection_ref(),
- {closed, channel_id()}}</c></tag>
+ <tag><c>1 x {ssh_cm, </c><seealso marker="ssh:ssh#type-connection_ref">ssh:connection_ref()</seealso><c>, {closed, </c><seealso marker="ssh:ssh#type-channel_id">ssh:channel_id()</seealso><c>}}</c></tag>
<item><p>Indicates that the <c>ssh_client_channel</c> started for the
execution of the command has now been shut down.</p></item>
+ </taglist-->
+
+ <taglist>
+ <tag>N x <seealso marker="#type-data_ch_msg">data message(s)</seealso></tag>
+ <item><p>The result of executing the command can be only one line
+ or thousands of lines depending on the command.</p></item>
+
+ <tag>0 or 1 x <seealso marker="#type-eof_ch_msg">eof message</seealso></tag>
+ <item><p>Indicates that no more data is to be sent.</p></item>
+
+ <tag>0 or 1 x <seealso marker="#type-exit_signal_ch_msg">exit signal message</seealso></tag>
+ <item><p>Not all systems send signals. For details on valid string
+ values, see RFC 4254, Section 6.10</p></item>
+
+ <tag>0 or 1 x <seealso marker="#type-exit_status_ch_msg">exit status message</seealso></tag>
+ <item><p>It is recommended by the SSH Connection Protocol to send this
+ message, but that is not always the case.</p></item>
+
+ <tag>1 x <seealso marker="#type-closed_ch_msg">closed status message</seealso></tag>
+ <item><p>Indicates that the <c>ssh_client_channel</c> started for the
+ execution of the command has now been shut down.</p></item>
</taglist>
</desc>
</func>
<func>
- <name since="">exit_status(ConnectionRef, ChannelId, Status) -> ok</name>
+ <name since="" name="exit_status" arity="3"/>
<fsummary>Sends the exit status of a command to the client.</fsummary>
- <type>
- <v>ConnectionRef = connection_ref() </v>
- <v>ChannelId = channel_id()</v>
- <v>Status = integer()</v>
- </type>
<desc>
<p>Is to be called by a server-channel process to send the exit status of a command
to the client.</p>
@@ -298,16 +345,10 @@
</func>
<func>
- <name since="OTP 17.5">ptty_alloc(ConnectionRef, ChannelId, Options) -></name>
- <name since="OTP 17.4">ptty_alloc(ConnectionRef, ChannelId, Options, Timeout) -> > ssh_request_status() |
- {error, reason()}</name>
+ <name since="OTP 17.5" name="ptty_alloc" arity="3"/>
+ <name since="OTP 17.4" name="ptty_alloc" arity="4"/>
<fsummary>Sends an SSH Connection Protocol <c>pty_req</c>,
to allocate a pseudo-terminal.</fsummary>
- <type>
- <v>ConnectionRef = connection_ref()</v>
- <v>ChannelId = channel_id()</v>
- <v>Options = proplists:proplist()</v>
- </type>
<desc>
<p>Sends an SSH Connection Protocol <c>pty_req</c>, to allocate a pseudo-terminal.
Is to be called by an SSH client process.</p>
@@ -339,14 +380,8 @@
</func>
<func>
- <name since="">reply_request(ConnectionRef, WantReply, Status, ChannelId) -> ok</name>
+ <name since="" name="reply_request" arity="4"/>
<fsummary>Sends status replies to requests that want such replies.</fsummary>
- <type>
- <v>ConnectionRef = connection_ref()</v>
- <v>WantReply = boolean()</v>
- <v>Status = ssh_request_status()</v>
- <v>ChannelId = channel_id()</v>
- </type>
<desc>
<p>Sends status replies to requests where the requester has
stated that it wants a status report, that is, <c>WantReply = true</c>.
@@ -361,14 +396,15 @@
<name since="">send(ConnectionRef, ChannelId, Data, Timeout) -></name>
<name since="">send(ConnectionRef, ChannelId, Type, Data) -></name>
<name since="">send(ConnectionRef, ChannelId, Type, Data, TimeOut) ->
- ok | {error, timeout} | {error, closed}</name>
+ ok | Error</name>
<fsummary>Sends channel data.</fsummary>
<type>
- <v>ConnectionRef = connection_ref()</v>
- <v>ChannelId = channel_id()</v>
+ <v>ConnectionRef = <seealso marker="ssh:ssh#type-connection_ref">ssh:connection_ref()</seealso></v>
+ <v>ChannelId = <seealso marker="ssh:ssh#type-channel_id">ssh:channel_id()</seealso></v>
<v>Data = binary()</v>
- <v>Type = ssh_data_type_code()</v>
+ <v>Type = <seealso marker="#type-ssh_data_type_code">ssh_data_type_code()</seealso></v>
<v>Timeout = timeout()</v>
+ <v>Error = {error, <seealso marker="#type-reason">reason()</seealso>}</v>
</type>
<desc>
<p>Is to be called by client- and server-channel processes to send data to each other.
@@ -380,29 +416,17 @@
</func>
<func>
- <name since="">send_eof(ConnectionRef, ChannelId) -> ok | {error, closed}</name>
+ <name since="" name="send_eof" arity="2"/>
<fsummary>Sends EOF on channel <c>ChannelId</c>.</fsummary>
- <type>
- <v>ConnectionRef = connection_ref()</v>
- <v>ChannelId = channel_id()</v>
- </type>
<desc>
<p>Sends EOF on channel <c>ChannelId</c>.</p>
</desc>
</func>
<func>
- <name since="">session_channel(ConnectionRef, Timeout) -></name>
- <name since="">session_channel(ConnectionRef, InitialWindowSize,
- MaxPacketSize, Timeout) -> {ok, channel_id()} | {error, reason()}</name>
+ <name since="" name="session_channel" arity="2"/>
+ <name since="" name="session_channel" arity="4"/>
<fsummary>Opens a channel for an SSH session.</fsummary>
- <type>
- <v>ConnectionRef = connection_ref()</v>
- <v>InitialWindowSize = integer()</v>
- <v>MaxPacketSize = integer()</v>
- <v>Timeout = timeout()</v>
- <v>Reason = term()</v>
- </type>
<desc>
<p>Opens a channel for an SSH session. The channel id returned from this function
is the id used as input to the other functions in this module.</p>
@@ -410,17 +434,9 @@
</func>
<func>
- <name since="">setenv(ConnectionRef, ChannelId, Var, Value, TimeOut) -> ssh_request_status() |
- {error, reason()}</name>
+ <name since="" name="setenv" arity="5"/>
<fsummary>Environment variables can be passed to the
shell/command to be started later.</fsummary>
- <type>
- <v>ConnectionRef = connection_ref()</v>
- <v>ChannelId = channel_id()</v>
- <v>Var = string()</v>
- <v>Value = string()</v>
- <v>Timeout = timeout()</v>
- </type>
<desc>
<p>Environment variables can be passed before starting the
shell/command. Is to be called by a client channel processes.</p>
@@ -428,14 +444,9 @@
</func>
<func>
- <name since="">shell(ConnectionRef, ChannelId) -> ok | failure | {error, closed}
- </name>
+ <name since="" name="shell" arity="2"/>
<fsummary>Requests that the user default shell (typically defined in
/etc/passwd in Unix systems) is to be executed at the server end.</fsummary>
- <type>
- <v>ConnectionRef = connection_ref()</v>
- <v>ChannelId = channel_id()</v>
- </type>
<desc>
<p>Is to be called by a client channel process to request that the user default
shell (typically defined in /etc/passwd in Unix systems) is executed
@@ -448,15 +459,8 @@
</func>
<func>
- <name since="">subsystem(ConnectionRef, ChannelId, Subsystem, Timeout) -> ssh_request_status() |
- {error, reason()}</name>
+ <name since="" name="subsystem" arity="4"/>
<fsummary>Requests to execute a predefined subsystem on the server.</fsummary>
- <type>
- <v>ConnectionRef = connection_ref()</v>
- <v>ChannelId = channel_id()</v>
- <v>Subsystem = string()</v>
- <v>Timeout = timeout()</v>
- </type>
<desc>
<p>Is to be called by a client-channel process for requesting to execute a predefined
subsystem on the server.
diff --git a/lib/ssh/doc/src/ssh_server_channel.xml b/lib/ssh/doc/src/ssh_server_channel.xml
index a4e18bbfbf..87c745c9fb 100644
--- a/lib/ssh/doc/src/ssh_server_channel.xml
+++ b/lib/ssh/doc/src/ssh_server_channel.xml
@@ -112,7 +112,7 @@
function and all channels are to handle the following message.</p>
<taglist>
- <tag><c>{ssh_channel_up, ssh:channel_id(), ssh:connection_ref()}</c></tag>
+ <tag><c>{ssh_channel_up, </c><seealso marker="ssh:ssh#type-channel_id">ssh:channel_id()</seealso><c>, </c><seealso marker="ssh:ssh#type-connection_ref">ssh:connection_ref()</seealso><c>}</c></tag>
<item><p>This is the first message that the channel receives.
This is especially useful if the
server wants to send a message to the client without first
@@ -129,21 +129,21 @@
ChannelId, State}</name>
<fsummary>Handles <c>ssh</c> connection protocol messages.</fsummary>
<type>
- <v>Msg = ssh_connection:event()</v>
+ <v>Msg = <seealso marker="ssh_connection#type-event">ssh_connection:event()</seealso></v>
<v>ChannelId = <seealso marker="ssh#type-channel_id">ssh:channel_id()</seealso></v>
<v>State = term()</v>
</type>
<desc>
<p>Handles SSH Connection Protocol messages that may need
service-specific attention. For details,
- see <seealso marker="ssh_connection"> ssh_connection:event()</seealso>.
+ see <seealso marker="ssh_connection#type-event">ssh_connection:event()</seealso>.
</p>
<p>The following message is taken care of by the
<c>ssh_server_channel</c> behavior.</p>
<taglist>
- <tag><c>{closed, ssh:channel_id()}</c></tag>
+ <tag><c>{closed, </c><seealso marker="ssh:ssh#type-channel_id">ssh:channel_id()</seealso><c>}</c></tag>
<item><p>The channel behavior sends a close message to the
other side, if such a message has not already been sent.
Then it terminates the channel with reason <c>normal</c>.</p></item>
diff --git a/lib/ssh/doc/src/ssh_sftp.xml b/lib/ssh/doc/src/ssh_sftp.xml
index c89092798d..f9f1e0953b 100644
--- a/lib/ssh/doc/src/ssh_sftp.xml
+++ b/lib/ssh/doc/src/ssh_sftp.xml
@@ -37,60 +37,112 @@
SSH.</p>
</description>
- <section>
- <title>DATA TYPES</title>
- <p>Type definitions that are used more than once in this module,
- or abstractions to indicate the intended use of the data type, or both:
- </p>
-
- <taglist>
- <tag><c>reason()</c></tag>
- <item>
- <p>= <c>atom() | string() | tuple() </c>A description of the reason why an operation failed.</p>
- <p>
- The <c>atom()</c> value is formed from the sftp error codes in the protocol-level responses as defined in
- <url href="https://tools.ietf.org/id/draft-ietf-secsh-filexfer-13.txt">draft-ietf-secsh-filexfer-13.txt</url>
- section 9.1.
- </p>
- <p>
+ <datatypes>
+ <datatype>
+ <name name="sftp_option"/>
+ <desc>
+ </desc>
+ </datatype>
+
+ <datatype_title>Error cause</datatype_title>
+ <datatype>
+ <name name="reason"/>
+ <desc>
+ <p>A description of the reason why an operation failed.</p>
+ <p>The <c>atom()</c> value is formed from the sftp error codes in the protocol-level responses as defined in
+ <url href="https://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#page-49">draft-ietf-secsh-filexfer-13</url>
+ section 9.1.
The codes are named as <c>SSH_FX_*</c> which are transformed into lowercase of the star-part.
E.g. the error code <c>SSH_FX_NO_SUCH_FILE</c>
will cause the <c>reason()</c> to be <c>no_such_file</c>.
</p>
<p>The <c>string()</c> reason is the error information from the server in case of an exit-signal. If that information is empty, the reason is the exit signal name.
</p>
- <p>The <c>tuple()</c> reason are other errors like the <c>{exit_status,integer()}</c> if the exit status is not 0.
+ <p>The <c>tuple()</c> reason are other errors like for example <c>{exit_status,1}</c>.
</p>
- </item>
+ </desc>
+ </datatype>
- <tag><c>connection_ref() =</c></tag>
- <item><p><c>opaque()</c> - as returned by
- <seealso marker="ssh#connect-3"><c>ssh:connect/3</c></seealso></p></item>
+ <datatype_title>Crypto operations for open_tar</datatype_title>
+ <datatype>
+ <name name="tar_crypto_spec"/>
+ <name name="encrypt_spec"/>
+ <name name="decrypt_spec"/>
+ <desc>
+ <p>Specifies the encryption or decryption applied to tar files when using
+ <seealso marker="#open_tar/3">open_tar/3</seealso> or
+ <seealso marker="#open_tar/4">open_tar/4</seealso>.
+ </p>
+ <p>The encryption or decryption is applied to the generated stream of
+ bytes prior to sending the resulting stream to the SFTP server.
+ </p>
+ <p>For code examples see Section
+ <seealso marker="using_ssh#example-with-encryption">Example with encryption</seealso>
+ in the ssh Users Guide.
+ </p>
+ </desc>
+ </datatype>
- <tag><c>timeout()</c></tag>
- <item><p>= <c>infinity | integer()</c> in milliseconds. Default infinity.</p></item>
- </taglist>
- </section>
+ <datatype>
+ <name name="init_fun"/>
+ <name name="chunk_size"/>
+ <name name="crypto_state"/>
+ <desc>
+ <p>The <c>init_fun()</c> in the
+ <seealso marker="#type-tar_crypto_spec">tar_crypto_spec</seealso>
+ is applied once prior to any other <c>crypto</c>
+ operation. The intention is that this function initiates the encryption or
+ decryption for example by calling
+ <seealso marker="crypto:crypto#crypto_init/4">crypto:crypto_init/4</seealso>
+ or similar. The <c>crypto_state()</c> is the state such a function may return.
+ </p>
+ <p>If the selected cipher needs to have the input data partioned into
+ blocks of a certain size, the <c>init_fun()</c> should return the second
+ form of return value with the <c>chunk_size()</c> set to the block size.
+ If the <c>chunk_size()</c> is <c>undefined</c>, the size of the <c>PlainBin</c>s varies,
+ because this is intended for stream crypto, whereas a fixed <c>chunk_size()</c> is intended for block crypto.
+ A <c>chunk_size()</c> can be changed in the return from the <c>crypto_fun()</c>.
+ The value can be changed between <c>pos_integer()</c> and <c>undefined</c>.
+ </p>
+ </desc>
+ </datatype>
- <section>
- <title>Time-outs</title>
- <p>If the request functions for the SFTP channel return <c>{error, timeout}</c>,
- no answer was received from the server within the expected time.</p>
- <p>The request may have reached the server and may have been performed.
- However, no answer was received from the server within the expected time.</p>
- </section>
+ <datatype>
+ <name name="crypto_fun"/>
+ <name name="crypto_result"/>
+ <desc>
+ <p>The initial <c>crypto_state()</c> returned from the
+ <seealso marker="#type-init_fun">init_fun()</seealso>
+ is folded into repeated applications of the <c>crypto_fun()</c> in the
+ <seealso marker="#type-tar_crypto_spec">tar_crypto_spec</seealso>.
+ The binary returned from that fun is sent to the remote SFTP server and
+ the new <c>crypto_state()</c> is used in the next call of the
+ <c>crypto_fun()</c>.
+ </p>
+ <p>If the <c>crypto_fun()</c> reurns a <c>chunk_size()</c>, that value
+ is as block size for further blocks in calls to <c>crypto_fun()</c>.
+ </p>
+ </desc>
+ </datatype>
+
+ <datatype>
+ <name name="final_fun"/>
+ <desc>
+ <p>If doing encryption,
+ the <c>final_fun()</c> in the
+ <seealso marker="#type-tar_crypto_spec">tar_crypto_spec</seealso>
+ is applied to the last piece of data.
+ The <c>final_fun()</c> is responsible for padding (if needed) and
+ encryption of that last piece.
+ </p>
+ </desc>
+ </datatype>
+ </datatypes>
<funcs>
<func>
- <name since="">apread(ChannelPid, Handle, Position, Len) -> {async, N} | {error, reason()}</name>
+ <name name="apread" arity="4" since=""/>
<fsummary>Reads asynchronously from an open file.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- <v>Handle = term()</v>
- <v>Position = integer()</v>
- <v>Len = integer()</v>
- <v>N = term()</v>
- </type>
<desc><p>The <c><![CDATA[apread/4]]></c> function reads from a specified position,
combining the <seealso marker="#position-3"><c>position/3</c></seealso> and
<seealso marker="#aread-3"><c>aread/3</c></seealso> functions.</p>
@@ -98,17 +150,8 @@
</func>
<func>
- <name since="">apwrite(ChannelPid, Handle, Position, Data) -> {async, N} | {error, reason()}</name>
+ <name name="apwrite" arity="4" since=""/>
<fsummary>Writes asynchronously to an open file.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- <v>Handle = term()</v>
- <v>Position = integer()</v>
- <v>Len = integer()</v>
- <v>Data = binary()</v>
- <v>Timeout = timeout()</v>
- <v>N = term()</v>
- </type>
<desc><p>The <c><![CDATA[apwrite/4]]></c> function writes to a specified position,
combining the <seealso marker="#position-3"><c>position/3</c></seealso> and
<seealso marker="#awrite-3"><c>awrite/3</c></seealso> functions.</p>
@@ -116,15 +159,8 @@
</func>
<func>
- <name since="">aread(ChannelPid, Handle, Len) -> {async, N} | {error, reason()}</name>
+ <name name="aread" arity="3" since=""/>
<fsummary>Reads asynchronously from an open file.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- <v>Handle = term()</v>
- <v>Position = integer()</v>
- <v>Len = integer()</v>
- <v>N = term()</v>
- </type>
<desc>
<p>Reads from an open file, without waiting for the result. If the
handle is valid, the function returns <c><![CDATA[{async, N}]]></c>, where <c>N</c>
@@ -137,16 +173,8 @@
</func>
<func>
- <name since="">awrite(ChannelPid, Handle, Data) -> {async, N} | {error, reason()}</name>
+ <name name="awrite" arity="3" since=""/>
<fsummary>Writes asynchronously to an open file.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- <v>Handle = term()</v>
- <v>Position = integer()</v>
- <v>Len = integer()</v>
- <v>Data = binary()</v>
- <v>Timeout = timeout()</v>
- </type>
<desc>
<p>Writes to an open file, without waiting for the result. If the
handle is valid, the function returns <c><![CDATA[{async, N}]]></c>, where <c>N</c>
@@ -159,28 +187,18 @@
</func>
<func>
- <name since="">close(ChannelPid, Handle) -></name>
- <name since="">close(ChannelPid, Handle, Timeout) -> ok | {error, reason()}</name>
+ <name name="close" arity="2" since=""/>
+ <name name="close" arity="3" since=""/>
<fsummary>Closes an open handle.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- <v>Handle = term()</v>
- <v>Timeout = timeout()</v>
- </type>
<desc>
<p>Closes a handle to an open file or directory on the server.</p>
</desc>
</func>
<func>
- <name since="">delete(ChannelPid, Name) -></name>
- <name since="">delete(ChannelPid, Name, Timeout) -> ok | {error, reason()}</name>
+ <name name="delete" arity="2" since=""/>
+ <name name="delete" arity="3" since=""/>
<fsummary>Deletes a file.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- <v>Name = string()</v>
- <v>Timeout = timeout()</v>
- </type>
<desc>
<p>Deletes the file specified by <c><![CDATA[Name]]></c>.
</p>
@@ -188,14 +206,9 @@
</func>
<func>
- <name since="">del_dir(ChannelPid, Name) -></name>
- <name since="">del_dir(ChannelPid, Name, Timeout) -> ok | {error, reason()}</name>
+ <name name="del_dir" arity="2" since=""/>
+ <name name="del_dir" arity="3" since=""/>
<fsummary>Deletes an empty directory.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- <v>Name = string()</v>
- <v>Timeout = timeout()</v>
- </type>
<desc>
<p>Deletes a directory specified by <c><![CDATA[Name]]></c>.
The directory must be empty before it can be successfully deleted.
@@ -204,16 +217,9 @@
</func>
<func>
- <name since="">list_dir(ChannelPid, Path) -></name>
- <name since="">list_dir(ChannelPid, Path, Timeout) -> {ok, Filenames} | {error, reason()}</name>
+ <name name="list_dir" arity="2" since=""/>
+ <name name="list_dir" arity="3" since=""/>
<fsummary>Lists the directory.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- <v>Path = string()</v>
- <v>Filenames = [Filename]</v>
- <v>Filename = string()</v>
- <v>Timeout = timeout()</v>
- </type>
<desc>
<p>Lists the given directory on the server, returning the
filenames as a list of strings.</p>
@@ -221,14 +227,9 @@
</func>
<func>
- <name since="">make_dir(ChannelPid, Name) -></name>
- <name since="">make_dir(ChannelPid, Name, Timeout) -> ok | {error, reason()}</name>
+ <name name="make_dir" arity="2" since=""/>
+ <name name="make_dir" arity="3" since=""/>
<fsummary>Creates a directory.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- <v>Name = string()</v>
- <v>Timeout = timeout()</v>
- </type>
<desc>
<p>Creates a directory specified by <c><![CDATA[Name]]></c>. <c><![CDATA[Name]]></c>
must be a full path to a new directory. The directory can only be
@@ -237,14 +238,9 @@
</func>
<func>
- <name since="">make_symlink(ChannelPid, Name, Target) -></name>
- <name since="">make_symlink(ChannelPid, Name, Target, Timeout) -> ok | {error, reason()}</name>
+ <name name="make_symlink" arity="3" since=""/>
+ <name name="make_symlink" arity="4" since=""/>
<fsummary>Creates a symbolic link.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- <v>Name = string()</v>
- <v>Target = string()</v>
- </type>
<desc>
<p>Creates a symbolic link pointing to <c><![CDATA[Target]]></c> with the
name <c><![CDATA[Name]]></c>.
@@ -252,32 +248,19 @@
</desc>
</func>
- <func>
- <name since="">open(ChannelPid, File, Mode) -></name>
- <name since="">open(ChannelPid, File, Mode, Timeout) -> {ok, Handle} | {error, reason()}</name>
+ <func>
+ <name name="open" arity="3" since=""/>
+ <name name="open" arity="4" since=""/>
<fsummary>Opens a file and returns a handle.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- <v>File = string()</v>
- <v>Mode = [Modeflag]</v>
- <v>Modeflag = read | write | creat | trunc | append | binary</v>
- <v>Timeout = timeout()</v>
- <v>Handle = term()</v>
- </type>
<desc>
<p>Opens a file on the server and returns a handle, which
can be used for reading or writing.</p>
</desc>
</func>
<func>
- <name since="">opendir(ChannelPid, Path) -></name>
- <name since="">opendir(ChannelPid, Path, Timeout) -> {ok, Handle} | {error, reason()}</name>
+ <name name="opendir" arity="2" since=""/>
+ <name name="opendir" arity="3" since=""/>
<fsummary>Opens a directory and returns a handle.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- <v>Path = string()</v>
- <v>Timeout = timeout()</v>
- </type>
<desc>
<p>Opens a handle to a directory on the server. The handle
can be used for reading directory contents.</p>
@@ -285,72 +268,36 @@
</func>
<func>
- <name since="OTP 17.4">open_tar(ChannelPid, Path, Mode) -></name>
- <name since="OTP 17.4">open_tar(ChannelPid, Path, Mode, Timeout) -> {ok, Handle} | {error, reason()}</name>
+ <name name="open_tar" arity="3" since="OTP 17.4"/>
+ <name name="open_tar" arity="4" since="OTP 17.4"/>
<fsummary>Opens a tar file on the server to which <c>ChannelPid</c>
is connected and returns a handle.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- <v>Path = string()</v>
- <v>Mode = [read] | [write] | [read,EncryptOpt] | [write,DecryptOpt]</v>
- <v>EncryptOpt = {crypto,{InitFun,EncryptFun,CloseFun}}</v>
- <v>DecryptOpt = {crypto,{InitFun,DecryptFun}}</v>
- <v>InitFun = (fun() -> {ok,CryptoState}) | (fun() -> {ok,CryptoState,ChunkSize})</v>
- <v>CryptoState = any()</v>
- <v>ChunkSize = undefined | pos_integer()</v>
- <v>EncryptFun = (fun(PlainBin,CryptoState) -> EncryptResult)</v>
- <v>EncryptResult = {ok,EncryptedBin,CryptoState} | {ok,EncryptedBin,CryptoState,ChunkSize}</v>
- <v>PlainBin = binary()</v>
- <v>EncryptedBin = binary()</v>
- <v>DecryptFun = (fun(EncryptedBin,CryptoState) -> DecryptResult)</v>
- <v>DecryptResult = {ok,PlainBin,CryptoState} | {ok,PlainBin,CryptoState,ChunkSize}</v>
- <v>CloseFun = (fun(PlainBin,CryptoState) -> {ok,EncryptedBin})</v>
- <v>Timeout = timeout()</v>
- </type>
<desc>
<p>Opens a handle to a tar file on the server, associated with <c>ChannelPid</c>.
- The handle can be used for remote tar creation and extraction, as defined by the
- <seealso marker="stdlib:erl_tar#init-3">erl_tar:init/3</seealso> function.
- </p>
-
- <p> For code exampel see Section
- <seealso marker="using_ssh">SFTP Client with TAR Compression and Encryption</seealso> in
- the ssh Users Guide. </p>
-
- <p>The <c>crypto</c> mode option is applied to the generated stream of bytes prior to sending
- them to the SFTP server. This is intended for encryption but can be used for other
- purposes.
+ The handle can be used for remote tar creation and extraction. The actual writing
+ and reading is performed by calls to
+ <seealso marker="stdlib:erl_tar#add-3">erl_tar:add/3,4</seealso> and
+ <seealso marker="stdlib:erl_tar#extract-2">erl_tar:extract/2</seealso>.
+ Note: The
+ <seealso marker="stdlib:erl_tar#init-3">erl_tar:init/3</seealso> function should not
+ be called, that one is called by this open_tar function.
</p>
- <p>The <c>InitFun</c> is applied once
- prior to any other <c>crypto</c> operation. The returned <c>CryptoState</c> is then folded into
- repeated applications of the <c>EncryptFun</c> or <c>DecryptFun</c>. The binary returned
- from those funs are sent further to the remote SFTP server. Finally, if doing encryption,
- the <c>CloseFun</c> is applied to the last piece of data. The <c>CloseFun</c> is
- responsible for padding (if needed) and encryption of that last piece.
+ <p>For code examples see Section
+ <seealso marker="using_ssh#sftp-client-with-tar-compression">SFTP Client with TAR Compression</seealso>
+ in the ssh Users Guide.
</p>
- <p>The <c>ChunkSize</c> defines the size of the <c>PlainBin</c>s that <c>EncodeFun</c> is applied
- to. If the <c>ChunkSize</c> is <c>undefined</c>, the size of the <c>PlainBin</c>s varies,
- because this is intended for stream crypto, whereas a fixed <c>ChunkSize</c> is intended for block crypto.
- <c>ChunkSize</c>s can be changed in the return from the <c>EncryptFun</c> or
- <c>DecryptFun</c>. The value can be changed between <c>pos_integer()</c> and <c>undefined</c>.
+ <p>The <c>crypto</c> mode option is explained in the data types section above, see
+ <seealso marker="#Crypto operations for open_tar">Crypto operations for open_tar</seealso>.
+ Encryption is assumed if the <c>Mode</c> contains <c>write</c>, and
+ decryption if the <c>Mode</c> contains <c>read</c>.
</p>
-
</desc>
</func>
<func>
- <name since="">position(ChannelPid, Handle, Location) -></name>
- <name since="">position(ChannelPid, Handle, Location, Timeout) -> {ok, NewPosition | {error, reason()}</name>
+ <name name="position" arity="3" since=""/>
+ <name name="position" arity="4" since=""/>
<fsummary>Sets the file position of a file.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- <v>Handle = term()</v>
- <v>Location = Offset
- | {bof, Offset} | {cur, Offset} | {eof, Offset} | bof | cur | eof</v>
- <v>Offset = integer()</v>
- <v>Timeout = timeout()</v>
- <v>NewPosition = integer()</v>
- </type>
<desc>
<p>Sets the file position of the file referenced by <c><![CDATA[Handle]]></c>.
Returns <c><![CDATA[{ok, NewPosition}]]></c> (as an absolute offset) if
@@ -384,17 +331,9 @@
</func>
<func>
- <name since="">pread(ChannelPid, Handle, Position, Len) -></name>
- <name since="">pread(ChannelPid, Handle, Position, Len, Timeout) -> {ok, Data} | eof | {error, reason()}</name>
+ <name name="pread" arity="4" since=""/>
+ <name name="pread" arity="5" since=""/>
<fsummary>Reads from an open file.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- <v>Handle = term()</v>
- <v>Position = integer()</v>
- <v>Len = integer()</v>
- <v>Timeout = timeout()</v>
- <v>Data = string() | binary()</v>
- </type>
<desc><p>The <c><![CDATA[pread/3,4]]></c> function reads from a specified position,
combining the <seealso marker="#position-3"><c>position/3</c></seealso> and
<seealso marker="#read-3"><c>read/3,4</c></seealso> functions.</p>
@@ -402,16 +341,9 @@
</func>
<func>
- <name since="">pwrite(ChannelPid, Handle, Position, Data) -> ok</name>
- <name since="">pwrite(ChannelPid, Handle, Position, Data, Timeout) -> ok | {error, reason()}</name>
+ <name name="pwrite" arity="4" since=""/>
+ <name name="pwrite" arity="5" since=""/>
<fsummary>Writes to an open file.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- <v>Handle = term()</v>
- <v>Position = integer()</v>
- <v>Data = iolist()</v>
- <v>Timeout = timeout()</v>
- </type>
<desc><p>The <c><![CDATA[pwrite/3,4]]></c> function writes to a specified position,
combining the <seealso marker="#position-3"><c>position/3</c></seealso> and
<seealso marker="#write-3"><c>write/3,4</c></seealso> functions.</p>
@@ -419,16 +351,9 @@
</func>
<func>
- <name since="">read(ChannelPid, Handle, Len) -></name>
- <name since="">read(ChannelPid, Handle, Len, Timeout) -> {ok, Data} | eof | {error, reason()}</name>
+ <name name="read" arity="3" since=""/>
+ <name name="read" arity="4" since=""/>
<fsummary>Reads from an open file.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- <v>Handle = term()</v>
- <v>Len = integer()</v>
- <v>Timeout = timeout()</v>
- <v>Data = string() | binary()</v>
- </type>
<desc>
<p>Reads <c><![CDATA[Len]]></c> bytes from the file referenced by
<c><![CDATA[Handle]]></c>. Returns <c><![CDATA[{ok, Data}]]></c>, <c><![CDATA[eof]]></c>, or
@@ -440,32 +365,19 @@
</desc>
</func>
- <func>
- <name since="">read_file(ChannelPid, File) -></name>
- <name since="">read_file(ChannelPid, File, Timeout) -> {ok, Data} | {error, reason()}</name>
+ <func>
+ <name name="read_file" arity="2" since=""/>
+ <name name="read_file" arity="3" since=""/>
<fsummary>Reads a file.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- <v>File = string()</v>
- <v>Data = binary()</v>
- <v>Timeout = timeout()</v>
- </type>
<desc>
<p>Reads a file from the server, and returns the data in a binary.</p>
</desc>
</func>
- <func>
- <name since="">read_file_info(ChannelPid, Name) -></name>
- <name since="">read_file_info(ChannelPid, Name, Timeout) -> {ok, FileInfo} | {error, reason()}</name>
+ <func>
+ <name name="read_file_info" arity="2" since=""/>
+ <name name="read_file_info" arity="3" since=""/>
<fsummary>Gets information about a file.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- <v>Name = string()</v>
- <v>Handle = term()</v>
- <v>Timeout = timeout()</v>
- <v>FileInfo = record()</v>
- </type>
<desc>
<p>Returns a <c><![CDATA[file_info]]></c> record from the file system object specified by
<c><![CDATA[Name]]></c> or <c><![CDATA[Handle]]></c>. See
@@ -474,38 +386,26 @@
</p>
<p>
Depending on the underlying OS:es links might be followed and info on the final file, directory
- etc is returned. See <seealso marker="#read_link_info-2">ssh_sftp::read_link_info/2</seealso>
+ etc is returned. See <seealso marker="#read_link_info-2">read_link_info/2</seealso>
on how to get information on links instead.
</p>
</desc>
</func>
<func>
- <name since="">read_link(ChannelPid, Name) -></name>
- <name since="">read_link(ChannelPid, Name, Timeout) -> {ok, Target} | {error, reason()}</name>
+ <name name="read_link" arity="2" since=""/>
+ <name name="read_link" arity="3" since=""/>
<fsummary>Reads symbolic link.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- <v>Name = string()</v>
- <v>Target = string()</v>
- </type>
<desc>
<p>Reads the link target from the symbolic link specified by <c><![CDATA[name]]></c>.
</p>
</desc>
</func>
- <func>
- <name since="">read_link_info(ChannelPid, Name) -> {ok, FileInfo} | {error, reason()}</name>
- <name since="">read_link_info(ChannelPid, Name, Timeout) -> {ok, FileInfo} | {error, reason()}</name>
+ <func>
+ <name since="" name="read_link_info" arity="2"/>
+ <name since="" name="read_link_info" arity="3"/>
<fsummary>Gets information about a symbolic link.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- <v>Name = string()</v>
- <v>Handle = term()</v>
- <v>Timeout = timeout()</v>
- <v>FileInfo = record()</v>
- </type>
<desc>
<p>Returns a <c><![CDATA[file_info]]></c> record from the symbolic
link specified by <c><![CDATA[Name]]></c> or <c><![CDATA[Handle]]></c>.
@@ -517,15 +417,9 @@
</func>
<func>
- <name since="">rename(ChannelPid, OldName, NewName) -> </name>
- <name since="">rename(ChannelPid, OldName, NewName, Timeout) -> ok | {error, reason()}</name>
+ <name since="" name="rename" arity="3"/>
+ <name since="" name="rename" arity="4"/>
<fsummary>Renames a file.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- <v>OldName = string()</v>
- <v>NewName = string()</v>
- <v>Timeout = timeout()</v>
- </type>
<desc>
<p>Renames a file named <c><![CDATA[OldName]]></c> and gives it the name
<c><![CDATA[NewName]]></c>.
@@ -535,25 +429,27 @@
<func>
<name since="">start_channel(ConnectionRef) -></name>
- <name since="">start_channel(ConnectionRef, Options) ->
- {ok, Pid} | {error, reason()|term()}</name>
+ <name since="">start_channel(ConnectionRef, SftpOptions) ->
+ {ok, ChannelPid} | Error</name>
+ <name since="">start_channel(Host) -></name>
<name since="">start_channel(Host, Options) -></name>
- <name since="">start_channel(Host, Port, Options) ->
- {ok, Pid, ConnectionRef} | {error, reason()|term()}</name>
-
+ <name since="">start_channel(Host, Port, Options) -></name>
<name since="">start_channel(TcpSocket) -></name>
<name since="">start_channel(TcpSocket, Options) ->
- {ok, Pid, ConnectionRef} | {error, reason()|term()}</name>
+ {ok, ChannelPid, ConnectionRef} | Error</name>
<fsummary>Starts an SFTP client.</fsummary>
<type>
- <v>Host = string()</v>
- <v>ConnectionRef = connection_ref()</v>
- <v>Port = integer()</v>
- <v>TcpSocket = port()</v>
- <d>The socket is supposed to be from <seealso marker="kernel:gen_tcp#connect-3">gen_tcp:connect</seealso> or <seealso marker="kernel:gen_tcp#accept-1">gen_tcp:accept</seealso> with option <c>{active,false}</c></d>
- <v>Options = [{Option, Value}]</v>
+ <v>Host = <seealso marker="ssh:ssh#type-host">ssh:host()</seealso></v>
+ <v>Port = <seealso marker="kernel:inet#type-port_number">inet:port_number()</seealso></v>
+ <v>TcpSocket = <seealso marker="ssh:ssh#type-open_socket">ssh:open_socket()</seealso></v>
+ <v>Options = [ <seealso marker="#type-sftp_option">sftp_option()</seealso>
+ | <seealso marker="ssh:ssh#type-client_option">ssh:client_option()</seealso> ]</v>
+ <v>SftpOptions = [ <seealso marker="#type-sftp_option">sftp_option()</seealso> ]</v>
+ <v>ChannelPid = pid()</v>
+ <v>ConnectionRef = <seealso marker="ssh:ssh#type-connection_ref">ssh:connection_ref()</seealso></v>
+ <v>Error = {error, <seealso marker="#type-reason">reason()</seealso>}</v>
</type>
<desc>
<p>If no connection reference is provided, a connection is set
@@ -594,11 +490,8 @@
</func>
<func>
- <name since="">stop_channel(ChannelPid) -> ok</name>
+ <name since="" name="stop_channel" arity="1"/>
<fsummary>Stops the SFTP client channel.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- </type>
<desc>
<p>Stops an SFTP channel. Does not close the SSH connection.
Use <seealso marker="ssh#close-1">ssh:close/1</seealso> to close it.</p>
@@ -606,16 +499,9 @@
</func>
<func>
- <name since="">write(ChannelPid, Handle, Data) -></name>
- <name since="">write(ChannelPid, Handle, Data, Timeout) -> ok | {error, reason()}</name>
+ <name since="" name="write" arity="3"/>
+ <name since="" name="write" arity="4"/>
<fsummary>Writes to an open file.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- <v>Handle = term()</v>
- <v>Position = integer()</v>
- <v>Data = iolist()</v>
- <v>Timeout = timeout()</v>
- </type>
<desc>
<p>Writes <c><![CDATA[data]]></c> to the file referenced by <c><![CDATA[Handle]]></c>.
The file is to be opened with <c><![CDATA[write]]></c> or <c><![CDATA[append]]></c>
@@ -625,15 +511,9 @@
</func>
<func>
- <name since="">write_file(ChannelPid, File, Iolist) -></name>
- <name since="">write_file(ChannelPid, File, Iolist, Timeout) -> ok | {error, reason()}</name>
+ <name since="" name="write_file" arity="3"/>
+ <name since="" name="write_file" arity="4"/>
<fsummary>Writes a file.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- <v>File = string()</v>
- <v>Iolist = iolist()</v>
- <v>Timeout = timeout()</v>
- </type>
<desc>
<p>Writes a file to the server. The file is created if it does not exist
but overwritten if it exists.</p>
@@ -641,15 +521,9 @@
</func>
<func>
- <name since="">write_file_info(ChannelPid, Name, Info) -></name>
- <name since="">write_file_info(ChannelPid, Name, Info, Timeout) -> ok | {error, reason()}</name>
+ <name since="" name="write_file_info" arity="3"/>
+ <name since="" name="write_file_info" arity="4"/>
<fsummary>Writes information for a file.</fsummary>
- <type>
- <v>ChannelPid = pid()</v>
- <v>Name = string()</v>
- <v>Info = record()</v>
- <v>Timeout = timeout()</v>
- </type>
<desc>
<p>Writes file information from a <c><![CDATA[file_info]]></c> record to the
file specified by <c><![CDATA[Name]]></c>. See
diff --git a/lib/ssh/doc/src/ssh_sftpd.xml b/lib/ssh/doc/src/ssh_sftpd.xml
index ee72784add..0d7b340399 100644
--- a/lib/ssh/doc/src/ssh_sftpd.xml
+++ b/lib/ssh/doc/src/ssh_sftpd.xml
@@ -35,36 +35,23 @@
<p>Specifies a channel process to handle an SFTP subsystem.</p>
</description>
- <section>
- <title>DATA TYPES</title>
- <taglist>
- <tag><c>subsystem_spec() =</c></tag>
- <item><p><c>{subsystem_name(), {channel_callback(), channel_init_args()}}</c></p></item>
- <tag><c>subsystem_name() =</c></tag>
- <item><p><c>"sftp"</c></p></item>
- <tag><c>channel_callback() =</c></tag>
- <item><p><c>atom()</c> - Name of the Erlang module implementing the subsystem using the
- <seealso marker="ssh_server_channel">ssh_server_channel</seealso> (replaces ssh_daemon_channel) behaviour.</p></item>
- <tag><c>channel_init_args() =</c></tag>
- <item><p><c>list()</c> - The one given as argument to function <c>subsystem_spec/1</c>.</p></item>
- </taglist>
- </section>
<funcs>
<func>
- <name since="">subsystem_spec(Options) -> subsystem_spec()</name>
+ <name name="subsystem_spec" arity="1" since=""/>
<fsummary>Returns the subsystem specification that allows an SSH daemon to handle the subsystem "sftp".</fsummary>
- <type>
- <v>Options = [{Option, Value}]</v>
- </type>
<desc>
<p>Is to be used together with <c>ssh:daemon/[1,2,3]</c></p>
+ <p>The <c>Name</c> is <c>"sftp"</c> and
+ <c>CbMod</c> is the name of the Erlang module implementing the subsystem using the
+ <seealso marker="ssh_server_channel">ssh_server_channel</seealso> (replaces ssh_daemon_channel) behaviour.
+ </p>
<p>Options:</p>
<taglist>
- <tag><c><![CDATA[{cwd, String}]]></c></tag>
+ <tag><c>cwd</c></tag>
<item>
<p>Sets the initial current working directory for the server.</p>
</item>
- <tag><c><![CDATA[{file_handler, CallbackModule}]]></c></tag>
+ <tag><c>file_handler</c></tag>
<item>
<p>Determines which module to call for accessing
the file server. The default value is <c>ssh_sftpd_file</c>, which uses the
@@ -72,13 +59,13 @@
APIs to access the standard OTP file server. This option can be used to plug in
other file servers.</p>
</item>
- <tag><c><![CDATA[{max_files, Integer}]]></c></tag>
+ <tag><c>max_files</c></tag>
<item>
<p>The default value is <c>0</c>, which means that there is no upper limit.
If supplied, the number of filenames returned to the SFTP client per <c>READDIR</c>
request is limited to at most the given value.</p>
</item>
- <tag><c><![CDATA[{root, String}]]></c></tag>
+ <tag><c>root</c></tag>
<item>
<p>Sets the SFTP root directory. Then the user cannot see any files
above this root. If, for example, the root directory is set to <c>/tmp</c>,
@@ -86,7 +73,7 @@
<c>cd /etc</c>, the user moves to <c>/tmp/etc</c>.
</p>
</item>
- <tag><c><![CDATA[{sftpd_vsn, integer()}]]></c></tag>
+ <tag><c>sftpd_vsn</c></tag>
<item>
<p>Sets the SFTP version to use. Defaults to 5. Version 6 is under
development and limited.</p>
diff --git a/lib/ssh/doc/src/using_ssh.xml b/lib/ssh/doc/src/using_ssh.xml
index 4455d5ecc5..5c56dee81d 100644
--- a/lib/ssh/doc/src/using_ssh.xml
+++ b/lib/ssh/doc/src/using_ssh.xml
@@ -232,9 +232,10 @@
</section>
<section>
- <title>SFTP Client with TAR Compression and Encryption</title>
-
- <p>Example of writing and then reading a tar file follows:</p>
+ <title>SFTP Client with TAR Compression</title>
+ <section>
+ <title>Basic example</title>
+ <p>This is an example of writing and then reading a tar file:</p>
<code type="erl">
{ok,HandleWrite} = ssh_sftp:open_tar(ChannelPid, ?tar_file_name, [write]),
ok = erl_tar:add(HandleWrite, .... ),
@@ -248,8 +249,12 @@
{ok,NameValueList} = erl_tar:extract(HandleRead,[memory]),
ok = erl_tar:close(HandleRead),
</code>
+ </section>
- <p>The previous write and read example can be extended with encryption and decryption as follows:</p>
+ <section>
+ <title>Example with encryption</title>
+ <p>The previous <seealso marker="using_ssh#basic-example">Basic example</seealso>
+ can be extended with encryption and decryption as follows:</p>
<code type="erl">
%% First three parameters depending on which crypto type we select:
Key = &lt;&lt;"This is a 256 bit key. abcdefghi">>,
@@ -297,6 +302,7 @@ Cr = {InitFun,DecryptFun},
ok = erl_tar:close(HandleRead),
</code>
</section>
+ </section>
<section>
<marker id="usersguide_creating_a_subsystem"/>
diff --git a/lib/ssh/src/Makefile b/lib/ssh/src/Makefile
index 9627b70eeb..46bf6332e5 100644
--- a/lib/ssh/src/Makefile
+++ b/lib/ssh/src/Makefile
@@ -55,6 +55,7 @@ MODULES= \
ssh_app \
ssh_auth\
ssh_bits \
+ ssh_channel_sup \
ssh_cli \
ssh_connection \
ssh_connection_handler \
@@ -66,7 +67,6 @@ MODULES= \
ssh_message \
ssh_no_io \
ssh_options \
- ssh_server_channel_sup \
ssh_sftp \
ssh_sftpd \
ssh_sftpd_file\
@@ -171,7 +171,7 @@ $(EBIN)/ssh_connection_handler.$(EMULATOR): ssh_connection_handler.erl ssh.hrl \
$(EBIN)/ssh_shell.$(EMULATOR): ssh_shell.erl ssh_connect.hrl
$(EBIN)/ssh_system_sup.$(EMULATOR): ssh_system_sup.erl ssh.hrl
$(EBIN)/ssh_subsystem_sup.$(EMULATOR): ssh_subsystem_sup.erl
-$(EBIN)/ssh_server_channel_sup.$(EMULATOR): ssh_server_channel_sup.erl
+$(EBIN)/ssh_channel_sup.$(EMULATOR): ssh_channel_sup.erl ssh.hrl
$(EBIN)/ssh_acceptor_sup.$(EMULATOR): ssh_acceptor_sup.erl ssh.hrl
$(EBIN)/ssh_acceptor.$(EMULATOR): ssh_acceptor.erl ssh.hrl
$(EBIN)/ssh_app.$(EMULATOR): ssh_app.erl
diff --git a/lib/ssh/src/ssh.app.src b/lib/ssh/src/ssh.app.src
index 2193c14611..379cde23e3 100644
--- a/lib/ssh/src/ssh.app.src
+++ b/lib/ssh/src/ssh.app.src
@@ -11,6 +11,7 @@
ssh_auth,
ssh_message,
ssh_bits,
+ ssh_channel_sup,
ssh_cli,
ssh_client_channel,
ssh_client_key_api,
@@ -28,7 +29,6 @@
ssh_info,
ssh_no_io,
ssh_server_channel,
- ssh_server_channel_sup,
ssh_server_key_api,
ssh_sftp,
ssh_sftpd,
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
index ff5aee14d7..874c545e61 100644
--- a/lib/ssh/src/ssh.erl
+++ b/lib/ssh/src/ssh.erl
@@ -31,9 +31,10 @@
-export([start/0, start/1, stop/0,
connect/2, connect/3, connect/4,
close/1, connection_info/2,
+ connection_info/1,
channel_info/3,
daemon/1, daemon/2, daemon/3,
- daemon_info/1,
+ daemon_info/1, daemon_info/2,
default_algorithms/0,
chk_algos_opts/1,
stop_listener/1, stop_listener/2, stop_listener/3,
@@ -66,6 +67,8 @@
cipher_alg/0,
mac_alg/0,
compression_alg/0,
+ host/0,
+ open_socket/0,
ip_port/0
]).
@@ -125,15 +128,13 @@ connect(Socket, UserOptions, NegotiationTimeout) when is_port(Socket),
{error, Error} ->
{error, Error};
Options ->
- case valid_socket_to_use(Socket, ?GET_OPT(transport,Options)) of
- ok ->
- {ok, {Host,_Port}} = inet:peername(Socket),
- Opts = ?PUT_INTERNAL_OPT([{user_pid,self()}, {host,Host}], Options),
- ssh_connection_handler:start_connection(client, Socket, Opts, NegotiationTimeout);
- {error,SockError} ->
- {error,SockError}
- end
- end;
+ case valid_socket_to_use(Socket, ?GET_OPT(transport,Options)) of
+ ok ->
+ connect_socket(Socket, Options, NegotiationTimeout);
+ {error,SockError} ->
+ {error,SockError}
+ end
+ end;
connect(Host, Port, Options) when is_integer(Port),
Port>0,
@@ -147,9 +148,9 @@ connect(Host, Port, Options) when is_integer(Port),
Options :: client_options(),
NegotiationTimeout :: timeout().
-connect(Host0, Port, UserOptions, Timeout) when is_integer(Port),
- Port>0,
- is_list(UserOptions) ->
+connect(Host0, Port, UserOptions, NegotiationTimeout) when is_integer(Port),
+ Port>0,
+ is_list(UserOptions) ->
case ssh_options:handle_options(client, UserOptions) of
{error, _Reason} = Error ->
Error;
@@ -160,8 +161,7 @@ connect(Host0, Port, UserOptions, Timeout) when is_integer(Port),
Host = mangle_connect_address(Host0, SocketOpts),
try Transport:connect(Host, Port, SocketOpts, ConnectionTimeout) of
{ok, Socket} ->
- Opts = ?PUT_INTERNAL_OPT([{user_pid,self()}, {host,Host}], Options),
- ssh_connection_handler:start_connection(client, Socket, Opts, Timeout);
+ connect_socket(Socket, Options, NegotiationTimeout);
{error, Reason} ->
{error, Reason}
catch
@@ -172,6 +172,22 @@ connect(Host0, Port, UserOptions, Timeout) when is_integer(Port),
end
end.
+
+connect_socket(Socket, Options0, NegotiationTimeout) ->
+ {ok, {Host,Port}} = inet:sockname(Socket),
+ Profile = ?GET_OPT(profile, Options0),
+ {ok, SystemSup} = sshc_sup:start_child(Host, Port, Profile, Options0),
+ {ok, SubSysSup} = ssh_system_sup:start_subsystem(SystemSup, client, Host, Port, Profile, Options0),
+ ConnectionSup = ssh_system_sup:connection_supervisor(SystemSup),
+ Opts = ?PUT_INTERNAL_OPT([{user_pid,self()},
+ {host,Host},
+ {supervisors, [{system_sup, SystemSup},
+ {subsystem_sup, SubSysSup},
+ {connection_sup, ConnectionSup}]}
+ ], Options0),
+ ssh_connection_handler:start_connection(client, Socket, Opts, NegotiationTimeout).
+
+
%%--------------------------------------------------------------------
-spec close(ConnectionRef) -> ok | {error,term()} when
ConnectionRef :: connection_ref() .
@@ -183,21 +199,50 @@ close(ConnectionRef) ->
%%--------------------------------------------------------------------
%% Description: Retrieves information about a connection.
-%%--------------------------------------------------------------------
--spec connection_info(ConnectionRef, Keys) -> ConnectionInfo when
+%%---------------------------------------------------------------------
+-type version() :: {protocol_version(), software_version()}.
+-type protocol_version() :: {Major::pos_integer(), Minor::non_neg_integer()}.
+-type software_version() :: string().
+-type conn_info_algs() :: [{kex, kex_alg()}
+ | {hkey, pubkey_alg()}
+ | {encrypt, cipher_alg()}
+ | {decrypt, cipher_alg()}
+ | {send_mac, mac_alg()}
+ | {recv_mac, mac_alg()}
+ | {compress, compression_alg()}
+ | {decompress, compression_alg()}
+ | {send_ext_info, boolean()}
+ | {recv_ext_info, boolean()}
+ ].
+-type conn_info_channels() :: [proplists:proplist()].
+
+-type connection_info_tuple() ::
+ {client_version, version()}
+ | {server_version, version()}
+ | {user, string()}
+ | {peer, {inet:hostname(), ip_port()}}
+ | {sockname, ip_port()}
+ | {options, client_options()}
+ | {algorithms, conn_info_algs()}
+ | {channels, conn_info_channels()}.
+
+-spec connection_info(ConnectionRef) -> InfoTupleList when
ConnectionRef :: connection_ref(),
- Keys :: [client_version | server_version | user | peer | sockname],
- ConnectionInfo :: [{client_version, Version}
- | {server_version, Version}
- | {user,string()}
- | {peer, {inet:hostname(), ip_port()}}
- | {sockname, ip_port()}
- ],
- Version :: {ProtocolVersion, VersionString::string()},
- ProtocolVersion :: {Major::pos_integer(), Minor::non_neg_integer()} .
-
-connection_info(Connection, Options) ->
- ssh_connection_handler:connection_info(Connection, Options).
+ InfoTupleList :: [InfoTuple],
+ InfoTuple :: connection_info_tuple().
+
+connection_info(ConnectionRef) ->
+ connection_info(ConnectionRef, []).
+
+-spec connection_info(ConnectionRef, ItemList|Item) -> InfoTupleList|InfoTuple when
+ ConnectionRef :: connection_ref(),
+ ItemList :: [Item],
+ Item :: client_version | server_version | user | peer | sockname | options | algorithms | sockname,
+ InfoTupleList :: [InfoTuple],
+ InfoTuple :: connection_info_tuple().
+
+connection_info(ConnectionRef, Key) ->
+ ssh_connection_handler:connection_info(ConnectionRef, Key).
%%--------------------------------------------------------------------
-spec channel_info(connection_ref(), channel_id(), [atom()]) -> proplists:proplist().
@@ -319,33 +364,72 @@ daemon(_, _, _) ->
{error, badarg}.
%%--------------------------------------------------------------------
--spec daemon_info(Daemon) -> {ok, DaemonInfo} | {error,term()} when
- Daemon :: daemon_ref(),
- DaemonInfo :: [ {ip, inet:ip_address()}
- | {port, inet:port_number()}
- | {profile, term()}
- ].
-
-daemon_info(Pid) ->
- case catch ssh_system_sup:acceptor_supervisor(Pid) of
+-type daemon_info_tuple() ::
+ {port, inet:port_number()}
+ | {ip, inet:ip_address()}
+ | {profile, atom()}
+ | {options, daemon_options()}.
+
+-spec daemon_info(DaemonRef) -> {ok,InfoTupleList} | {error,bad_daemon_ref} when
+ DaemonRef :: daemon_ref(),
+ InfoTupleList :: [InfoTuple],
+ InfoTuple :: daemon_info_tuple().
+
+daemon_info(DaemonRef) ->
+ case catch ssh_system_sup:acceptor_supervisor(DaemonRef) of
AsupPid when is_pid(AsupPid) ->
- [{IP,Port,Profile}] =
- [{IP,Prt,Prf}
+ [{Host,Port,Profile}] =
+ [{Hst,Prt,Prf}
|| {{ssh_acceptor_sup,Hst,Prt,Prf},_Pid,worker,[ssh_acceptor]}
- <- supervisor:which_children(AsupPid),
- IP <- [case inet:parse_strict_address(Hst) of
- {ok,IP} -> IP;
- _ -> Hst
- end]
- ],
+ <- supervisor:which_children(AsupPid)],
+ IP =
+ case inet:parse_strict_address(Host) of
+ {ok,IP0} -> IP0;
+ _ -> Host
+ end,
+
+ Opts =
+ case ssh_system_sup:get_options(DaemonRef, Host, Port, Profile) of
+ {ok, OptMap} ->
+ lists:sort(
+ maps:to_list(
+ ssh_options:keep_set_options(
+ server,
+ ssh_options:keep_user_options(server,OptMap))));
+ _ ->
+ []
+ end,
+
{ok, [{port,Port},
{ip,IP},
- {profile,Profile}
+ {profile,Profile},
+ {options,Opts}
]};
_ ->
{error,bad_daemon_ref}
end.
+-spec daemon_info(DaemonRef, ItemList|Item) -> InfoTupleList|InfoTuple | {error,bad_daemon_ref} when
+ DaemonRef :: daemon_ref(),
+ ItemList :: [Item],
+ Item :: ip | port | profile | options,
+ InfoTupleList :: [InfoTuple],
+ InfoTuple :: daemon_info_tuple().
+
+daemon_info(DaemonRef, Key) when is_atom(Key) ->
+ case daemon_info(DaemonRef, [Key]) of
+ [{Key,Val}] -> {Key,Val};
+ Other -> Other
+ end;
+daemon_info(DaemonRef, Keys) ->
+ case daemon_info(DaemonRef) of
+ {ok,KVs} ->
+ [{Key,proplists:get_value(Key,KVs)} || Key <- Keys,
+ lists:keymember(Key,1,KVs)];
+ _ ->
+ []
+ end.
+
%%--------------------------------------------------------------------
%% Description: Stops the listener, but leaves
%% existing connections started by the listener up and running.
@@ -380,7 +464,7 @@ stop_listener(Address, Port, Profile) ->
-spec stop_daemon(DaemonRef::daemon_ref()) -> ok.
stop_daemon(SysSup) ->
- ssh_system_sup:stop_system(SysSup).
+ ssh_system_sup:stop_system(server, SysSup).
-spec stop_daemon(inet:ip_address(), inet:port_number()) -> ok.
@@ -393,11 +477,11 @@ stop_daemon(Address, Port) ->
stop_daemon(any, Port, Profile) ->
map_ip(fun(IP) ->
- ssh_system_sup:stop_system(IP, Port, Profile)
+ ssh_system_sup:stop_system(server, IP, Port, Profile)
end, [{0,0,0,0},{0,0,0,0,0,0,0,0}]);
stop_daemon(Address, Port, Profile) ->
map_ip(fun(IP) ->
- ssh_system_sup:stop_system(IP, Port, Profile)
+ ssh_system_sup:stop_system(server, IP, Port, Profile)
end, {address,Address}).
%%--------------------------------------------------------------------
diff --git a/lib/ssh/src/ssh_server_channel_sup.erl b/lib/ssh/src/ssh_channel_sup.erl
index ff74061bb3..4b36c8a5a0 100644
--- a/lib/ssh/src/ssh_server_channel_sup.erl
+++ b/lib/ssh/src/ssh_channel_sup.erl
@@ -22,11 +22,12 @@
%%----------------------------------------------------------------------
%% Purpose: Ssh channel supervisor.
%%----------------------------------------------------------------------
--module(ssh_server_channel_sup).
+-module(ssh_channel_sup).
-behaviour(supervisor).
+-include("ssh.hrl").
--export([start_link/1, start_child/5]).
+-export([start_link/1, start_child/8]).
%% Supervisor callback
-export([init/1]).
@@ -37,15 +38,36 @@
start_link(Args) ->
supervisor:start_link(?MODULE, [Args]).
-start_child(Sup, Callback, Id, Args, Exec) ->
+
+start_child(client, ChannelSup, ConnRef, Callback, Id, Args, Exec, _Opts) when is_pid(ConnRef) ->
+ start_the_child(ssh_client_channel, ChannelSup, ConnRef, Callback, Id, Args, Exec);
+start_child(server, ChannelSup, ConnRef, Callback, Id, Args, Exec, Opts) when is_pid(ConnRef) ->
+ case max_num_channels_not_exceeded(ChannelSup, Opts) of
+ true ->
+ start_the_child(ssh_server_channel, ChannelSup, ConnRef, Callback, Id, Args, Exec);
+ false ->
+ {error, max_num_channels_exceeded}
+ end.
+
+
+start_the_child(ChanMod, ChannelSup, ConnRef, Callback, Id, Args, Exec) ->
ChildSpec =
#{id => make_ref(),
- start => {ssh_server_channel, start_link, [self(), Id, Callback, Args, Exec]},
+ start => {ChanMod, start_link, [ConnRef, Id, Callback, Args, Exec]},
restart => temporary,
type => worker,
- modules => [ssh_server_channel]
+ modules => [ChanMod]
},
- supervisor:start_child(Sup, ChildSpec).
+ case supervisor:start_child(ChannelSup, ChildSpec) of
+ {ok, Pid} ->
+ {ok, Pid};
+ {ok, Pid, _Info} ->
+ {ok,Pid};
+ {error, {Error,_Info}} ->
+ {error, Error};
+ {error, Error} ->
+ {error, Error}
+ end.
%%%=========================================================================
%%% Supervisor callback
@@ -60,3 +82,9 @@ init(_Args) ->
%%%=========================================================================
%%% Internal functions
%%%=========================================================================
+max_num_channels_not_exceeded(ChannelSup, Opts) ->
+ MaxNumChannels = ?GET_OPT(max_channels, Opts),
+ NumChannels = length([x || {_,_,worker,[ssh_server_channel]} <-
+ supervisor:which_children(ChannelSup)]),
+ %% Note that NumChannels is BEFORE starting a new one
+ NumChannels < MaxNumChannels.
diff --git a/lib/ssh/src/ssh_client_channel.erl b/lib/ssh/src/ssh_client_channel.erl
index f985d8e273..3bd1e1fdf1 100644
--- a/lib/ssh/src/ssh_client_channel.erl
+++ b/lib/ssh/src/ssh_client_channel.erl
@@ -52,7 +52,7 @@
-callback handle_msg(Msg ::term(), State :: term()) ->
{ok, State::term()} | {stop, ChannelId::ssh:channel_id(), State::term()}.
--callback handle_ssh_msg({ssh_cm, ConnectionRef::ssh:connection_ref(), SshMsg::term()},
+-callback handle_ssh_msg(ssh_connection:event(),
State::term()) -> {ok, State::term()} |
{stop, ChannelId::ssh:channel_id(),
State::term()}.
diff --git a/lib/ssh/src/ssh_connect.hrl b/lib/ssh/src/ssh_connect.hrl
index 9a060b8304..d6b50613f9 100644
--- a/lib/ssh/src/ssh_connect.hrl
+++ b/lib/ssh/src/ssh_connect.hrl
@@ -263,11 +263,8 @@
-record(connection, {
requests = [], %% [{ChannelId, Pid}...] awaiting reply on request,
channel_cache,
- port_bindings,
channel_id_seed,
cli_spec,
- address,
- port,
options,
exec,
system_supervisor,
diff --git a/lib/ssh/src/ssh_connection.erl b/lib/ssh/src/ssh_connection.erl
index 83f85b1d8e..067e5230c9 100644
--- a/lib/ssh/src/ssh_connection.erl
+++ b/lib/ssh/src/ssh_connection.erl
@@ -60,13 +60,121 @@
request_failure_msg/0,
request_success_msg/1,
- bind/4, unbind/3, unbind_channel/2,
- bound_channel/3, encode_ip/1
+ encode_ip/1
]).
-type connection_ref() :: ssh:connection_ref().
-type channel_id() :: ssh:channel_id().
+-type req_status() :: success | failure .
+-type reason() :: closed | timeout .
+
+-type result() :: req_status() | {error, reason()} .
+
+-type ssh_data_type_code() :: non_neg_integer(). % Only 0 and 1 are used
+
+
+%%% The SSH Connection Protocol
+
+-export_type([event/0,
+ channel_msg/0,
+ want_reply/0,
+ data_ch_msg/0,
+ eof_ch_msg/0,
+ signal_ch_msg/0,
+ exit_signal_ch_msg/0,
+ exit_status_ch_msg/0,
+ closed_ch_msg/0,
+ env_ch_msg/0,
+ pty_ch_msg/0,
+ shell_ch_msg/0,
+ window_change_ch_msg/0,
+ exec_ch_msg/0
+ ]).
+
+-type event() :: {ssh_cm, ssh:connection_ref(), channel_msg()}.
+-type channel_msg() :: data_ch_msg()
+ | eof_ch_msg()
+ | closed_ch_msg()
+ | pty_ch_msg()
+ | env_ch_msg()
+ | shell_ch_msg()
+ | exec_ch_msg()
+ | signal_ch_msg()
+ | window_change_ch_msg()
+ | exit_status_ch_msg()
+ | exit_signal_ch_msg()
+ .
+
+-type want_reply() :: boolean().
+
+-type data_ch_msg() :: {data,
+ ssh:channel_id(),
+ ssh_data_type_code(),
+ Data :: binary()
+ } .
+-type eof_ch_msg() :: {eof,
+ ssh:channel_id()
+ } .
+-type signal_ch_msg() :: {signal,
+ ssh:channel_id(),
+ SignalName :: string()
+ } .
+-type exit_signal_ch_msg() :: {exit_signal, ssh:channel_id(),
+ ExitSignal :: string(),
+ ErrorMsg :: string(),
+ LanguageString :: string()} .
+-type exit_status_ch_msg() :: {exit_status,
+ ssh:channel_id(),
+ ExitStatus :: non_neg_integer()
+ } .
+-type closed_ch_msg() :: {closed,
+ ssh:channel_id()
+ } .
+-type env_ch_msg() :: {env,
+ ssh:channel_id(),
+ want_reply(),
+ Var :: string(),
+ Value :: string()
+ } .
+-type pty_ch_msg() :: {pty,
+ ssh:channel_id(),
+ want_reply(),
+ {Terminal :: string(),
+ CharWidth :: non_neg_integer(),
+ RowHeight :: non_neg_integer(),
+ PixelWidth :: non_neg_integer(),
+ PixelHeight :: non_neg_integer(),
+ TerminalModes :: [term_mode()]
+ }
+ } .
+
+-type term_mode() :: {Opcode :: atom() | byte(),
+ Value :: non_neg_integer()} .
+
+-type shell_ch_msg() :: {shell,
+ ssh:channel_id(),
+ want_reply()
+ } .
+-type window_change_ch_msg() :: {window_change,
+ ssh:channel_id(),
+ CharWidth :: non_neg_integer(),
+ RowHeight :: non_neg_integer(),
+ PixelWidth :: non_neg_integer(),
+ PixelHeight :: non_neg_integer()
+ } .
+-type exec_ch_msg() :: {exec,
+ ssh:channel_id(),
+ want_reply(),
+ Command :: string()
+ } .
+
+%%% This function is soley to convince all
+%%% checks that the type event() exists...
+-export([dummy/1]).
+-spec dummy(event()) -> false.
+dummy(_) -> false.
+
%%--------------------------------------------------------------------
%%% API
%%--------------------------------------------------------------------
@@ -77,14 +185,21 @@
%% application, a system command, or some built-in subsystem.
%% --------------------------------------------------------------------
--spec session_channel(connection_ref(), timeout()) ->
- {ok, channel_id()} | {error, timeout | closed}.
+-spec session_channel(ConnectionRef, Timeout) -> Result when
+ ConnectionRef :: ssh:connection_ref(),
+ Timeout :: timeout(),
+ Result :: {ok, ssh:channel_id()} | {error, reason()} .
session_channel(ConnectionHandler, Timeout) ->
session_channel(ConnectionHandler, ?DEFAULT_WINDOW_SIZE, ?DEFAULT_PACKET_SIZE, Timeout).
--spec session_channel(connection_ref(), integer(), integer(), timeout()) ->
- {ok, channel_id()} | {error, timeout | closed}.
+
+-spec session_channel(ConnectionRef, InitialWindowSize, MaxPacketSize, Timeout) -> Result when
+ ConnectionRef :: ssh:connection_ref(),
+ InitialWindowSize :: pos_integer(),
+ MaxPacketSize :: pos_integer(),
+ Timeout :: timeout(),
+ Result :: {ok, ssh:channel_id()} | {error, reason()} .
session_channel(ConnectionHandler, InitialWindowSize, MaxPacketSize, Timeout) ->
case ssh_connection_handler:open_channel(ConnectionHandler, "session", <<>>,
@@ -100,8 +215,11 @@ session_channel(ConnectionHandler, InitialWindowSize, MaxPacketSize, Timeout) ->
%% Description: Will request that the server start the
%% execution of the given command.
%%--------------------------------------------------------------------
--spec exec(connection_ref(), channel_id(), string(), timeout()) ->
- success | failure | {error, timeout | closed}.
+-spec exec(ConnectionRef, ChannelId, Command, Timeout) -> result() when
+ ConnectionRef :: ssh:connection_ref(),
+ ChannelId :: ssh:channel_id(),
+ Command :: string(),
+ Timeout :: timeout().
exec(ConnectionHandler, ChannelId, Command, TimeOut) ->
ssh_connection_handler:request(ConnectionHandler, self(), ChannelId, "exec",
@@ -112,8 +230,10 @@ exec(ConnectionHandler, ChannelId, Command, TimeOut) ->
%% defined in /etc/passwd in UNIX systems) be started at the other
%% end.
%%--------------------------------------------------------------------
--spec shell(connection_ref(), channel_id()) ->
- ok | success | failure | {error, timeout}.
+-spec shell(ConnectionRef, ChannelId) -> Result when
+ ConnectionRef :: ssh:connection_ref(),
+ ChannelId :: ssh:channel_id(),
+ Result :: ok | success | failure | {error, timeout} .
shell(ConnectionHandler, ChannelId) ->
ssh_connection_handler:request(ConnectionHandler, self(), ChannelId,
@@ -122,8 +242,11 @@ shell(ConnectionHandler, ChannelId) ->
%%
%% Description: Executes a predefined subsystem.
%%--------------------------------------------------------------------
--spec subsystem(connection_ref(), channel_id(), string(), timeout()) ->
- success | failure | {error, timeout | closed}.
+-spec subsystem(ConnectionRef, ChannelId, Subsystem, Timeout) -> result() when
+ ConnectionRef :: ssh:connection_ref(),
+ ChannelId :: ssh:channel_id(),
+ Subsystem :: string(),
+ Timeout :: timeout().
subsystem(ConnectionHandler, ChannelId, SubSystem, TimeOut) ->
ssh_connection_handler:request(ConnectionHandler, self(),
@@ -134,12 +257,13 @@ subsystem(ConnectionHandler, ChannelId, SubSystem, TimeOut) ->
%%--------------------------------------------------------------------
-spec send(connection_ref(), channel_id(), iodata()) ->
ok | {error, timeout | closed}.
+
send(ConnectionHandler, ChannelId, Data) ->
send(ConnectionHandler, ChannelId, 0, Data, infinity).
--spec send(connection_ref(), channel_id(), integer()| iodata(), timeout() | iodata()) ->
- ok | {error, timeout | closed}.
+-spec send(connection_ref(), channel_id(), iodata(), timeout()) -> ok | {error, reason()};
+ (connection_ref(), channel_id(), ssh_data_type_code(), iodata()) -> ok | {error, reason()}.
send(ConnectionHandler, ChannelId, Data, TimeOut) when is_integer(TimeOut) ->
send(ConnectionHandler, ChannelId, 0, Data, TimeOut);
@@ -151,14 +275,15 @@ send(ConnectionHandler, ChannelId, Type, Data) ->
send(ConnectionHandler, ChannelId, Type, Data, infinity).
--spec send(connection_ref(), channel_id(), integer(), iodata(), timeout()) ->
- ok | {error, timeout | closed}.
+-spec send(connection_ref(), channel_id(), ssh_data_type_code(), iodata(), timeout()) -> ok | {error, reason()}.
send(ConnectionHandler, ChannelId, Type, Data, TimeOut) ->
ssh_connection_handler:send(ConnectionHandler, ChannelId,
Type, Data, TimeOut).
%%--------------------------------------------------------------------
--spec send_eof(connection_ref(), channel_id()) -> ok | {error, closed}.
+-spec send_eof(ConnectionRef, ChannelId) -> ok | {error, closed} when
+ ConnectionRef :: ssh:connection_ref(),
+ ChannelId :: ssh:channel_id().
%%
%%
%% Description: Sends eof on the channel <ChannelId>.
@@ -167,7 +292,10 @@ send_eof(ConnectionHandler, Channel) ->
ssh_connection_handler:send_eof(ConnectionHandler, Channel).
%%--------------------------------------------------------------------
--spec adjust_window(connection_ref(), channel_id(), integer()) -> ok.
+-spec adjust_window(ConnectionRef, ChannelId, NumOfBytes) -> ok when
+ ConnectionRef :: ssh:connection_ref(),
+ ChannelId :: ssh:channel_id(),
+ NumOfBytes :: integer().
%%
%%
%% Description: Adjusts the ssh flowcontrol window.
@@ -176,8 +304,12 @@ adjust_window(ConnectionHandler, Channel, Bytes) ->
ssh_connection_handler:adjust_window(ConnectionHandler, Channel, Bytes).
%%--------------------------------------------------------------------
--spec setenv(connection_ref(), channel_id(), string(), string(), timeout()) ->
- success | failure | {error, timeout | closed}.
+-spec setenv(ConnectionRef, ChannelId, Var, Value, Timeout) -> result() when
+ ConnectionRef :: ssh:connection_ref(),
+ ChannelId :: ssh:channel_id(),
+ Var :: string(),
+ Value :: string(),
+ Timeout :: timeout().
%%
%%
%% Description: Environment variables may be passed to the shell/command to be
@@ -189,7 +321,9 @@ setenv(ConnectionHandler, ChannelId, Var, Value, TimeOut) ->
%%--------------------------------------------------------------------
--spec close(connection_ref(), channel_id()) -> ok.
+-spec close(ConnectionRef, ChannelId) -> ok when
+ ConnectionRef :: ssh:connection_ref(),
+ ChannelId :: ssh:channel_id().
%%
%%
%% Description: Sends a close message on the channel <ChannelId>.
@@ -198,7 +332,11 @@ close(ConnectionHandler, ChannelId) ->
ssh_connection_handler:close(ConnectionHandler, ChannelId).
%%--------------------------------------------------------------------
--spec reply_request(connection_ref(), boolean(), success | failure, channel_id()) -> ok.
+-spec reply_request(ConnectionRef, WantReply, Status, ChannelId) -> ok when
+ ConnectionRef :: ssh:connection_ref(),
+ WantReply :: boolean(),
+ Status :: req_status(),
+ ChannelId :: ssh:channel_id().
%%
%%
%% Description: Send status replies to requests that want such replies.
@@ -211,15 +349,20 @@ reply_request(_,false, _, _) ->
%%--------------------------------------------------------------------
%% Description: Sends a ssh connection protocol pty_req.
%%--------------------------------------------------------------------
--spec ptty_alloc(connection_ref(), channel_id(), proplists:proplist()) ->
- success | failure | {error, timeout}.
+-spec ptty_alloc(ConnectionRef, ChannelId, Options) -> result() when
+ ConnectionRef :: ssh:connection_ref(),
+ ChannelId :: ssh:channel_id(),
+ Options :: proplists:proplist().
ptty_alloc(ConnectionHandler, Channel, Options) ->
ptty_alloc(ConnectionHandler, Channel, Options, infinity).
--spec ptty_alloc(connection_ref(), channel_id(), proplists:proplist(), timeout()) ->
- success | failure | {error, timeout | closed}.
+-spec ptty_alloc(ConnectionRef, ChannelId, Options, Timeout) -> result() when
+ ConnectionRef :: ssh:connection_ref(),
+ ChannelId :: ssh:channel_id(),
+ Options :: proplists:proplist(),
+ Timeout :: timeout().
ptty_alloc(ConnectionHandler, Channel, Options0, TimeOut) ->
TermData = backwards_compatible(Options0, []), % FIXME
@@ -252,6 +395,10 @@ signal(ConnectionHandler, Channel, Sig) ->
"signal", false, [?string(Sig)], 0).
+-spec exit_status(ConnectionRef, ChannelId, Status) -> ok when
+ ConnectionRef :: ssh:connection_ref(),
+ ChannelId :: ssh:channel_id(),
+ Status :: integer().
exit_status(ConnectionHandler, Channel, Status) ->
ssh_connection_handler:request(ConnectionHandler, Channel,
"exit-status", false, [?uint32(Status)], 0).
@@ -501,19 +648,14 @@ handle_msg(#ssh_msg_channel_request{recipient_channel = ChannelId,
#channel{remote_id=RemoteId} = Channel =
ssh_client_channel:cache_lookup(Cache, ChannelId),
Reply =
- try
- start_subsystem(SsName, Connection, Channel,
- {subsystem, ChannelId, WantReply, binary_to_list(SsName)})
- of
+ case start_subsystem(SsName, Connection, Channel,
+ {subsystem, ChannelId, WantReply, binary_to_list(SsName)}) of
{ok, Pid} ->
erlang:monitor(process, Pid),
ssh_client_channel:cache_update(Cache, Channel#channel{user=Pid}),
channel_success_msg(RemoteId);
{error,_Error} ->
channel_failure_msg(RemoteId)
- catch
- _:_ ->
- channel_failure_msg(RemoteId)
end,
{[{connection_reply,Reply}], Connection};
@@ -713,29 +855,6 @@ request_success_msg(Data) ->
%%%----------------------------------------------------------------
%%%
%%%
-bind(IP, Port, ChannelPid, Connection) ->
- Binds = [{{IP, Port}, ChannelPid}
- | lists:keydelete({IP, Port}, 1,
- Connection#connection.port_bindings)],
- Connection#connection{port_bindings = Binds}.
-
-unbind(IP, Port, Connection) ->
- Connection#connection{
- port_bindings =
- lists:keydelete({IP, Port}, 1,
- Connection#connection.port_bindings)}.
-unbind_channel(ChannelPid, Connection) ->
- Binds = [{Bind, ChannelP} || {Bind, ChannelP}
- <- Connection#connection.port_bindings,
- ChannelP =/= ChannelPid],
- Connection#connection{port_bindings = Binds}.
-
-bound_channel(IP, Port, Connection) ->
- case lists:keysearch({IP, Port}, 1, Connection#connection.port_bindings) of
- {value, {{IP, Port}, ChannelPid}} -> ChannelPid;
- _ -> undefined
- end.
-
encode_ip(Addr) when is_tuple(Addr) ->
case catch inet_parse:ntoa(Addr) of
{'EXIT',_} -> false;
@@ -797,7 +916,7 @@ start_cli(#connection{options = Options,
no_cli ->
{error, cli_disabled};
{CbModule, Args} ->
- start_channel(CbModule, ChannelId, Args, SubSysSup, Exec, Options)
+ ssh_subsystem_sup:start_channel(server, SubSysSup, self(), CbModule, ChannelId, Args, Exec, Options)
end.
@@ -807,37 +926,15 @@ start_subsystem(BinName, #connection{options = Options,
Name = binary_to_list(BinName),
case check_subsystem(Name, Options) of
{Callback, Opts} when is_atom(Callback), Callback =/= none ->
- start_channel(Callback, ChannelId, Opts, SubSysSup, Options);
- {Other, _} when Other =/= none ->
+ ssh_subsystem_sup:start_channel(server, SubSysSup, self(), Callback, ChannelId, Opts, undefined, Options);
+ {none, _} ->
+ {error, bad_subsystem};
+ {_, _} ->
{error, legacy_option_not_supported}
end.
%%% Helpers for starting cli/subsystems
-start_channel(Cb, Id, Args, SubSysSup, Opts) ->
- start_channel(Cb, Id, Args, SubSysSup, undefined, Opts).
-
-start_channel(Cb, Id, Args, SubSysSup, Exec, Opts) ->
- ChannelSup = ssh_subsystem_sup:channel_supervisor(SubSysSup),
- case max_num_channels_not_exceeded(ChannelSup, Opts) of
- true ->
- case ssh_server_channel_sup:start_child(ChannelSup, Cb, Id, Args, Exec) of
- {error,{Error,_Info}} ->
- throw(Error);
- Others ->
- Others
- end;
- false ->
- throw(max_num_channels_exceeded)
- end.
-
-max_num_channels_not_exceeded(ChannelSup, Opts) ->
- MaxNumChannels = ?GET_OPT(max_channels, Opts),
- NumChannels = length([x || {_,_,worker,[ssh_server_channel]} <-
- supervisor:which_children(ChannelSup)]),
- %% Note that NumChannels is BEFORE starting a new one
- NumChannels < MaxNumChannels.
-
check_subsystem("sftp"= SsName, Options) ->
case ?GET_OPT(subsystems, Options) of
no_subsys -> % FIXME: Can 'no_subsys' ever be matched?
@@ -1176,13 +1273,13 @@ handle_cli_msg(C0, ChId, Reply0) ->
Ch0 = ssh_client_channel:cache_lookup(Cache, ChId),
case Ch0#channel.user of
undefined ->
- case (catch start_cli(C0, ChId)) of
+ case start_cli(C0, ChId) of
{ok, Pid} ->
erlang:monitor(process, Pid),
Ch = Ch0#channel{user = Pid},
ssh_client_channel:cache_update(Cache, Ch),
reply_msg(Ch, C0, Reply0);
- _Other ->
+ {error, _Error} ->
Reply = {connection_reply, channel_failure_msg(Ch0#channel.remote_id)},
{[Reply], C0}
end;
diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl
index 8f32966a12..69d3c3b18f 100644
--- a/lib/ssh/src/ssh_connection_handler.erl
+++ b/lib/ssh/src/ssh_connection_handler.erl
@@ -48,6 +48,7 @@
-export([start_connection/4,
available_hkey_algorithms/2,
open_channel/6,
+ start_channel/5,
request/6, request/7,
reply_request/3,
send/5,
@@ -126,35 +127,29 @@ stop(ConnectionHandler)->
timeout()
) -> {ok, connection_ref()} | {error, term()}.
%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
-start_connection(client = Role, Socket, Options, Timeout) ->
+start_connection(Role, Socket, Options, Timeout) ->
try
- {ok, Pid} = sshc_sup:start_child([Role, Socket, Options]),
- ok = socket_control(Socket, Pid, Options),
- handshake(Pid, erlang:monitor(process,Pid), Timeout)
- catch
- exit:{noproc, _} ->
- {error, ssh_not_started};
- _:Error ->
- {error, Error}
- end;
-
-start_connection(server = Role, Socket, Options, Timeout) ->
- try
- case ?GET_OPT(parallel_login, Options) of
- true ->
- 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 ->
- ChildPid = start_the_connection_child(self(), Role, Socket, Options),
- handshake(ChildPid, erlang:monitor(process,ChildPid), Timeout)
- end
+ case Role of
+ client ->
+ ChildPid = start_the_connection_child(self(), Role, Socket, Options),
+ handshake(ChildPid, erlang:monitor(process,ChildPid), Timeout);
+ server ->
+ case ?GET_OPT(parallel_login, Options) of
+ true ->
+ 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 ->
+ ChildPid = start_the_connection_child(self(), Role, Socket, Options),
+ handshake(ChildPid, erlang:monitor(process,ChildPid), Timeout)
+ end
+ end
catch
exit:{noproc, _} ->
{error, ssh_not_started};
@@ -176,6 +171,8 @@ disconnect(Code, DetailedText, Module, Line) ->
[{next_event, internal, {send_disconnect, Code, DetailedText, Module, Line}}]}).
%%--------------------------------------------------------------------
+%%% Open a channel in the connection to the peer, that is, do the ssh
+%%% signalling with the peer.
-spec open_channel(connection_ref(),
string(),
iodata(),
@@ -195,6 +192,18 @@ open_channel(ConnectionHandler,
Timeout}).
%%--------------------------------------------------------------------
+%%% Start a channel handling process in the superviser tree
+-spec start_channel(connection_ref(), atom(), channel_id(), list(), term()) ->
+ {ok, pid()} | {error, term()}.
+
+%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+start_channel(ConnectionHandler, CallbackModule, ChannelId, Args, Exec) ->
+ {ok, {SubSysSup,Role,Opts}} = call(ConnectionHandler, get_misc),
+ ssh_subsystem_sup:start_channel(Role, SubSysSup,
+ ConnectionHandler, CallbackModule, ChannelId,
+ Args, Exec, Opts).
+
+%%--------------------------------------------------------------------
-spec request(connection_ref(),
pid(),
channel_id(),
@@ -279,10 +288,13 @@ get_print_info(ConnectionHandler) ->
call(ConnectionHandler, get_print_info, 1000).
%%--------------------------------------------------------------------
--spec connection_info(connection_ref(),
- [atom()]
- ) -> proplists:proplist().
-%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+connection_info(ConnectionHandler, []) ->
+ connection_info(ConnectionHandler, conn_info_keys());
+connection_info(ConnectionHandler, Key) when is_atom(Key) ->
+ case connection_info(ConnectionHandler, [Key]) of
+ [{Key,Val}] -> {Key,Val};
+ Other -> Other
+ end;
connection_info(ConnectionHandler, Options) ->
call(ConnectionHandler, {connection_info, Options}).
@@ -378,24 +390,45 @@ alg(ConnectionHandler) ->
%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
init_connection_handler(Role, Socket, Opts) ->
case init([Role, Socket, Opts]) of
- {ok, StartState, D} ->
+ {ok, StartState, D} when Role == server ->
process_flag(trap_exit, true),
gen_statem:enter_loop(?MODULE,
[], %%[{debug,[trace,log,statistics,debug]} ], %% []
StartState,
D);
- {stop, Error} ->
+ {ok, StartState, D0=#data{connection_state=C}} when Role == client ->
+ process_flag(trap_exit, true),
Sups = ?GET_INTERNAL_OPT(supervisors, Opts),
- C = #connection{system_supervisor = proplists:get_value(system_sup, Sups),
- sub_system_supervisor = proplists:get_value(subsystem_sup, Sups),
- connection_supervisor = proplists:get_value(connection_sup, Sups)
- },
+ D = D0#data{connection_state =
+ C#connection{system_supervisor = proplists:get_value(system_sup, Sups),
+ sub_system_supervisor = proplists:get_value(subsystem_sup, Sups),
+ connection_supervisor = proplists:get_value(connection_sup, Sups)
+ }},
+ gen_statem:enter_loop(?MODULE,
+ [], %%[{debug,[trace,log,statistics,debug]} ], %% []
+ StartState,
+ D);
+
+ {stop, Error} ->
+ D = try
+ %% Only servers have supervisorts defined in Opts
+ Sups = ?GET_INTERNAL_OPT(supervisors, Opts),
+ #connection{system_supervisor = proplists:get_value(system_sup, Sups),
+ sub_system_supervisor = proplists:get_value(subsystem_sup, Sups),
+ connection_supervisor = proplists:get_value(connection_sup, Sups)
+ }
+ of
+ C ->
+ #data{connection_state=C}
+ catch
+ _:_ ->
+ #data{connection_state=#connection{}}
+ end,
gen_statem:enter_loop(?MODULE,
[],
{init_error,Error},
- #data{connection_state=C,
- socket=Socket})
+ D#data{socket=Socket})
end.
@@ -406,7 +439,6 @@ init([Role,Socket,Opts]) ->
{Protocol, Callback, CloseTag} = ?GET_OPT(transport, Opts),
C = #connection{channel_cache = ssh_client_channel:cache_create(),
channel_id_seed = 0,
- port_bindings = [],
requests = [],
options = Opts},
D0 = #data{starter = ?GET_INTERNAL_OPT(user_pid, Opts),
@@ -1240,6 +1272,13 @@ handle_event({call,From}, {eof, ChannelId}, StateName, D0)
{keep_state, D0, [{reply,From,{error,closed}}]}
end;
+handle_event({call,From}, get_misc, StateName,
+ #data{connection_state = #connection{options = Opts}} = D) when ?CONNECTED(StateName) ->
+ Sups = ?GET_INTERNAL_OPT(supervisors, Opts),
+ SubSysSup = proplists:get_value(subsystem_sup, Sups),
+ Reply = {ok, {SubSysSup, role(StateName), Opts}},
+ {keep_state, D, [{reply,From,Reply}]};
+
handle_event({call,From},
{open, ChannelPid, Type, InitialWindowSize, MaxPacketSize, Data, Timeout},
StateName,
@@ -1550,7 +1589,7 @@ terminate({shutdown,"Connection closed"}, _StateName, D) ->
terminate({shutdown,{init,Reason}}, StateName, D) ->
%% Error in initiation. "This error should not occur".
- log(error, D, io_lib:format("Shutdown in init (StateName=~p): ~p~n",[StateName,Reason])),
+ log(error, D, "Shutdown in init (StateName=~p): ~p~n", [StateName,Reason]),
stop_subsystem(D),
close_transport(D);
@@ -1654,17 +1693,36 @@ start_the_connection_child(UserPid, Role, Socket, Options0) ->
Sups = ?GET_INTERNAL_OPT(supervisors, Options0),
ConnectionSup = proplists:get_value(connection_sup, Sups),
Options = ?PUT_INTERNAL_OPT({user_pid,UserPid}, Options0),
- {ok, Pid} = ssh_connection_sup:start_child(ConnectionSup, [Role, Socket, Options]),
- ok = socket_control(Socket, Pid, Options),
+ InitArgs = [Role, Socket, Options],
+ {ok, Pid} = ssh_connection_sup:start_child(ConnectionSup, InitArgs),
+ ok = socket_control(Socket, Pid, Options), % transfer the Socket ownership in a controlled way.
Pid.
%%--------------------------------------------------------------------
%% Stopping
-stop_subsystem(#data{connection_state =
+stop_subsystem(#data{ssh_params =
+ #ssh{role = Role},
+ connection_state =
#connection{system_supervisor = SysSup,
- sub_system_supervisor = SubSysSup}}) when is_pid(SubSysSup) ->
- ssh_system_sup:stop_subsystem(SysSup, SubSysSup);
+ sub_system_supervisor = SubSysSup}
+ }) when is_pid(SysSup) andalso is_pid(SubSysSup) ->
+ process_flag(trap_exit, false),
+ C = self(),
+ spawn(fun() ->
+ Mref = erlang:monitor(process, C),
+ receive
+ {'DOWN', Mref, process, C, _Info} -> ok
+ after
+ 10000 -> ok
+ end,
+ case Role of
+ client ->
+ ssh_system_sup:stop_system(Role, SysSup);
+ _ ->
+ ssh_system_sup:stop_subsystem(SysSup, SubSysSup)
+ end
+ end);
stop_subsystem(_) ->
ok.
@@ -1756,6 +1814,8 @@ call(FsmPid, Event, Timeout) ->
exit:{normal, _R} ->
{error, closed};
exit:{{shutdown, _R},_} ->
+ {error, closed};
+ exit:{shutdown, _R} ->
{error, closed}
end.
@@ -1952,12 +2012,12 @@ send_disconnect(Code, Reason, DetailedText, Module, Line, StateName, D0) ->
call_disconnectfun_and_log_cond(LogMsg, DetailedText, Module, Line, StateName, D) ->
case disconnect_fun(LogMsg, D) of
void ->
- log(info, D,
- io_lib:format("~s~n"
- "State = ~p~n"
- "Module = ~p, Line = ~p.~n"
- "Details:~n ~s~n",
- [LogMsg, StateName, Module, Line, DetailedText]));
+ log(info, D,
+ "~s~n"
+ "State = ~p~n"
+ "Module = ~p, Line = ~p.~n"
+ "Details:~n ~s~n",
+ [LogMsg, StateName, Module, Line, DetailedText]);
_ ->
ok
end.
@@ -1986,18 +2046,57 @@ counterpart_versions(NumVsn, StrVsn, #ssh{role = client} = Ssh) ->
Ssh#ssh{s_vsn = NumVsn , s_version = StrVsn}.
%%%----------------------------------------------------------------
+conn_info_keys() ->
+ [client_version,
+ server_version,
+ peer,
+ user,
+ sockname,
+ options,
+ algorithms,
+ channels
+ ].
+
conn_info(client_version, #data{ssh_params=S}) -> {S#ssh.c_vsn, S#ssh.c_version};
conn_info(server_version, #data{ssh_params=S}) -> {S#ssh.s_vsn, S#ssh.s_version};
conn_info(peer, #data{ssh_params=S}) -> S#ssh.peer;
conn_info(user, D) -> D#data.auth_user;
conn_info(sockname, #data{ssh_params=S}) -> S#ssh.local;
+conn_info(options, #data{ssh_params=#ssh{opts=Opts}}) -> lists:sort(
+ maps:to_list(
+ ssh_options:keep_set_options(
+ client,
+ ssh_options:keep_user_options(client,Opts))));
+conn_info(algorithms, #data{ssh_params=#ssh{algorithms=A}}) -> conn_info_alg(A);
+conn_info(channels, D) -> try conn_info_chans(ets:tab2list(cache(D)))
+ catch _:_ -> undefined
+ end;
%% dbg options ( = not documented):
-conn_info(socket, D) -> D#data.socket;
+conn_info(socket, D) -> D#data.socket;
conn_info(chan_ids, D) ->
ssh_client_channel:cache_foldl(fun(#channel{local_id=Id}, Acc) ->
[Id | Acc]
end, [], cache(D)).
+conn_info_chans(Chs) ->
+ Fs = record_info(fields, channel),
+ [lists:zip(Fs, tl(tuple_to_list(Ch))) || Ch=#channel{} <- Chs].
+
+conn_info_alg(AlgTup) ->
+ [alg|Vs] = tuple_to_list(AlgTup),
+ Fs = record_info(fields, alg),
+ [{K,V} || {K,V} <- lists:zip(Fs,Vs),
+ lists:member(K,[kex,
+ hkey,
+ encrypt,
+ decrypt,
+ send_mac,
+ recv_mac,
+ compress,
+ decompress,
+ send_ext_info,
+ recv_ext_info])].
+
%%%----------------------------------------------------------------
chann_info(recv_window, C) ->
{{win_size, C#channel.recv_window_size},
@@ -2021,6 +2120,9 @@ fold_keys(Keys, Fun, Extra) ->
end, [], Keys).
%%%----------------------------------------------------------------
+log(Tag, D, Format, Args) ->
+ log(Tag, D, io_lib:format(Format,Args)).
+
log(Tag, D, Reason) ->
case atom_to_list(Tag) of % Dialyzer-technical reasons...
"error" -> do_log(error_msg, Reason, D);
@@ -2028,36 +2130,56 @@ log(Tag, D, Reason) ->
"info" -> do_log(info_msg, Reason, D)
end.
-do_log(F, Reason, #data{ssh_params = #ssh{role = Role} = S
- }) ->
- VSN =
- case application:get_key(ssh,vsn) of
- {ok,Vsn} -> Vsn;
- undefined -> ""
- end,
- PeerVersion =
- case Role of
- server -> S#ssh.c_version;
- client -> S#ssh.s_version
- end,
- CryptoInfo =
- try
- [{_,_,CI}] = crypto:info_lib(),
- <<"(",CI/binary,")">>
+
+do_log(F, Reason0, #data{ssh_params = S}) ->
+ Reason =
+ try io_lib:format("~s",[Reason0])
+ of _ -> Reason0
catch
- _:_ -> ""
- end,
- Other =
- case Role of
- server -> "Client";
- client -> "Server"
+ _:_ -> io_lib:format("~p",[Reason0])
end,
- error_logger:F("Erlang SSH ~p ~s ~s.~n"
- "~s: ~p~n"
- "~s~n",
- [Role, VSN, CryptoInfo,
- Other, PeerVersion,
- Reason]).
+ case S of
+ #ssh{role = Role} when Role==server ;
+ Role==client ->
+ {PeerRole,PeerVersion} =
+ case Role of
+ server -> {"Client", S#ssh.c_version};
+ client -> {"Server", S#ssh.s_version}
+ end,
+ error_logger:F("Erlang SSH ~p ~s ~s.~n"
+ "~s: ~p~n"
+ "~s~n",
+ [Role,
+ ssh_log_version(), crypto_log_info(),
+ PeerRole, PeerVersion,
+ Reason]);
+ _ ->
+ error_logger:F("Erlang SSH ~s ~s.~n"
+ "~s~n",
+ [ssh_log_version(), crypto_log_info(),
+ Reason])
+ end.
+
+crypto_log_info() ->
+ try
+ [{_,_,CI}] = crypto:info_lib(),
+ case crypto:info_fips() of
+ enabled ->
+ <<"(",CI/binary,". FIPS enabled)">>;
+ not_enabled ->
+ <<"(",CI/binary,". FIPS available but not enabled)">>;
+ _ ->
+ <<"(",CI/binary,")">>
+ end
+ catch
+ _:_ -> ""
+ end.
+
+ssh_log_version() ->
+ case application:get_key(ssh,vsn) of
+ {ok,Vsn} -> Vsn;
+ undefined -> ""
+ end.
%%%----------------------------------------------------------------
not_connected_filter({connection_reply, _Data}) -> true;
diff --git a/lib/ssh/src/ssh_info.erl b/lib/ssh/src/ssh_info.erl
index 79cd95e422..91365205aa 100644
--- a/lib/ssh/src/ssh_info.erl
+++ b/lib/ssh/src/ssh_info.erl
@@ -79,8 +79,8 @@ print_clients() ->
lists:map(fun print_client/1,
supervisor:which_children(sshc_sup))
catch
- C:E ->
- io_lib:format('***print_clients FAILED: ~p:~p~n',[C,E])
+ C:E:S ->
+ io_lib:format('***print_clients FAILED: ~p:~p,~n ~p~n',[C,E,S])
end.
print_client({undefined,Pid,supervisor,[ssh_connection_handler]}) ->
@@ -94,9 +94,9 @@ print_client({undefined,Pid,supervisor,[ssh_connection_handler]}) ->
io_lib:format(?INDENT?INDENT?INDENT"No channels~n",[])
end];
-print_client(Other) ->
- io_lib:format(" [[Other 1: ~p]]~n",[Other]).
-
+print_client({{client,ssh_system_sup,_,_,_},Pid,supervisor,[ssh_system_sup]}) when is_pid(Pid) ->
+ lists:map(fun print_system_sup/1,
+ supervisor:which_children(Pid)).
%%%================================================================
print_servers() ->
@@ -104,8 +104,8 @@ print_servers() ->
lists:map(fun print_server/1,
supervisor:which_children(sshd_sup))
catch
- C:E ->
- io_lib:format('***print_servers FAILED: ~p:~p~n',[C,E])
+ C:E:S ->
+ io_lib:format('***print_servers FAILED: ~p:~p,~n ~p~n',[C,E,S])
end.
@@ -140,22 +140,33 @@ print_system_sup({{ssh_acceptor_sup,_LocalHost,_LocalPort,_Profile}, Pid, superv
-print_channels({{server,ssh_server_channel_sup,_,_},Pid,supervisor,[ssh_server_channel_sup]}) when is_pid(Pid) ->
+print_channels({{Role,ssh_channel_sup,_,_},Pid,supervisor,[ssh_channel_sup]}) when is_pid(Pid) ->
+ ChanBehaviour =
+ case Role of
+ server -> ssh_server_channel;
+ client -> ssh_client_channel
+ end,
Children = supervisor:which_children(Pid),
- ChannelPids = [P || {R,P,worker,[ssh_server_channel]} <- Children,
+ ChannelPids = [P || {R,P,worker,[Mod]} <- Children,
+ ChanBehaviour == Mod,
is_pid(P),
is_reference(R)],
case ChannelPids of
[] -> io_lib:format(?INDENT?INDENT"No channels~n",[]);
[Ch1Pid|_] ->
- {{ConnManager,_}, _Str} = ssh_server_channel:get_print_info(Ch1Pid),
+ {{ConnManager,_}, _Str} = ChanBehaviour:get_print_info(Ch1Pid),
{{_,Remote},_} = ssh_connection_handler:get_print_info(ConnManager),
[io_lib:format(?INDENT?INDENT"Remote: ~s ConnectionRef = ~p~n",[fmt_host_port(Remote),ConnManager]),
lists:map(fun print_ch/1, ChannelPids)
]
end;
-print_channels({{server,ssh_connection_sup,_,_},Pid,supervisor,[ssh_connection_sup]}) when is_pid(Pid) ->
- []. % The supervisor of the connections socket owning process
+print_channels({{_Role,ssh_connection_sup,_,_},Pid,supervisor,[ssh_connection_sup]}) when is_pid(Pid) ->
+ []; % The supervisor of the connections socket owning process
+
+print_channels({Ref,Pid,supervisor,[ssh_tcpip_forward_acceptor_sup]}) when is_pid(Pid),
+ is_reference(Ref) ->
+ []. % The supervisor of the forward_acceptor process
+
print_ch(Pid) ->
try
diff --git a/lib/ssh/src/ssh_options.erl b/lib/ssh/src/ssh_options.erl
index 1010c9be55..39f23a8b8a 100644
--- a/lib/ssh/src/ssh_options.erl
+++ b/lib/ssh/src/ssh_options.erl
@@ -29,7 +29,9 @@
get_value/5, get_value/6,
put_value/5,
delete_key/5,
- handle_options/2
+ handle_options/2,
+ keep_user_options/2,
+ keep_set_options/2
]).
-export_type([private_options/0
@@ -42,14 +44,14 @@
-type option_class() :: internal_options | socket_options | user_options .
--type option_declaration() :: #{class := user_options,
- chk := fun((any) -> boolean() | {true,any()}),
+-type option_declaration() :: #{class := user_option | undoc_user_option,
+ chk := fun((any()) -> boolean() | {true,any()}),
default => any()
}.
-type option_key() :: atom().
--type option_declarations() :: #{ {option_key(),def} := option_declaration() }.
+-type option_declarations() :: #{ option_key() := option_declaration() }.
-type error() :: {error,{eoptions,any()}} .
@@ -166,7 +168,7 @@ handle_options(Role, PropList0, Opts0) when is_map(Opts0),
OptionDefinitions = default(Role),
InitialMap =
maps:fold(
- fun({K,def}, #{default:=V}, M) -> M#{K=>V};
+ fun(K, #{default:=V}, M) -> M#{K=>V};
(_,_,M) -> M
end,
Opts0#{user_options =>
@@ -192,7 +194,7 @@ handle_options(Role, PropList0, Opts0) when is_map(Opts0),
check_fun(Key, Defs) ->
- #{chk := Fun} = maps:get({Key,def}, Defs),
+ #{chk := Fun} = maps:get(Key, Defs),
Fun.
%%%================================================================
@@ -232,10 +234,10 @@ save({Key,Value}, Defs, OptMap) when is_map(OptMap) ->
catch
%% An unknown Key (= not in the definition map) is
%% regarded as an inet option:
- error:{badkey,{inet,def}} ->
+ error:{badkey,inet} ->
%% atomic (= non-tuple) options 'inet' and 'inet6':
OptMap#{socket_options := [Value | maps:get(socket_options,OptMap)]};
- error:{badkey,{Key,def}} ->
+ error:{badkey,Key} ->
OptMap#{socket_options := [{Key,Value} | maps:get(socket_options,OptMap)]};
%% But a Key that is known but the value does not validate
@@ -249,6 +251,35 @@ save(Opt, _Defs, OptMap) when is_map(OptMap) ->
%%%================================================================
%%%
+-spec keep_user_options(client|server, #{}) -> #{}.
+
+keep_user_options(Type, Opts) ->
+ Defs = default(Type),
+ maps:filter(fun(Key, _Value) ->
+ try
+ #{class := Class} = maps:get(Key,Defs),
+ Class == user_option
+ catch
+ _:_ -> false
+ end
+ end, Opts).
+
+
+-spec keep_set_options(client|server, #{}) -> #{}.
+
+keep_set_options(Type, Opts) ->
+ Defs = default(Type),
+ maps:filter(fun(Key, Value) ->
+ try
+ #{default := DefVal} = maps:get(Key,Defs),
+ DefVal =/= Value
+ catch
+ _:_ -> false
+ end
+ end, Opts).
+
+%%%================================================================
+%%%
%%% Default options
%%%
@@ -257,7 +288,7 @@ save(Opt, _Defs, OptMap) when is_map(OptMap) ->
default(server) ->
(default(common))
#{
- {subsystems, def} =>
+ subsystems =>
#{default => [ssh_sftpd:subsystem_spec([])],
chk => fun(L) ->
is_list(L) andalso
@@ -269,42 +300,42 @@ default(server) ->
false
end, L)
end,
- class => user_options
+ class => user_option
},
- {shell, def} =>
+ shell =>
#{default => ?DEFAULT_SHELL,
chk => fun({M,F,A}) -> is_atom(M) andalso is_atom(F) andalso is_list(A);
(V) -> check_function1(V) orelse check_function2(V)
end,
- class => user_options
+ class => user_option
},
- {exec, def} =>
+ exec =>
#{default => undefined,
chk => fun({direct, V}) -> check_function1(V) orelse check_function2(V) orelse check_function3(V);
%% Compatibility (undocumented):
({M,F,A}) -> is_atom(M) andalso is_atom(F) andalso is_list(A);
(V) -> check_function1(V) orelse check_function2(V) orelse check_function3(V)
end,
- class => user_options
+ class => user_option
},
- {ssh_cli, def} =>
+ ssh_cli =>
#{default => undefined,
chk => fun({Cb, As}) -> is_atom(Cb) andalso is_list(As);
(V) -> V == no_cli
end,
- class => user_options
+ class => user_option
},
- {system_dir, def} =>
+ system_dir =>
#{default => "/etc/ssh",
chk => fun(V) -> check_string(V) andalso check_dir(V) end,
- class => user_options
+ class => user_option
},
- {auth_method_kb_interactive_data, def} =>
+ auth_method_kb_interactive_data =>
#{default => undefined, % Default value can be constructed when User is known
chk => fun({S1,S2,S3,B}) ->
check_string(S1) andalso
@@ -314,10 +345,10 @@ default(server) ->
(F) ->
check_function3(F)
end,
- class => user_options
+ class => user_option
},
- {user_passwords, def} =>
+ user_passwords =>
#{default => [],
chk => fun(V) ->
is_list(V) andalso
@@ -326,22 +357,22 @@ default(server) ->
check_string(S2)
end, V)
end,
- class => user_options
+ class => user_option
},
- {password, def} =>
+ password =>
#{default => undefined,
chk => fun check_string/1,
- class => user_options
+ class => user_option
},
- {dh_gex_groups, def} =>
+ dh_gex_groups =>
#{default => undefined,
chk => fun check_dh_gex_groups/1,
- class => user_options
+ class => user_option
},
- {dh_gex_limits, def} =>
+ dh_gex_limits =>
#{default => {0, infinity},
chk => fun({I1,I2}) ->
check_pos_integer(I1) andalso
@@ -350,137 +381,137 @@ default(server) ->
(_) ->
false
end,
- class => user_options
+ class => user_option
},
- {pwdfun, def} =>
+ pwdfun =>
#{default => undefined,
chk => fun(V) -> check_function4(V) orelse check_function2(V) end,
- class => user_options
+ class => user_option
},
- {negotiation_timeout, def} =>
+ negotiation_timeout =>
#{default => 2*60*1000,
chk => fun check_timeout/1,
- class => user_options
+ class => user_option
},
- {max_sessions, def} =>
+ max_sessions =>
#{default => infinity,
chk => fun check_pos_integer/1,
- class => user_options
+ class => user_option
},
- {max_channels, def} =>
+ max_channels =>
#{default => infinity,
chk => fun check_pos_integer/1,
- class => user_options
+ class => user_option
},
- {parallel_login, def} =>
+ parallel_login =>
#{default => false,
chk => fun erlang:is_boolean/1,
- class => user_options
+ class => user_option
},
- {minimal_remote_max_packet_size, def} =>
+ minimal_remote_max_packet_size =>
#{default => 0,
chk => fun check_pos_integer/1,
- class => user_options
+ class => user_option
},
- {failfun, def} =>
+ failfun =>
#{default => fun(_,_,_) -> void end,
chk => fun(V) -> check_function3(V) orelse
check_function2(V) % Backwards compatibility
end,
- class => user_options
+ class => user_option
},
- {connectfun, def} =>
+ connectfun =>
#{default => fun(_,_,_) -> void end,
chk => fun check_function3/1,
- class => user_options
+ class => user_option
},
%%%%% Undocumented
- {infofun, def} =>
+ infofun =>
#{default => fun(_,_,_) -> void end,
chk => fun(V) -> check_function3(V) orelse
check_function2(V) % Backwards compatibility
end,
- class => user_options
+ class => undoc_user_option
}
};
default(client) ->
(default(common))
#{
- {dsa_pass_phrase, def} =>
+ dsa_pass_phrase =>
#{default => undefined,
chk => fun check_string/1,
- class => user_options
+ class => user_option
},
- {rsa_pass_phrase, def} =>
+ rsa_pass_phrase =>
#{default => undefined,
chk => fun check_string/1,
- class => user_options
+ class => user_option
},
- {ecdsa_pass_phrase, def} =>
+ ecdsa_pass_phrase =>
#{default => undefined,
chk => fun check_string/1,
- class => user_options
+ class => user_option
},
-%%% Not yet implemented {ed25519_pass_phrase, def} =>
+%%% Not yet implemented ed25519_pass_phrase =>
%%% Not yet implemented #{default => undefined,
%%% Not yet implemented chk => fun check_string/1,
-%%% Not yet implemented class => user_options
+%%% Not yet implemented class => user_option
%%% Not yet implemented },
%%% Not yet implemented
-%%% Not yet implemented {ed448_pass_phrase, def} =>
+%%% Not yet implemented ed448_pass_phrase =>
%%% Not yet implemented #{default => undefined,
%%% Not yet implemented chk => fun check_string/1,
-%%% Not yet implemented class => user_options
+%%% Not yet implemented class => user_option
%%% Not yet implemented },
%%% Not yet implemented
- {silently_accept_hosts, def} =>
+ silently_accept_hosts =>
#{default => false,
chk => fun check_silently_accept_hosts/1,
- class => user_options
+ class => user_option
},
- {user_interaction, def} =>
+ user_interaction =>
#{default => true,
chk => fun erlang:is_boolean/1,
- class => user_options
+ class => user_option
},
- {save_accepted_host, def} =>
+ save_accepted_host =>
#{default => true,
chk => fun erlang:is_boolean/1,
- class => user_options
+ class => user_option
},
- {dh_gex_limits, def} =>
+ dh_gex_limits =>
#{default => {1024, 6144, 8192}, % FIXME: Is this true nowadays?
chk => fun({Min,I,Max}) ->
lists:all(fun check_pos_integer/1,
[Min,I,Max]);
(_) -> false
end,
- class => user_options
+ class => user_option
},
- {connect_timeout, def} =>
+ connect_timeout =>
#{default => infinity,
chk => fun check_timeout/1,
- class => user_options
+ class => user_option
},
- {user, def} =>
+ user =>
#{default =>
begin
Env = case os:type() of
@@ -498,59 +529,59 @@ default(client) ->
end
end,
chk => fun check_string/1,
- class => user_options
+ class => user_option
},
- {password, def} =>
+ password =>
#{default => undefined,
chk => fun check_string/1,
- class => user_options
+ class => user_option
},
- {quiet_mode, def} =>
+ quiet_mode =>
#{default => false,
chk => fun erlang:is_boolean/1,
- class => user_options
+ class => user_option
},
%%%%% Undocumented
- {keyboard_interact_fun, def} =>
+ keyboard_interact_fun =>
#{default => undefined,
chk => fun check_function3/1,
- class => user_options
+ class => undoc_user_option
}
};
default(common) ->
#{
- {user_dir, def} =>
+ user_dir =>
#{default => false, % FIXME: TBD ~/.ssh at time of call when user is known
chk => fun(V) -> check_string(V) andalso check_dir(V) end,
- class => user_options
+ class => user_option
},
- {pref_public_key_algs, def} =>
+ pref_public_key_algs =>
#{default => ssh_transport:default_algorithms(public_key),
chk => fun check_pref_public_key_algs/1,
- class => user_options
+ class => user_option
},
- {preferred_algorithms, def} =>
+ preferred_algorithms =>
#{default => ssh:default_algorithms(),
chk => fun check_preferred_algorithms/1,
- class => user_options
+ class => user_option
},
%% NOTE: This option is supposed to be used only in this very module (?MODULE). There is
%% a final stage in handle_options that "merges" the preferred_algorithms option and this one.
%% The preferred_algorithms is the one to use in the rest of the ssh application!
- {modify_algorithms, def} =>
+ modify_algorithms =>
#{default => undefined, % signals error if unsupported algo in preferred_algorithms :(
chk => fun check_modify_algorithms/1,
- class => user_options
+ class => user_option
},
- {id_string, def} =>
+ id_string =>
#{default => undefined, % FIXME: see ssh_transport:ssh_vsn/0
chk => fun(random) ->
{true, {random,2,5}}; % 2 - 5 random characters
@@ -562,56 +593,49 @@ default(common) ->
(V) ->
check_string(V)
end,
- class => user_options
+ class => user_option
},
- {key_cb, def} =>
+ key_cb =>
#{default => {ssh_file, []},
chk => fun({Mod,Opts}) -> is_atom(Mod) andalso is_list(Opts);
(Mod) when is_atom(Mod) -> {true, {Mod,[]}};
(_) -> false
end,
- class => user_options
+ class => user_option
},
- {profile, def} =>
+ profile =>
#{default => ?DEFAULT_PROFILE,
chk => fun erlang:is_atom/1,
- class => user_options
+ class => user_option
},
- {idle_time, def} =>
+ idle_time =>
#{default => infinity,
chk => fun check_timeout/1,
- class => user_options
+ class => user_option
},
- %% This is a "SocketOption"...
- %% {fd, def} =>
- %% #{default => undefined,
- %% chk => fun erlang:is_integer/1,
- %% class => user_options
- %% },
-
- {disconnectfun, def} =>
+ disconnectfun =>
#{default => fun(_) -> void end,
chk => fun check_function1/1,
- class => user_options
+ class => user_option
},
- {unexpectedfun, def} =>
+ unexpectedfun =>
#{default => fun(_,_) -> report end,
chk => fun check_function2/1,
- class => user_options
+ class => user_option
},
- {ssh_msg_debug_fun, def} =>
+ ssh_msg_debug_fun =>
#{default => fun(_,_,_,_) -> void end,
chk => fun check_function4/1,
- class => user_options
+ class => user_option
},
- {rekey_limit, def} =>
+ rekey_limit =>
#{default => {3600000, 1024000000}, % {1 hour, 1 GB}
chk => fun({infinity, infinity}) ->
true;
@@ -629,10 +653,10 @@ default(common) ->
(_) ->
false
end,
- class => user_options
+ class => user_option
},
- {auth_methods, def} =>
+ auth_methods =>
#{default => ?SUPPORTED_AUTH_METHODS,
chk => fun(As) ->
try
@@ -644,54 +668,54 @@ default(common) ->
_:_ -> false
end
end,
- class => user_options
+ class => user_option
},
+ send_ext_info =>
+ #{default => true,
+ chk => fun erlang:is_boolean/1,
+ class => user_option
+ },
+
+ recv_ext_info =>
+ #{default => true,
+ chk => fun erlang:is_boolean/1,
+ class => user_option
+ },
+
%%%%% Undocumented
- {transport, def} =>
+ transport =>
#{default => ?DEFAULT_TRANSPORT,
chk => fun({A,B,C}) ->
is_atom(A) andalso is_atom(B) andalso is_atom(C)
end,
- class => user_options
+ class => undoc_user_option
},
- {vsn, def} =>
+ vsn =>
#{default => {2,0},
chk => fun({Maj,Min}) -> check_non_neg_integer(Maj) andalso check_non_neg_integer(Min);
(_) -> false
end,
- class => user_options
+ class => undoc_user_option
},
- {tstflg, def} =>
+ tstflg =>
#{default => [],
chk => fun erlang:is_list/1,
- class => user_options
+ class => undoc_user_option
},
- {user_dir_fun, def} =>
+ user_dir_fun =>
#{default => undefined,
chk => fun check_function1/1,
- class => user_options
+ class => undoc_user_option
},
- {max_random_length_padding, def} =>
+ max_random_length_padding =>
#{default => ?MAX_RND_PADDING_LEN,
chk => fun check_non_neg_integer/1,
- class => user_options
- },
-
- {send_ext_info, def} =>
- #{default => true,
- chk => fun erlang:is_boolean/1,
- class => user_options
- },
-
- {recv_ext_info, def} =>
- #{default => true,
- chk => fun erlang:is_boolean/1,
- class => user_options
+ class => undoc_user_option
}
}.
diff --git a/lib/ssh/src/ssh_server_channel.erl b/lib/ssh/src/ssh_server_channel.erl
index 555080e9ee..1905c40c98 100644
--- a/lib/ssh/src/ssh_server_channel.erl
+++ b/lib/ssh/src/ssh_server_channel.erl
@@ -37,7 +37,7 @@
-callback handle_msg(Msg ::term(), State :: term()) ->
{ok, State::term()} | {stop, ChannelId::ssh:channel_id(), State::term()}.
--callback handle_ssh_msg({ssh_cm, ConnectionRef::ssh:connection_ref(), SshMsg::term()},
+-callback handle_ssh_msg(ssh_connection:event(),
State::term()) -> {ok, State::term()} |
{stop, ChannelId::ssh:channel_id(),
State::term()}.
diff --git a/lib/ssh/src/ssh_sftp.erl b/lib/ssh/src/ssh_sftp.erl
index 1b2ba5a50b..305c1c31fd 100644
--- a/lib/ssh/src/ssh_sftp.erl
+++ b/lib/ssh/src/ssh_sftp.erl
@@ -92,24 +92,63 @@
-define(XF(S), S#state.xf).
-define(REQID(S), S#state.req_id).
+-type sftp_option() :: {timeout, timeout()}
+ | {sftp_vsn, pos_integer()}
+ | {window_size, pos_integer()}
+ | {packet_size, pos_integer()} .
+
+-type reason() :: atom() | string() | tuple() .
+
%%====================================================================
%% API
%%====================================================================
+
+
+%%%================================================================
+%%%
+
+%%%----------------------------------------------------------------
+%%% start_channel/1
+
start_channel(Cm) when is_pid(Cm) ->
start_channel(Cm, []);
+
start_channel(Socket) when is_port(Socket) ->
start_channel(Socket, []);
-start_channel(Host) when is_list(Host) ->
+
+start_channel(Host) ->
start_channel(Host, []).
+
+%%%----------------------------------------------------------------
+%%% start_channel/2
+
+%%% -spec:s are as if Dialyzer handled signatures for separate
+%%% function clauses.
+
+-spec start_channel(ssh:open_socket(),
+ [ssh:client_options() | sftp_option()]
+ )
+ -> {ok,pid(),ssh:connection_ref()} | {error,reason()};
+
+ (ssh:connection_ref(),
+ [sftp_option()]
+ )
+ -> {ok,pid()} | {ok,pid(),ssh:connection_ref()} | {error,reason()};
+
+ (ssh:host(),
+ [ssh:client_options() | sftp_option()]
+ )
+ -> {ok,pid(),ssh:connection_ref()} | {error,reason()} .
+
start_channel(Socket, UserOptions) when is_port(Socket) ->
- {SshOpts, _ChanOpts, SftpOpts} = handle_options(UserOptions),
+ {SshOpts, ChanOpts, SftpOpts} = handle_options(UserOptions),
Timeout = % A mixture of ssh:connect and ssh_sftp:start_channel:
proplists:get_value(connect_timeout, SshOpts,
proplists:get_value(timeout, SftpOpts, infinity)),
case ssh:connect(Socket, SshOpts, Timeout) of
{ok,Cm} ->
- case start_channel(Cm, UserOptions) of
+ case start_channel(Cm, ChanOpts ++ SftpOpts) of
{ok, Pid} ->
{ok, Pid, Cm};
Error ->
@@ -123,8 +162,8 @@ start_channel(Cm, UserOptions) when is_pid(Cm) ->
{_SshOpts, ChanOpts, SftpOpts} = handle_options(UserOptions),
case ssh_xfer:attach(Cm, [], ChanOpts) of
{ok, ChannelId, Cm} ->
- case ssh_client_channel:start(Cm, ChannelId,
- ?MODULE, [Cm, ChannelId, SftpOpts]) of
+ case ssh_connection_handler:start_channel(Cm, ?MODULE, ChannelId,
+ [Cm,ChannelId,SftpOpts], undefined) of
{ok, Pid} ->
case wait_for_version_negotiation(Pid, Timeout) of
ok ->
@@ -133,9 +172,7 @@ start_channel(Cm, UserOptions) when is_pid(Cm) ->
TimeOut
end;
{error, Reason} ->
- {error, format_channel_start_error(Reason)};
- ignore ->
- {error, ignore}
+ {error, format_channel_start_error(Reason)}
end;
Error ->
Error
@@ -144,6 +181,16 @@ start_channel(Cm, UserOptions) when is_pid(Cm) ->
start_channel(Host, UserOptions) ->
start_channel(Host, 22, UserOptions).
+
+%%%----------------------------------------------------------------
+%%% start_channel/3
+
+-spec start_channel(ssh:host(),
+ inet:port_number(),
+ [ssh:client_option() | sftp_option()]
+ )
+ -> {ok,pid(),ssh:connection_ref()} | {error,reason()}.
+
start_channel(Host, Port, UserOptions) ->
{SshOpts, ChanOpts, SftpOpts} = handle_options(UserOptions),
Timeout = % A mixture of ssh:connect and ssh_sftp:start_channel:
@@ -151,7 +198,8 @@ start_channel(Host, Port, UserOptions) ->
proplists:get_value(timeout, SftpOpts, infinity)),
case ssh_xfer:connect(Host, Port, SshOpts, ChanOpts, Timeout) of
{ok, ChannelId, Cm} ->
- case ssh_client_channel:start(Cm, ChannelId, ?MODULE, [Cm,ChannelId,SftpOpts]) of
+ case ssh_connection_handler:start_channel(Cm, ?MODULE, ChannelId,
+ [Cm,ChannelId,SftpOpts], undefined) of
{ok, Pid} ->
case wait_for_version_negotiation(Pid, Timeout) of
ok ->
@@ -160,14 +208,21 @@ start_channel(Host, Port, UserOptions) ->
TimeOut
end;
{error, Reason} ->
- {error, format_channel_start_error(Reason)};
- ignore ->
- {error, ignore}
+ {error, format_channel_start_error(Reason)}
end;
Error ->
Error
end.
+%%% Helper for start_channel
+
+wait_for_version_negotiation(Pid, Timeout) ->
+ call(Pid, wait_for_version_negotiation, Timeout).
+
+%%%----------------------------------------------------------------
+-spec stop_channel(ChannelPid) -> ok when
+ ChannelPid :: pid().
+
stop_channel(Pid) ->
case is_process_alive(Pid) of
true ->
@@ -185,20 +240,63 @@ stop_channel(Pid) ->
ok
end.
-wait_for_version_negotiation(Pid, Timeout) ->
- call(Pid, wait_for_version_negotiation, Timeout).
-
+%%%----------------------------------------------------------------
+-spec open(ChannelPid, Name, Mode) -> {ok, Handle} | Error when
+ ChannelPid :: pid(),
+ Name :: string(),
+ Mode :: [read | write | append | binary | raw],
+ Handle :: term(),
+ Error :: {error, reason()} .
open(Pid, File, Mode) ->
open(Pid, File, Mode, ?FILEOP_TIMEOUT).
+-spec open(ChannelPid, Name, Mode, Timeout) -> {ok, Handle} | Error when
+ ChannelPid :: pid(),
+ Name :: string(),
+ Mode :: [read | write | append | binary | raw],
+ Timeout :: timeout(),
+ Handle :: term(),
+ Error :: {error, reason()} .
open(Pid, File, Mode, FileOpTimeout) ->
call(Pid, {open, false, File, Mode}, FileOpTimeout).
+
+-type tar_crypto_spec() :: encrypt_spec() | decrypt_spec() .
+
+-type encrypt_spec() :: {init_fun(), crypto_fun(), final_fun()} .
+-type decrypt_spec() :: {init_fun(), crypto_fun()} .
+
+-type init_fun() :: fun(() -> {ok,crypto_state()})
+ | fun(() -> {ok,crypto_state(),chunk_size()}) .
+
+-type crypto_fun() :: fun((TextIn::binary(), crypto_state()) -> crypto_result()) .
+-type crypto_result() :: {ok,TextOut::binary(),crypto_state()}
+ | {ok,TextOut::binary(),crypto_state(),chunk_size()} .
+
+-type final_fun() :: fun((FinalTextIn::binary(),crypto_state()) -> {ok,FinalTextOut::binary()}) .
+
+-type chunk_size() :: undefined | pos_integer().
+-type crypto_state() :: any() .
+
+
+-spec open_tar(ChannelPid, Path, Mode) -> {ok, Handle} | Error when
+ ChannelPid :: pid(),
+ Path :: string(),
+ Mode :: [read | write | {crypto, tar_crypto_spec()} ],
+ Handle :: term(),
+ Error :: {error, reason()} .
open_tar(Pid, File, Mode) ->
open_tar(Pid, File, Mode, ?FILEOP_TIMEOUT).
+-spec open_tar(ChannelPid, Path, Mode, Timeout) -> {ok, Handle} | Error when
+ ChannelPid :: pid(),
+ Path :: string(),
+ Mode :: [read | write | {crypto, tar_crypto_spec()} ],
+ Timeout :: timeout(),
+ Handle :: term(),
+ Error :: {error, reason()} .
open_tar(Pid, File, Mode, FileOpTimeout) ->
case {lists:member(write,Mode),
lists:member(read,Mode),
- Mode -- [read,write]} of
+ Mode -- [write,read]} of
{true,false,[]} ->
{ok,Handle} = open(Pid, File, [write], FileOpTimeout),
erl_tar:init(Pid, write,
@@ -264,13 +362,33 @@ open_tar(Pid, File, Mode, FileOpTimeout) ->
end.
+-spec opendir(ChannelPid, Path) -> {ok, Handle} | Error when
+ ChannelPid :: pid(),
+ Path :: string(),
+ Handle :: term(),
+ Error :: {error, reason()} .
opendir(Pid, Path) ->
opendir(Pid, Path, ?FILEOP_TIMEOUT).
+-spec opendir(ChannelPid, Path, Timeout) -> {ok, Handle} | Error when
+ ChannelPid :: pid(),
+ Path :: string(),
+ Timeout :: timeout(),
+ Handle :: term(),
+ Error :: {error, reason()} .
opendir(Pid, Path, FileOpTimeout) ->
call(Pid, {opendir, false, Path}, FileOpTimeout).
+-spec close(ChannelPid, Handle) -> ok | Error when
+ ChannelPid :: pid(),
+ Handle :: term(),
+ Error :: {error, reason()} .
close(Pid, Handle) ->
close(Pid, Handle, ?FILEOP_TIMEOUT).
+-spec close(ChannelPid, Handle, Timeout) -> ok | Error when
+ ChannelPid :: pid(),
+ Handle :: term(),
+ Timeout :: timeout(),
+ Error :: {error, reason()} .
close(Pid, Handle, FileOpTimeout) ->
call(Pid, {close,false,Handle}, FileOpTimeout).
@@ -279,47 +397,149 @@ readdir(Pid,Handle) ->
readdir(Pid,Handle, FileOpTimeout) ->
call(Pid, {readdir,false,Handle}, FileOpTimeout).
+-spec pread(ChannelPid, Handle, Position, Len) -> {ok, Data} | eof | Error when
+ ChannelPid :: pid(),
+ Handle :: term(),
+ Position :: integer(),
+ Len :: integer(),
+ Data :: string() | binary(),
+ Error :: {error, reason()}.
pread(Pid, Handle, Offset, Len) ->
pread(Pid, Handle, Offset, Len, ?FILEOP_TIMEOUT).
+
+-spec pread(ChannelPid, Handle, Position, Len, Timeout) -> {ok, Data} | eof | Error when
+ ChannelPid :: pid(),
+ Handle :: term(),
+ Position :: integer(),
+ Len :: integer(),
+ Timeout :: timeout(),
+ Data :: string() | binary(),
+ Error :: {error, reason()}.
pread(Pid, Handle, Offset, Len, FileOpTimeout) ->
call(Pid, {pread,false,Handle, Offset, Len}, FileOpTimeout).
+
+-spec read(ChannelPid, Handle, Len) -> {ok, Data} | eof | Error when
+ ChannelPid :: pid(),
+ Handle :: term(),
+ Len :: integer(),
+ Data :: string() | binary(),
+ Error :: {error, reason()}.
read(Pid, Handle, Len) ->
read(Pid, Handle, Len, ?FILEOP_TIMEOUT).
+
+-spec read(ChannelPid, Handle, Len, Timeout) -> {ok, Data} | eof | Error when
+ ChannelPid :: pid(),
+ Handle :: term(),
+ Len :: integer(),
+ Timeout :: timeout(),
+ Data :: string() | binary(),
+ Error :: {error, reason()}.
read(Pid, Handle, Len, FileOpTimeout) ->
call(Pid, {read,false,Handle, Len}, FileOpTimeout).
+
%% TODO this ought to be a cast! Is so in all practical meaning
%% even if it is obscure!
+-spec apread(ChannelPid, Handle, Position, Len) -> {async, N} | Error when
+ ChannelPid :: pid(),
+ Handle :: term(),
+ Position :: integer(),
+ Len :: integer(),
+ Error :: {error, reason()},
+ N :: term() .
apread(Pid, Handle, Offset, Len) ->
call(Pid, {pread,true,Handle, Offset, Len}, infinity).
%% TODO this ought to be a cast!
+-spec aread(ChannelPid, Handle, Len) -> {async, N} | Error when
+ ChannelPid :: pid(),
+ Handle :: term(),
+ Len :: integer(),
+ Error :: {error, reason()},
+ N :: term() .
aread(Pid, Handle, Len) ->
call(Pid, {read,true,Handle, Len}, infinity).
+
+-spec pwrite(ChannelPid, Handle, Position, Data) -> ok | Error when
+ ChannelPid :: pid(),
+ Handle :: term(),
+ Position :: integer(),
+ Data :: iolist(),
+ Error :: {error, reason()}.
pwrite(Pid, Handle, Offset, Data) ->
pwrite(Pid, Handle, Offset, Data, ?FILEOP_TIMEOUT).
+
+-spec pwrite(ChannelPid, Handle, Position, Data, Timeout) -> ok | Error when
+ ChannelPid :: pid(),
+ Handle :: term(),
+ Position :: integer(),
+ Data :: iolist(),
+ Timeout :: timeout(),
+ Error :: {error, reason()}.
pwrite(Pid, Handle, Offset, Data, FileOpTimeout) ->
call(Pid, {pwrite,false,Handle,Offset,Data}, FileOpTimeout).
+
+-spec write(ChannelPid, Handle, Data) -> ok | Error when
+ ChannelPid :: pid(),
+ Handle :: term(),
+ Data :: iodata(),
+ Error :: {error, reason()}.
write(Pid, Handle, Data) ->
write(Pid, Handle, Data, ?FILEOP_TIMEOUT).
+
+-spec write(ChannelPid, Handle, Data, Timeout) -> ok | Error when
+ ChannelPid :: pid(),
+ Handle :: term(),
+ Data :: iodata(),
+ Timeout :: timeout(),
+ Error :: {error, reason()}.
write(Pid, Handle, Data, FileOpTimeout) ->
call(Pid, {write,false,Handle,Data}, FileOpTimeout).
%% TODO this ought to be a cast! Is so in all practical meaning
%% even if it is obscure!
+-spec apwrite(ChannelPid, Handle, Position, Data) -> {async, N} | Error when
+ ChannelPid :: pid(),
+ Handle :: term(),
+ Position :: integer(),
+ Data :: binary(),
+ Error :: {error, reason()},
+ N :: term() .
apwrite(Pid, Handle, Offset, Data) ->
call(Pid, {pwrite,true,Handle,Offset,Data}, infinity).
%% TODO this ought to be a cast! Is so in all practical meaning
%% even if it is obscure!
+-spec awrite(ChannelPid, Handle, Data) -> {async, N} | Error when
+ ChannelPid :: pid(),
+ Handle :: term(),
+ Data :: binary(),
+ Error :: {error, reason()},
+ N :: term() .
awrite(Pid, Handle, Data) ->
call(Pid, {write,true,Handle,Data}, infinity).
+-spec position(ChannelPid, Handle, Location) -> {ok, NewPosition} | Error when
+ ChannelPid :: pid(),
+ Handle :: term(),
+ Location :: Offset | {bof, Offset} | {cur, Offset} | {eof, Offset} | bof | cur | eof,
+ Offset :: integer(),
+ NewPosition :: integer(),
+ Error :: {error, reason()}.
position(Pid, Handle, Pos) ->
position(Pid, Handle, Pos, ?FILEOP_TIMEOUT).
+
+-spec position(ChannelPid, Handle, Location, Timeout) -> {ok, NewPosition} | Error when
+ ChannelPid :: pid(),
+ Handle :: term(),
+ Location :: Offset | {bof, Offset} | {cur, Offset} | {eof, Offset} | bof | cur | eof,
+ Timeout :: timeout(),
+ Offset :: integer(),
+ NewPosition :: integer(),
+ Error :: {error, reason()}.
position(Pid, Handle, Pos, FileOpTimeout) ->
call(Pid, {position, Handle, Pos}, FileOpTimeout).
@@ -328,8 +548,21 @@ real_path(Pid, Path) ->
real_path(Pid, Path, FileOpTimeout) ->
call(Pid, {real_path, false, Path}, FileOpTimeout).
+
+-spec read_file_info(ChannelPid, Name) -> {ok, FileInfo} | Error when
+ ChannelPid :: pid(),
+ Name :: string(),
+ FileInfo :: file:file_info(),
+ Error :: {error, reason()}.
read_file_info(Pid, Name) ->
read_file_info(Pid, Name, ?FILEOP_TIMEOUT).
+
+-spec read_file_info(ChannelPid, Name, Timeout) -> {ok, FileInfo} | Error when
+ ChannelPid :: pid(),
+ Name :: string(),
+ Timeout :: timeout(),
+ FileInfo :: file:file_info(),
+ Error :: {error, reason()}.
read_file_info(Pid, Name, FileOpTimeout) ->
call(Pid, {read_file_info,false,Name}, FileOpTimeout).
@@ -338,18 +571,57 @@ get_file_info(Pid, Handle) ->
get_file_info(Pid, Handle, FileOpTimeout) ->
call(Pid, {get_file_info,false,Handle}, FileOpTimeout).
+
+-spec write_file_info(ChannelPid, Name, FileInfo) -> ok | Error when
+ ChannelPid :: pid(),
+ Name :: string(),
+ FileInfo :: file:file_info(),
+ Error :: {error, reason()}.
write_file_info(Pid, Name, Info) ->
write_file_info(Pid, Name, Info, ?FILEOP_TIMEOUT).
+
+-spec write_file_info(ChannelPid, Name, FileInfo, Timeout) -> ok | Error when
+ ChannelPid :: pid(),
+ Name :: string(),
+ FileInfo :: file:file_info(),
+ Timeout :: timeout(),
+ Error :: {error, reason()}.
write_file_info(Pid, Name, Info, FileOpTimeout) ->
call(Pid, {write_file_info,false,Name, Info}, FileOpTimeout).
+
+-spec read_link_info(ChannelPid, Name) -> {ok, FileInfo} | Error when
+ ChannelPid :: pid(),
+ Name :: string(),
+ FileInfo :: file:file_info(),
+ Error :: {error, reason()}.
read_link_info(Pid, Name) ->
read_link_info(Pid, Name, ?FILEOP_TIMEOUT).
+
+-spec read_link_info(ChannelPid, Name, Timeout) -> {ok, FileInfo} | Error when
+ ChannelPid :: pid(),
+ Name :: string(),
+ FileInfo :: file:file_info(),
+ Timeout :: timeout(),
+ Error :: {error, reason()}.
read_link_info(Pid, Name, FileOpTimeout) ->
call(Pid, {read_link_info,false,Name}, FileOpTimeout).
+
+-spec read_link(ChannelPid, Name) -> {ok, Target} | Error when
+ ChannelPid :: pid(),
+ Name :: string(),
+ Target :: string(),
+ Error :: {error, reason()}.
read_link(Pid, LinkName) ->
read_link(Pid, LinkName, ?FILEOP_TIMEOUT).
+
+-spec read_link(ChannelPid, Name, Timeout) -> {ok, Target} | Error when
+ ChannelPid :: pid(),
+ Name :: string(),
+ Target :: string(),
+ Timeout :: timeout(),
+ Error :: {error, reason()}.
read_link(Pid, LinkName, FileOpTimeout) ->
case call(Pid, {read_link,false,LinkName}, FileOpTimeout) of
{ok, [{Name, _Attrs}]} ->
@@ -358,28 +630,79 @@ read_link(Pid, LinkName, FileOpTimeout) ->
ErrMsg
end.
+-spec make_symlink(ChannelPid, Name, Target) -> ok | Error when
+ ChannelPid :: pid(),
+ Name :: string(),
+ Target :: string(),
+ Error :: {error, reason()} .
make_symlink(Pid, Name, Target) ->
make_symlink(Pid, Name, Target, ?FILEOP_TIMEOUT).
+-spec make_symlink(ChannelPid, Name, Target, Timeout) -> ok | Error when
+ ChannelPid :: pid(),
+ Name :: string(),
+ Target :: string(),
+ Timeout :: timeout(),
+ Error :: {error, reason()} .
make_symlink(Pid, Name, Target, FileOpTimeout) ->
call(Pid, {make_symlink,false, Name, Target}, FileOpTimeout).
+
+-spec rename(ChannelPid, OldName, NewName) -> ok | Error when
+ ChannelPid :: pid(),
+ OldName :: string(),
+ NewName :: string(),
+ Error :: {error, reason()}.
rename(Pid, FromFile, ToFile) ->
rename(Pid, FromFile, ToFile, ?FILEOP_TIMEOUT).
+
+-spec rename(ChannelPid, OldName, NewName, Timeout) -> ok | Error when
+ ChannelPid :: pid(),
+ OldName :: string(),
+ NewName :: string(),
+ Timeout :: timeout(),
+ Error :: {error, reason()}.
rename(Pid, FromFile, ToFile, FileOpTimeout) ->
call(Pid, {rename,false,FromFile, ToFile}, FileOpTimeout).
+-spec delete(ChannelPid, Name) -> ok | Error when
+ ChannelPid :: pid(),
+ Name :: string(),
+ Error :: {error, reason()} .
delete(Pid, Name) ->
delete(Pid, Name, ?FILEOP_TIMEOUT).
+-spec delete(ChannelPid, Name, Timeout) -> ok | Error when
+ ChannelPid :: pid(),
+ Name :: string(),
+ Timeout :: timeout(),
+ Error :: {error, reason()} .
delete(Pid, Name, FileOpTimeout) ->
call(Pid, {delete,false,Name}, FileOpTimeout).
+-spec make_dir(ChannelPid, Name) -> ok | Error when
+ ChannelPid :: pid(),
+ Name :: string(),
+ Error :: {error, reason()} .
make_dir(Pid, Name) ->
make_dir(Pid, Name, ?FILEOP_TIMEOUT).
+-spec make_dir(ChannelPid, Name, Timeout) -> ok | Error when
+ ChannelPid :: pid(),
+ Name :: string(),
+ Timeout :: timeout(),
+ Error :: {error, reason()} .
make_dir(Pid, Name, FileOpTimeout) ->
call(Pid, {make_dir,false,Name}, FileOpTimeout).
+-spec del_dir(ChannelPid, Name) -> ok | Error when
+ ChannelPid :: pid(),
+ Name :: string(),
+ Error :: {error, reason()} .
del_dir(Pid, Name) ->
del_dir(Pid, Name, ?FILEOP_TIMEOUT).
+-spec del_dir(ChannelPid, Name, Timeout) -> ok | Error when
+ ChannelPid :: pid(),
+ Name :: string(),
+ Timeout :: timeout(),
+ Error :: {error, reason()} .
del_dir(Pid, Name, FileOpTimeout) ->
call(Pid, {del_dir,false,Name}, FileOpTimeout).
@@ -396,9 +719,21 @@ recv_window(Pid, FileOpTimeout) ->
call(Pid, recv_window, FileOpTimeout).
+-spec list_dir(ChannelPid, Path) -> {ok,FileNames} | Error when
+ ChannelPid :: pid(),
+ Path :: string(),
+ FileNames :: [FileName],
+ FileName :: string(),
+ Error :: {error, reason()} .
list_dir(Pid, Name) ->
list_dir(Pid, Name, ?FILEOP_TIMEOUT).
-
+-spec list_dir(ChannelPid, Path, Timeout) -> {ok,FileNames} | Error when
+ ChannelPid :: pid(),
+ Path :: string(),
+ Timeout :: timeout(),
+ FileNames :: [FileName],
+ FileName :: string(),
+ Error :: {error, reason()} .
list_dir(Pid, Name, FileOpTimeout) ->
case opendir(Pid, Name, FileOpTimeout) of
{ok,Handle} ->
@@ -429,9 +764,20 @@ do_list_dir(Pid, Handle, FileOpTimeout, Acc) ->
end.
+-spec read_file(ChannelPid, File) -> {ok, Data} | Error when
+ ChannelPid :: pid(),
+ File :: string(),
+ Data :: binary(),
+ Error :: {error, reason()}.
read_file(Pid, Name) ->
read_file(Pid, Name, ?FILEOP_TIMEOUT).
+-spec read_file(ChannelPid, File, Timeout) -> {ok, Data} | Error when
+ ChannelPid :: pid(),
+ File :: string(),
+ Data :: binary(),
+ Timeout :: timeout(),
+ Error :: {error, reason()}.
read_file(Pid, Name, FileOpTimeout) ->
case open(Pid, Name, [read, binary], FileOpTimeout) of
{ok, Handle} ->
@@ -453,9 +799,20 @@ read_file_loop(Pid, Handle, PacketSz, FileOpTimeout, Acc) ->
Error
end.
+-spec write_file(ChannelPid, File, Data) -> ok | Error when
+ ChannelPid :: pid(),
+ File :: string(),
+ Data :: iodata(),
+ Error :: {error, reason()}.
write_file(Pid, Name, List) ->
write_file(Pid, Name, List, ?FILEOP_TIMEOUT).
+-spec write_file(ChannelPid, File, Data, Timeout) -> ok | Error when
+ ChannelPid :: pid(),
+ File :: string(),
+ Data :: iodata(),
+ Timeout :: timeout(),
+ Error :: {error, reason()}.
write_file(Pid, Name, List, FileOpTimeout) when is_list(List) ->
write_file(Pid, Name, list_to_binary(List), FileOpTimeout);
write_file(Pid, Name, Bin, FileOpTimeout) ->
diff --git a/lib/ssh/src/ssh_sftpd.erl b/lib/ssh/src/ssh_sftpd.erl
index 5ec12e2d04..bf921f0ff3 100644
--- a/lib/ssh/src/ssh_sftpd.erl
+++ b/lib/ssh/src/ssh_sftpd.erl
@@ -58,7 +58,17 @@
%%====================================================================
%% API
%%====================================================================
--spec subsystem_spec(list()) -> subsystem_spec().
+-spec subsystem_spec(Options) -> Spec when
+ Options :: [ {cwd, string()} |
+ {file_handler, CallbackModule::string()} |
+ {max_files, integer()} |
+ {root, string()} |
+ {sftpd_vsn, integer()}
+ ],
+ Spec :: {Name, {CbMod,Options}},
+ Name :: string(),
+ CbMod :: atom() .
+
subsystem_spec(Options) ->
{"sftp", {?MODULE, Options}}.
diff --git a/lib/ssh/src/ssh_subsystem_sup.erl b/lib/ssh/src/ssh_subsystem_sup.erl
index 5fc8f7e764..db34735e1b 100644
--- a/lib/ssh/src/ssh_subsystem_sup.erl
+++ b/lib/ssh/src/ssh_subsystem_sup.erl
@@ -30,7 +30,8 @@
-export([start_link/5,
connection_supervisor/1,
- channel_supervisor/1
+ channel_supervisor/1,
+ start_channel/8
]).
%% Supervisor callback
@@ -46,9 +47,13 @@ connection_supervisor(SupPid) ->
Children = supervisor:which_children(SupPid),
ssh_connection_sup(Children).
-channel_supervisor(SupPid) ->
+channel_supervisor(SupPid) when is_pid(SupPid) ->
Children = supervisor:which_children(SupPid),
- ssh_server_channel_sup(Children).
+ ssh_channel_sup(Children).
+
+start_channel(Role, SupPid, ConnRef, Callback, Id, Args, Exec, Opts) ->
+ ChannelSup = channel_supervisor(SupPid),
+ ssh_channel_sup:start_child(Role, ChannelSup, ConnRef, Callback, Id, Args, Exec, Opts).
%%%=========================================================================
%%% Supervisor callback
@@ -64,11 +69,9 @@ init([Role, Address, Port, Profile, Options]) ->
%%%=========================================================================
%%% Internal functions
%%%=========================================================================
-child_specs(client, _Address, _Port, _Profile, _Options) ->
- [];
-child_specs(server, Address, Port, Profile, Options) ->
- [ssh_channel_child_spec(server, Address, Port, Profile, Options),
- ssh_connection_child_spec(server, Address, Port, Profile, Options)].
+child_specs(Role, Address, Port, Profile, Options) ->
+ [ssh_channel_child_spec(Role, Address, Port, Profile, Options),
+ ssh_connection_child_spec(Role, Address, Port, Profile, Options)].
ssh_connection_child_spec(Role, Address, Port, _Profile, Options) ->
#{id => id(Role, ssh_connection_sup, Address, Port),
@@ -78,8 +81,8 @@ ssh_connection_child_spec(Role, Address, Port, _Profile, Options) ->
}.
ssh_channel_child_spec(Role, Address, Port, _Profile, Options) ->
- #{id => id(Role, ssh_server_channel_sup, Address, Port),
- start => {ssh_server_channel_sup, start_link, [Options]},
+ #{id => id(Role, ssh_channel_sup, Address, Port),
+ start => {ssh_channel_sup, start_link, [Options]},
restart => temporary,
type => supervisor
}.
@@ -92,10 +95,10 @@ ssh_connection_sup([{_, Child, _, [ssh_connection_sup]} | _]) ->
ssh_connection_sup([_ | Rest]) ->
ssh_connection_sup(Rest).
-ssh_server_channel_sup([{_, Child, _, [ssh_server_channel_sup]} | _]) ->
+ssh_channel_sup([{_, Child, _, [ssh_channel_sup]} | _]) ->
Child;
-ssh_server_channel_sup([_ | Rest]) ->
- ssh_server_channel_sup(Rest).
+ssh_channel_sup([_ | Rest]) ->
+ ssh_channel_sup(Rest).
diff --git a/lib/ssh/src/ssh_system_sup.erl b/lib/ssh/src/ssh_system_sup.erl
index ed7c0c2bd5..704c17c4e7 100644
--- a/lib/ssh/src/ssh_system_sup.erl
+++ b/lib/ssh/src/ssh_system_sup.erl
@@ -31,28 +31,33 @@
-include("ssh.hrl").
--export([start_link/4, stop_listener/1,
- stop_listener/3, stop_system/1,
- stop_system/3, system_supervisor/3,
+-export([start_link/5, stop_listener/1,
+ stop_listener/3, stop_system/2,
+ stop_system/4, system_supervisor/3,
subsystem_supervisor/1, channel_supervisor/1,
connection_supervisor/1,
acceptor_supervisor/1, start_subsystem/6,
- stop_subsystem/2]).
+ stop_subsystem/2,
+ get_options/4
+ ]).
%% Supervisor callback
-export([init/1]).
+-define(START(Address, Port, Profile, Options),
+ {ssh_acceptor_sup, start_link, [Address, Port, Profile, Options]}).
+
%%%=========================================================================
%%% API
%%%=========================================================================
-start_link(Address, Port, Profile, Options) ->
+start_link(Role, Address, Port, Profile, Options) ->
Name = make_name(Address, Port, Profile),
- supervisor:start_link({local, Name}, ?MODULE, [Address, Port, Profile, Options]).
+ supervisor:start_link({local, Name}, ?MODULE, [Role, Address, Port, Profile, Options]).
%%%=========================================================================
%%% Supervisor callback
%%%=========================================================================
-init([Address, Port, Profile, Options]) ->
+init([server, Address, Port, Profile, Options]) ->
SupFlags = #{strategy => one_for_one,
intensity => 0,
period => 3600
@@ -61,13 +66,21 @@ init([Address, Port, Profile, Options]) ->
case ?GET_INTERNAL_OPT(connected_socket,Options,undefined) of
undefined ->
[#{id => id(ssh_acceptor_sup, Address, Port, Profile),
- start => {ssh_acceptor_sup, start_link, [Address, Port, Profile, Options]},
+ start => ?START(Address,Port,Profile,Options),
restart => transient,
type => supervisor
}];
_ ->
[]
end,
+ {ok, {SupFlags,ChildSpecs}};
+
+init([client, _Address, _Port, _Profile, _Options]) ->
+ SupFlags = #{strategy => one_for_one,
+ intensity => 0,
+ period => 3600
+ },
+ ChildSpecs = [],
{ok, {SupFlags,ChildSpecs}}.
%%%=========================================================================
@@ -87,15 +100,23 @@ stop_listener(Address, Port, Profile) ->
system_supervisor(Address, Port, Profile)).
-stop_system(SysSup) ->
- catch sshd_sup:stop_child(SysSup),
- ok.
+stop_system(server, SysSup) -> catch sshd_sup:stop_child(SysSup), ok;
+stop_system(client, SysSup) -> catch sshc_sup:stop_child(SysSup), ok.
-stop_system(Address, Port, Profile) ->
+stop_system(server, Address, Port, Profile) ->
catch sshd_sup:stop_child(Address, Port, Profile),
ok.
+get_options(Sup, Address, Port, Profile) ->
+ try
+ {ok, #{start:=?START(Address,Port,Profile,Options)}} =
+ supervisor:get_childspec(Sup, id(ssh_acceptor_sup,Address,Port,Profile)),
+ {ok, Options}
+ catch
+ _:_ -> {error,not_found}
+ end.
+
system_supervisor(Address, Port, Profile) ->
Name = make_name(Address, Port, Profile),
whereis(Name).
diff --git a/lib/ssh/src/sshc_sup.erl b/lib/ssh/src/sshc_sup.erl
index 869de244ac..0c9f8be5a9 100644
--- a/lib/ssh/src/sshc_sup.erl
+++ b/lib/ssh/src/sshc_sup.erl
@@ -27,7 +27,10 @@
-behaviour(supervisor).
--export([start_link/0, start_child/1]).
+-export([start_link/0,
+ start_child/4,
+ stop_child/1
+ ]).
%% Supervisor callback
-export([init/1]).
@@ -38,22 +41,45 @@
%%% API
%%%=========================================================================
start_link() ->
- supervisor:start_link({local,?SSHC_SUP}, ?MODULE, []).
+ supervisor:start_link({local,?MODULE}, ?MODULE, []).
-start_child(Args) ->
- supervisor:start_child(?MODULE, Args).
+start_child(Address, Port, Profile, Options) ->
+ %% Here we a new connction on a new Host/EFERMERAL Port/Profile
+ Spec = child_spec(Address, Port, Profile, Options),
+ supervisor:start_child(?MODULE, Spec).
+
+stop_child(ChildId) when is_tuple(ChildId) ->
+ supervisor:terminate_child(?SSHC_SUP, ChildId);
+stop_child(ChildPid) when is_pid(ChildPid)->
+ stop_child(system_name(ChildPid)).
%%%=========================================================================
%%% Supervisor callback
%%%=========================================================================
init(_) ->
- SupFlags = #{strategy => simple_one_for_one,
+ SupFlags = #{strategy => one_for_one,
intensity => 0,
period => 3600
},
- ChildSpecs = [#{id => undefined, % As simple_one_for_one is used.
- start => {ssh_connection_handler, start_link, []},
- restart => temporary % because there is no way to restart a crashed connection
- }
- ],
+ ChildSpecs = [],
{ok, {SupFlags,ChildSpecs}}.
+
+%%%=========================================================================
+%%% Internal functions
+%%%=========================================================================
+child_spec(Address, Port, Profile, Options) ->
+ #{id => id(Address, Port, Profile),
+ start => {ssh_system_sup, start_link, [client, Address, Port, Profile, Options]},
+ restart => temporary,
+ type => supervisor
+ }.
+
+id(Address, Port, Profile) ->
+ {client, ssh_system_sup, Address, Port, Profile}.
+
+system_name(SysSup) ->
+ case lists:keyfind(SysSup, 2, supervisor:which_children(?SSHC_SUP)) of
+ {Name, SysSup, _, _} -> Name;
+ false -> undefind
+ end.
+
diff --git a/lib/ssh/src/sshd_sup.erl b/lib/ssh/src/sshd_sup.erl
index b5361abba5..b716b66ec7 100644
--- a/lib/ssh/src/sshd_sup.erl
+++ b/lib/ssh/src/sshd_sup.erl
@@ -89,7 +89,7 @@ init(_) ->
%%%=========================================================================
child_spec(Address, Port, Profile, Options) ->
#{id => id(Address, Port, Profile),
- start => {ssh_system_sup, start_link, [Address, Port, Profile, Options]},
+ start => {ssh_system_sup, start_link, [server, Address, Port, Profile, Options]},
restart => temporary,
type => supervisor
}.
diff --git a/lib/ssh/test/ssh_sup_SUITE.erl b/lib/ssh/test/ssh_sup_SUITE.erl
index a0e3d809be..0bf4d87f0a 100644
--- a/lib/ssh/test/ssh_sup_SUITE.erl
+++ b/lib/ssh/test/ssh_sup_SUITE.erl
@@ -116,18 +116,21 @@ sshc_subtree(Config) when is_list(Config) ->
{user_interaction, false},
{user, ?USER}, {password, ?PASSWD},{user_dir, UserDir}]),
- ?wait_match([{_, _,worker,[ssh_connection_handler]}],
- supervisor:which_children(sshc_sup)),
+ ?wait_match([{{client,ssh_system_sup, LocalIP, LocalPort, ?DEFAULT_PROFILE},
+ SysSup, supervisor,[ssh_system_sup]}],
+ supervisor:which_children(sshc_sup),
+ [SysSup, LocalIP, LocalPort]),
+ check_sshc_system_tree(SysSup, Pid1, LocalIP, LocalPort, Config),
{ok, Pid2} = ssh:connect(Host, Port, [{silently_accept_hosts, true},
{user_interaction, false},
{user, ?USER}, {password, ?PASSWD}, {user_dir, UserDir}]),
- ?wait_match([{_,_,worker,[ssh_connection_handler]},
- {_,_,worker,[ssh_connection_handler]}],
+ ?wait_match([{_, _,supervisor,[ssh_system_sup]},
+ {_, _,supervisor,[ssh_system_sup]}],
supervisor:which_children(sshc_sup)),
ssh:close(Pid1),
- ?wait_match([{_,_,worker,[ssh_connection_handler]}],
+ ?wait_match([{_, _,supervisor,[ssh_system_sup]}],
supervisor:which_children(sshc_sup)),
ssh:close(Pid2),
?wait_match([], supervisor:which_children(sshc_sup)).
@@ -217,6 +220,7 @@ killed_acceptor_restarts(Config) ->
ct:log("~s",[lists:flatten(ssh_info:string())]),
%% Make acceptor restart:
+ ct:pal("Expect a SUPERVISOR REPORT with offender {pid,~p}....~n", [AccPid]),
exit(AccPid, kill),
?wait_match(undefined, process_info(AccPid)),
@@ -226,6 +230,8 @@ killed_acceptor_restarts(Config) ->
AccPid1,
500, 30),
+ ct:pal("... now there should not be any SUPERVISOR REPORT.~n", []),
+
true = (AccPid1 =/= AccPid2),
%% Connect second client and check it is alive:
@@ -339,9 +345,9 @@ chk_empty_con_daemon(Daemon) ->
?wait_match([{{server,ssh_connection_sup, _,_},
ConnectionSup, supervisor,
[ssh_connection_sup]},
- {{server,ssh_server_channel_sup,_ ,_},
+ {{server,ssh_channel_sup,_ ,_},
ChannelSup,supervisor,
- [ssh_server_channel_sup]}],
+ [ssh_channel_sup]}],
supervisor:which_children(SubSysSup),
[ConnectionSup,ChannelSup]),
?wait_match([{{ssh_acceptor_sup,_,_,_},_,worker,[ssh_acceptor]}],
@@ -372,9 +378,9 @@ check_sshd_system_tree(Daemon, Config) ->
?wait_match([{{server,ssh_connection_sup, _,_},
ConnectionSup, supervisor,
[ssh_connection_sup]},
- {{server,ssh_server_channel_sup,_ ,_},
+ {{server,ssh_channel_sup,_ ,_},
ChannelSup,supervisor,
- [ssh_server_channel_sup]}],
+ [ssh_channel_sup]}],
supervisor:which_children(SubSysSup),
[ConnectionSup,ChannelSup]),
@@ -386,12 +392,55 @@ check_sshd_system_tree(Daemon, Config) ->
?wait_match([], supervisor:which_children(ChannelSup)),
- ssh_sftp:start_channel(Client),
+ {ok,PidC} = ssh_sftp:start_channel(Client),
+
+ ?wait_match([{_, PidS,worker,[ssh_server_channel]}],
+ supervisor:which_children(ChannelSup),
+ [PidS]),
+ true = (PidS =/= PidC),
- ?wait_match([{_, _,worker,[ssh_server_channel]}],
- supervisor:which_children(ChannelSup)),
ssh:close(Client).
+
+check_sshc_system_tree(SysSup, Connection, LocalIP, LocalPort, _Config) ->
+ ?wait_match([{_,SubSysSup,supervisor,[ssh_subsystem_sup]}],
+ supervisor:which_children(SysSup),
+ [SubSysSup]),
+ ?wait_match([{{client,ssh_connection_sup, LocalIP, LocalPort},
+ ConnectionSup, supervisor,
+ [ssh_connection_sup]},
+ {{client,ssh_channel_sup, LocalIP, LocalPort},
+ ChannelSup,supervisor,
+ [ssh_channel_sup]}],
+ supervisor:which_children(SubSysSup),
+ [ConnectionSup,ChannelSup]),
+ ?wait_match([{_, Connection, worker,[ssh_connection_handler]}],
+ supervisor:which_children(ConnectionSup)),
+ ?wait_match([], supervisor:which_children(ChannelSup)),
+
+ {ok,ChPid1} = ssh_sftp:start_channel(Connection),
+ ?wait_match([{_,ChPid1,worker,[ssh_client_channel]}],
+ supervisor:which_children(ChannelSup)),
+
+ {ok,ChPid2} = ssh_sftp:start_channel(Connection),
+ ?wait_match([{_,ChPidA,worker,[ssh_client_channel]},
+ {_,ChPidB,worker,[ssh_client_channel]}],
+ supervisor:which_children(ChannelSup),
+ [ChPidA, ChPidB]),
+ lists:sort([ChPidA, ChPidB]) == lists:sort([ChPid1, ChPid2]),
+
+ ct:pal("Expect a SUPERVISOR REPORT with offender {pid,~p}....~n", [ChPid1]),
+ exit(ChPid1, kill),
+ ?wait_match([{_,ChPid2,worker,[ssh_client_channel]}],
+ supervisor:which_children(ChannelSup)),
+
+ ct:pal("Expect a SUPERVISOR REPORT with offender {pid,~p}....~n", [ChPid2]),
+ exit(ChPid2, kill),
+ ?wait_match([], supervisor:which_children(ChannelSup)),
+ ct:pal("... now there should not be any SUPERVISOR REPORT.~n", []).
+
+
+
acceptor_pid(DaemonPid) ->
Parent = self(),
Pid = spawn(fun() ->
diff --git a/lib/ssh/vsn.mk b/lib/ssh/vsn.mk
index bb87dd388c..fcf97177d8 100644
--- a/lib/ssh/vsn.mk
+++ b/lib/ssh/vsn.mk
@@ -1,4 +1,4 @@
#-*-makefile-*- ; force emacs to enter makefile-mode
-SSH_VSN = 4.7.7
+SSH_VSN = 4.8
APP_VSN = "ssh-$(SSH_VSN)"
diff --git a/lib/ssl/Makefile b/lib/ssl/Makefile
index c761979474..526560288f 100644
--- a/lib/ssl/Makefile
+++ b/lib/ssl/Makefile
@@ -38,4 +38,6 @@ SPECIAL_TARGETS =
#
include $(ERL_TOP)/make/otp_subdir.mk
+DIA_PLT_APPS=crypto runtime_tools inets public_key
+
include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml
index a40aca658a..2f675560d6 100644
--- a/lib/ssl/doc/src/notes.xml
+++ b/lib/ssl/doc/src/notes.xml
@@ -27,6 +27,75 @@
</header>
<p>This document describes the changes made to the SSL application.</p>
+<section><title>SSL 9.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Handling of zero size fragments in TLS could cause an
+ infinite loop. This has now been corrected.</p>
+ <p>
+ Own Id: OTP-15328 Aux Id: ERIERL-379 </p>
+ </item>
+ <item>
+ <p>
+ DTLS record check needs to consider that a resent hello
+ message can have a different version than the negotiated.</p>
+ <p>
+ Own Id: OTP-15807 Aux Id: ERL-920 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Basic support for TLS 1.3 Client for experimental use.
+ For more information see the Standards Compliance chapter
+ of the User's Guide.</p>
+ <p>
+ Own Id: OTP-15431</p>
+ </item>
+ <item>
+ <p>
+ Correct solution for retaining tcp flow control OTP-15802
+ (ERL-934) as to not break ssl:recv as reported in
+ (ERL-938)</p>
+ <p>
+ Own Id: OTP-15823 Aux Id: ERL-934, ERL-938 </p>
+ </item>
+ <item>
+ <p>
+ Enhance dialyzer specs to reflect implementation better
+ and avoid dialyzer warnings for the user that wants to
+ use TLS with unix domain sockets.</p>
+ <p>
+ Own Id: OTP-15851 Aux Id: PR-2235 </p>
+ </item>
+ <item>
+ <p>
+ Add support for ECDSA signature algorithms in TLS 1.3.</p>
+ <p>
+ Own Id: OTP-15854</p>
+ </item>
+ <item>
+ <p>
+ Correct error handling of TLS downgrade, possible return
+ values form ssl:close/2 when downgrading is {ok, Port} or
+ {error, Reason}, it could happen that only ok was
+ returned instead of {error, closed} when downgrade failed
+ due to that the peer closed the TCP connection.</p>
+ <p>
+ Own Id: OTP-16027</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>SSL 9.3.5</title>
<section><title>Improvements and New Features</title>
@@ -226,6 +295,22 @@
</section>
+<section><title>SSL 9.2.3.5</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Handling of zero size fragments in TLS could cause an
+ infinite loop. This has now been corrected.</p>
+ <p>
+ Own Id: OTP-15328 Aux Id: ERIERL-379 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>SSL 9.2.3.4</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml
index 3aa6e09c2c..05590666da 100644
--- a/lib/ssl/doc/src/ssl.xml
+++ b/lib/ssl/doc/src/ssl.xml
@@ -207,6 +207,10 @@
<datatype>
<name name="sign_scheme"/>
</datatype>
+
+ <datatype>
+ <name name="group"/>
+ </datatype>
<datatype>
<name name="kex_algo"/>
@@ -363,7 +367,20 @@
</p>
</desc>
</datatype>
-
+
+ <datatype>
+ <name name="supported_groups"/>
+ <desc>
+ <p>TLS 1.3 introduces the "supported_groups" extension that is used for negotiating
+ the Diffie-Hellman parameters in a TLS 1.3 handshake. Both client and server
+ can specify a list of parameters that they are willing to use.
+ </p>
+ <p> If it is not specified it will use a default list ([x25519, x448, secp256r1, secp384r1]) that
+ is filtered based on the installed crypto library version.
+ </p>
+ </desc>
+ </datatype>
+
<datatype>
<name name="secure_renegotiation"/>
<desc><p>Specifies if to reject renegotiation attempt that does
@@ -919,6 +936,8 @@ fun(srp, Username :: string(), UserState :: term()) ->
<name name="dh_der"/>
<desc><p>The DER-encoded Diffie-Hellman parameters. If
specified, it overrides option <c>dhfile</c>.</p>
+ <warning><p>The <c>dh_der</c> option is not supported by TLS 1.3. Use the
+ <c>supported_groups</c> option instead.</p></warning>
</desc>
</datatype>
@@ -928,6 +947,8 @@ fun(srp, Username :: string(), UserState :: term()) ->
parameters to be used by the server if a cipher suite using
Diffie Hellman key exchange is negotiated. If not specified,
default parameters are used.</p>
+ <warning><p>The <c>dh_file</c> option is not supported by TLS 1.3. Use the
+ <c>supported_groups</c> option instead.</p></warning>
</desc>
</datatype>
diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl
index b220691e79..f8fd42e36d 100644
--- a/lib/ssl/src/dtls_connection.erl
+++ b/lib/ssl/src/dtls_connection.erl
@@ -66,7 +66,7 @@
%%====================================================================
%% Setup
%%====================================================================
-start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = false},_, Tracker} = Opts,
+start_fsm(Role, Host, Port, Socket, {#{erl_dist := false},_, Tracker} = Opts,
User, {CbModule, _, _, _, _} = CbInfo,
Timeout) ->
try
@@ -316,10 +316,10 @@ queue_handshake(Handshake0, #state{handshake_env = #handshake_env{tls_handshake_
flight_buffer = #{handshakes := HsBuffer0,
change_cipher_spec := undefined,
next_sequence := Seq} = Flight0,
- ssl_options = SslOpts} = State) ->
+ ssl_options = #{log_level := LogLevel}} = State) ->
Handshake = dtls_handshake:encode_handshake(Handshake0, Version, Seq),
Hist = update_handshake_history(Handshake0, Handshake, Hist0),
- ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'handshake', Handshake0),
+ ssl_logger:debug(LogLevel, outbound, 'handshake', Handshake0),
State#state{flight_buffer = Flight0#{handshakes => [Handshake | HsBuffer0],
next_sequence => Seq +1},
@@ -329,10 +329,10 @@ queue_handshake(Handshake0, #state{handshake_env = #handshake_env{tls_handshake_
connection_env = #connection_env{negotiated_version = Version},
flight_buffer = #{handshakes_after_change_cipher_spec := Buffer0,
next_sequence := Seq} = Flight0,
- ssl_options = SslOpts} = State) ->
+ ssl_options = #{log_level := LogLevel}} = State) ->
Handshake = dtls_handshake:encode_handshake(Handshake0, Version, Seq),
Hist = update_handshake_history(Handshake0, Handshake, Hist0),
- ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'handshake', Handshake0),
+ ssl_logger:debug(LogLevel, outbound, 'handshake', Handshake0),
State#state{flight_buffer = Flight0#{handshakes_after_change_cipher_spec => [Handshake | Buffer0],
next_sequence => Seq +1},
@@ -384,11 +384,11 @@ send_alert(Alert, #state{static_env = #static_env{socket = Socket,
connection_env = #connection_env{negotiated_version = Version},
connection_states = ConnectionStates0,
- ssl_options = SslOpts} = State0) ->
+ ssl_options = #{log_level := LogLevel}} = State0) ->
{BinMsg, ConnectionStates} =
encode_alert(Alert, Version, ConnectionStates0),
send(Transport, Socket, BinMsg),
- ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'record', BinMsg),
+ ssl_logger:debug(LogLevel, outbound, 'record', BinMsg),
State0#state{connection_states = ConnectionStates}.
send_alert_in_connection(Alert, State) ->
@@ -440,7 +440,7 @@ init({call, From}, {start, Timeout},
session_cache_cb = CacheCb},
handshake_env = #handshake_env{renegotiation = {Renegotiation, _}},
connection_env = CEnv,
- ssl_options = SslOpts,
+ ssl_options = #{versions := Versions} = SslOpts,
session = #session{own_certificate = Cert} = Session0,
connection_states = ConnectionStates0
} = State0) ->
@@ -448,7 +448,7 @@ init({call, From}, {start, Timeout},
Cache, CacheCb, Renegotiation, Cert),
Version = Hello#client_hello.client_version,
- HelloVersion = dtls_record:hello_version(Version, SslOpts#ssl_options.versions),
+ HelloVersion = dtls_record:hello_version(Version, Versions),
State1 = prepare_flight(State0#state{connection_env = CEnv#connection_env{negotiated_version = Version}}),
{State2, Actions} = send_handshake(Hello, State1#state{connection_env = CEnv#connection_env{negotiated_version = HelloVersion}}),
State = State2#state{connection_env = CEnv#connection_env{negotiated_version = Version}, %% RequestedVersion
@@ -547,14 +547,14 @@ hello(internal, #hello_verify_request{cookie = Cookie}, #state{static_env = #sta
Hello#client_hello.session_id}},
next_event(?FUNCTION_NAME, no_record, State, Actions);
hello(internal, #client_hello{extensions = Extensions} = Hello,
- #state{ssl_options = #ssl_options{handshake = hello},
+ #state{ssl_options = #{handshake := hello},
handshake_env = HsEnv,
start_or_recv_from = From} = State) ->
{next_state, user_hello, State#state{start_or_recv_from = undefined,
handshake_env = HsEnv#handshake_env{hello = Hello}},
[{reply, From, {ok, Extensions}}]};
hello(internal, #server_hello{extensions = Extensions} = Hello,
- #state{ssl_options = #ssl_options{handshake = hello},
+ #state{ssl_options = #{handshake := hello},
handshake_env = HsEnv,
start_or_recv_from = From} = State) ->
{next_state, user_hello, State#state{start_or_recv_from = undefined,
@@ -705,7 +705,7 @@ connection(internal, #hello_request{}, #state{static_env = #static_env{host = Ho
handshake_env = #handshake_env{ renegotiation = {Renegotiation, _}},
connection_env = CEnv,
session = #session{own_certificate = Cert} = Session0,
- ssl_options = SslOpts,
+ ssl_options = #{versions := Versions} = SslOpts,
connection_states = ConnectionStates0,
protocol_specific = PS
} = State0) ->
@@ -713,7 +713,7 @@ connection(internal, #hello_request{}, #state{static_env = #static_env{host = Ho
Hello = dtls_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts,
Cache, CacheCb, Renegotiation, Cert),
Version = Hello#client_hello.client_version,
- HelloVersion = dtls_record:hello_version(Version, SslOpts#ssl_options.versions),
+ HelloVersion = dtls_record:hello_version(Version, Versions),
State1 = prepare_flight(State0),
{State2, Actions} = send_handshake(Hello, State1#state{connection_env = CEnv#connection_env{negotiated_version = HelloVersion}}),
State = State2#state{protocol_specific = PS#{flight_state => initial_flight_state(DataTag)},
@@ -774,9 +774,10 @@ format_status(Type, Data) ->
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
-initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions, _}, User,
+initial_state(Role, Host, Port, Socket,
+ {#{client_renegotiation := ClientRenegotiation} = SSLOptions, SocketOptions, _}, User,
{CbModule, DataTag, CloseTag, ErrorTag, PassiveTag}) ->
- #ssl_options{beast_mitigation = BeastMitigation} = SSLOptions,
+ #{beast_mitigation := BeastMitigation} = SSLOptions,
ConnectionStates = dtls_record:init_connection_states(Role, BeastMitigation),
SessionCacheCb = case application:get_env(ssl, session_cb) of
@@ -810,13 +811,13 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions, _}, User,
handshake_env = #handshake_env{
tls_handshake_history = ssl_handshake:init_handshake_history(),
renegotiation = {false, first},
- allow_renegotiate = SSLOptions#ssl_options.client_renegotiation
+ allow_renegotiate = ClientRenegotiation
},
connection_env = #connection_env{user_application = {Monitor, User}},
socket_options = SocketOptions,
%% We do not want to save the password in the state so that
%% could be written in the clear into error logs.
- ssl_options = SSLOptions#ssl_options{password = undefined},
+ ssl_options = SSLOptions#{password => undefined},
session = #session{is_resumable = new},
connection_states = ConnectionStates,
protocol_buffers = #protocol_buffers{},
@@ -989,12 +990,13 @@ handle_alerts([Alert | Alerts], {next_state, StateName, State}) ->
handle_alerts([Alert | Alerts], {next_state, StateName, State, _Actions}) ->
handle_alerts(Alerts, ssl_connection:handle_alert(Alert, StateName, State)).
-handle_own_alert(Alert, Version, StateName, #state{static_env = #static_env{data_tag = udp,
- role = Role},
- ssl_options = Options} = State0) ->
+handle_own_alert(Alert, Version, StateName,
+ #state{static_env = #static_env{data_tag = udp,
+ role = Role},
+ ssl_options = #{log_level := LogLevel}} = State0) ->
case ignore_alert(Alert, State0) of
{true, State} ->
- log_ignore_alert(Options#ssl_options.log_level, StateName, Alert, Role),
+ log_ignore_alert(LogLevel, StateName, Alert, Role),
{next_state, StateName, State};
{false, State} ->
ssl_connection:handle_own_alert(Alert, Version, StateName, State)
@@ -1105,7 +1107,7 @@ send_handshake_flight(#state{static_env = #static_env{socket = Socket,
flight_buffer = #{handshakes := Flight,
change_cipher_spec := undefined},
connection_states = ConnectionStates0,
- ssl_options = #ssl_options{log_level = LogLevel}} = State0,
+ ssl_options = #{log_level := LogLevel}} = State0,
Epoch) ->
%% TODO remove hardcoded Max size
{Encoded, ConnectionStates} =
@@ -1121,7 +1123,7 @@ send_handshake_flight(#state{static_env = #static_env{socket = Socket,
change_cipher_spec := ChangeCipher,
handshakes_after_change_cipher_spec := []},
connection_states = ConnectionStates0,
- ssl_options = #ssl_options{log_level = LogLevel}} = State0,
+ ssl_options = #{log_level := LogLevel}} = State0,
Epoch) ->
{HsBefore, ConnectionStates1} =
encode_handshake_flight(lists:reverse(Flight0), Version, 1400, Epoch, ConnectionStates0),
@@ -1139,7 +1141,7 @@ send_handshake_flight(#state{static_env = #static_env{socket = Socket,
change_cipher_spec := ChangeCipher,
handshakes_after_change_cipher_spec := Flight1},
connection_states = ConnectionStates0,
- ssl_options = #ssl_options{log_level = LogLevel}} = State0,
+ ssl_options = #{log_level := LogLevel}} = State0,
Epoch) ->
{HsBefore, ConnectionStates1} =
encode_handshake_flight(lists:reverse(Flight0), Version, 1400, Epoch-1, ConnectionStates0),
@@ -1160,7 +1162,7 @@ send_handshake_flight(#state{static_env = #static_env{socket = Socket,
change_cipher_spec := ChangeCipher,
handshakes_after_change_cipher_spec := Flight1},
connection_states = ConnectionStates0,
- ssl_options = #ssl_options{log_level = LogLevel}} = State0,
+ ssl_options = #{log_level := LogLevel}} = State0,
Epoch) ->
{EncChangeCipher, ConnectionStates1} =
encode_change_cipher(ChangeCipher, Version, Epoch-1, ConnectionStates0),
@@ -1220,8 +1222,8 @@ send_application_data(Data, From, _StateName,
connection_env = #connection_env{negotiated_version = Version},
handshake_env = HsEnv,
connection_states = ConnectionStates0,
- ssl_options = #ssl_options{renegotiate_at = RenegotiateAt,
- log_level = LogLevel}} = State0) ->
+ ssl_options = #{renegotiate_at := RenegotiateAt,
+ log_level := LogLevel}} = State0) ->
case time_to_renegotiate(Data, ConnectionStates0, RenegotiateAt) of
true ->
diff --git a/lib/ssl/src/dtls_handshake.erl b/lib/ssl/src/dtls_handshake.erl
index d8c0e30973..b2a5fcfa66 100644
--- a/lib/ssl/src/dtls_handshake.erl
+++ b/lib/ssl/src/dtls_handshake.erl
@@ -47,7 +47,7 @@
%%====================================================================
%%--------------------------------------------------------------------
-spec client_hello(ssl:host(), inet:port_number(), ssl_record:connection_states(),
- #ssl_options{}, integer(), atom(), boolean(), der_cert()) ->
+ ssl_options(), integer(), atom(), boolean(), der_cert()) ->
#client_hello{}.
%%
%% Description: Creates a client hello message.
@@ -60,16 +60,15 @@ client_hello(Host, Port, ConnectionStates, SslOpts,
%%--------------------------------------------------------------------
-spec client_hello(ssl:host(), inet:port_number(), term(), ssl_record:connection_states(),
- #ssl_options{}, integer(), atom(), boolean(), der_cert()) ->
+ ssl_options(), integer(), atom(), boolean(), der_cert()) ->
#client_hello{}.
%%
%% Description: Creates a client hello message.
%%--------------------------------------------------------------------
client_hello(Host, Port, Cookie, ConnectionStates,
- #ssl_options{versions = Versions,
- ciphers = UserSuites,
- fallback = Fallback
- } = SslOpts,
+ #{versions := Versions,
+ ciphers := UserSuites,
+ fallback := Fallback} = SslOpts,
Cache, CacheCb, Renegotiation, OwnCert) ->
Version = dtls_record:highest_protocol_version(Versions),
Pending = ssl_record:pending_connection_state(ConnectionStates, read),
@@ -97,7 +96,7 @@ hello(#server_hello{server_version = Version, random = Random,
cipher_suite = CipherSuite,
compression_method = Compression,
session_id = SessionId, extensions = HelloExt},
- #ssl_options{versions = SupportedVersions} = SslOpt,
+ #{versions := SupportedVersions} = SslOpt,
ConnectionStates0, Renegotiation) ->
case dtls_record:is_acceptable_version(Version, SupportedVersions) of
true ->
@@ -108,7 +107,7 @@ hello(#server_hello{server_version = Version, random = Random,
?ALERT_REC(?FATAL, ?PROTOCOL_VERSION)
end;
hello(#client_hello{client_version = ClientVersion} = Hello,
- #ssl_options{versions = Versions} = SslOpts,
+ #{versions := Versions} = SslOpts,
Info, Renegotiation) ->
Version = ssl_handshake:select_version(dtls_record, ClientVersion, Versions),
handle_client_hello(Version, Hello, SslOpts, Info, Renegotiation).
@@ -151,7 +150,7 @@ encode_handshake(Handshake, Version, Seq) ->
%%--------------------------------------------------------------------
%%--------------------------------------------------------------------
--spec get_dtls_handshake(ssl_record:ssl_version(), binary(), #protocol_buffers{}, #ssl_options{}) ->
+-spec get_dtls_handshake(ssl_record:ssl_version(), binary(), #protocol_buffers{}, ssl_options()) ->
{[dtls_handshake()], #protocol_buffers{}}.
%%
%% Description: Given buffered and new data from dtls_record, collects
@@ -170,10 +169,10 @@ handle_client_hello(Version,
compression_methods = Compressions,
random = Random,
extensions = HelloExt},
- #ssl_options{versions = Versions,
- signature_algs = SupportedHashSigns,
- eccs = SupportedECCs,
- honor_ecc_order = ECCOrder} = SslOpts,
+ #{versions := Versions,
+ signature_algs := SupportedHashSigns,
+ eccs := SupportedECCs,
+ honor_ecc_order := ECCOrder} = SslOpts,
{Port, Session0, Cache, CacheCb, ConnectionStates0, Cert, _},
Renegotiation) ->
case dtls_record:is_acceptable_version(Version, Versions) of
@@ -255,7 +254,8 @@ enc_handshake(#client_hello{client_version = {Major, Minor},
CmLength = byte_size(BinCompMethods),
BinCipherSuites = list_to_binary(CipherSuites),
CsLength = byte_size(BinCipherSuites),
- ExtensionsBin = ssl_handshake:encode_hello_extensions(HelloExtensions),
+ ExtensionsBin = ssl_handshake:encode_hello_extensions(HelloExtensions,
+ dtls_v1:corresponding_tls_version({Major, Minor})),
{?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
?BYTE(SIDLength), SessionID/binary,
@@ -316,14 +316,14 @@ handle_fragments(Version, FragmentData, Buffers0, Options, Acc) ->
do_handle_fragments(_, [], Buffers, _Options, Acc) ->
{lists:reverse(Acc), Buffers};
-do_handle_fragments(Version, [Fragment | Fragments], Buffers0, Options, Acc) ->
+do_handle_fragments(Version, [Fragment | Fragments], Buffers0, #{log_level := LogLevel} = Options, Acc) ->
case reassemble(Version, Fragment, Buffers0) of
{more_data, Buffers} when Fragments == [] ->
{lists:reverse(Acc), Buffers};
{more_data, Buffers} ->
do_handle_fragments(Version, Fragments, Buffers, Options, Acc);
{{Handshake, _} = HsPacket, Buffers} ->
- ssl_logger:debug(Options#ssl_options.log_level, inbound, 'handshake', Handshake),
+ ssl_logger:debug(LogLevel, inbound, 'handshake', Handshake),
do_handle_fragments(Version, Fragments, Buffers, Options, [HsPacket | Acc])
end.
diff --git a/lib/ssl/src/dtls_record.erl b/lib/ssl/src/dtls_record.erl
index 8b8db7b2de..ee0ce2d22a 100644
--- a/lib/ssl/src/dtls_record.erl
+++ b/lib/ssl/src/dtls_record.erl
@@ -163,7 +163,7 @@ current_connection_state_epoch(#{current_write := #{epoch := Epoch}},
%%--------------------------------------------------------------------
-spec get_dtls_records(binary(), {atom(), atom(), ssl_record:ssl_version(), [ssl_record:ssl_version()]}, binary(),
- #ssl_options{}) -> {[binary()], binary()} | #alert{}.
+ ssl_options()) -> {[binary()], binary()} | #alert{}.
%%
%% Description: Given old buffer and new data from UDP/SCTP, packs up a records
%% and returns it as a list of tls_compressed binaries also returns leftover
@@ -399,13 +399,13 @@ initial_connection_state(ConnectionEnd, BeastMitigation) ->
get_dtls_records_aux({DataTag, StateName, _, Versions} = Vinfo, <<?BYTE(Type),?BYTE(MajVer),?BYTE(MinVer),
?UINT16(Epoch), ?UINT48(SequenceNumber),
?UINT16(Length), Data:Length/binary, Rest/binary>> = RawDTLSRecord,
- Acc, SslOpts) when ((StateName == hello) orelse
- ((StateName == certify) andalso (DataTag == udp)) orelse
- ((StateName == abbreviated) andalso(DataTag == udp)))
- andalso
- ((Type == ?HANDSHAKE) orelse
- (Type == ?ALERT)) ->
- ssl_logger:debug(SslOpts#ssl_options.log_level, inbound, 'record', [RawDTLSRecord]),
+ Acc, #{log_level := LogLevel} = SslOpts)
+ when ((StateName == hello)
+ orelse ((StateName == certify) andalso (DataTag == udp))
+ orelse ((StateName == abbreviated) andalso (DataTag == udp))) andalso ((Type == ?HANDSHAKE)
+ orelse
+ (Type == ?ALERT)) ->
+ ssl_logger:debug(LogLevel, inbound, 'record', [RawDTLSRecord]),
case is_acceptable_version({MajVer, MinVer}, Versions) of
true ->
get_dtls_records_aux(Vinfo, Rest, [#ssl_tls{type = Type,
@@ -418,11 +418,11 @@ get_dtls_records_aux({DataTag, StateName, _, Versions} = Vinfo, <<?BYTE(Type),?B
get_dtls_records_aux({_, _, Version, _} = Vinfo, <<?BYTE(Type),?BYTE(MajVer),?BYTE(MinVer),
?UINT16(Epoch), ?UINT48(SequenceNumber),
?UINT16(Length), Data:Length/binary, Rest/binary>> = RawDTLSRecord,
- Acc, SslOpts) when (Type == ?APPLICATION_DATA) orelse
+ Acc, #{log_level := LogLevel} = SslOpts) when (Type == ?APPLICATION_DATA) orelse
(Type == ?HANDSHAKE) orelse
(Type == ?ALERT) orelse
(Type == ?CHANGE_CIPHER_SPEC) ->
- ssl_logger:debug(SslOpts#ssl_options.log_level, inbound, 'record', [RawDTLSRecord]),
+ ssl_logger:debug(LogLevel, inbound, 'record', [RawDTLSRecord]),
case {MajVer, MinVer} of
Version ->
get_dtls_records_aux(Vinfo, Rest, [#ssl_tls{type = Type,
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index 20b1e85ceb..0d6501b5ee 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -97,7 +97,8 @@
new_ssl_options/3,
suite_to_str/1,
suite_to_openssl_str/1,
- str_to_suite/1]).
+ str_to_suite/1,
+ options_to_map/1]).
-deprecated({ssl_accept, 1, eventually}).
-deprecated({ssl_accept, 2, eventually}).
@@ -128,7 +129,8 @@
tls_alert/0,
srp_param_type/0,
named_curve/0,
- sign_scheme/0]).
+ sign_scheme/0,
+ group/0]).
%% -------------------------------------------------------------------------------------------------------
@@ -243,7 +245,7 @@
secp160r2. % exported
-type group() :: secp256r1 | secp384r1 | secp521r1 | ffdhe2048 |
- ffdhe3072 | ffdhe4096 | ffdhe6144 | ffdhe8192.
+ ffdhe3072 | ffdhe4096 | ffdhe6144 | ffdhe8192. % exported
-type srp_param_type() :: srp_1024 |
srp_1536 |
@@ -296,6 +298,7 @@
{ciphers, cipher_suites()} |
{eccs, [named_curve()]} |
{signature_algs_cert, signature_schemes()} |
+ {supported_groups, supported_groups()} |
{secure_renegotiate, secure_renegotiation()} |
{depth, allowed_cert_chain_length()} |
{verify_fun, custom_verify()} |
@@ -342,6 +345,7 @@
-type protocol_versions() :: [protocol_version()].
-type signature_algs() :: [{hash(), sign_algo()}].
-type signature_schemes() :: [sign_scheme()].
+-type supported_groups() :: [group()].
-type custom_user_lookup() :: {Lookupfun :: fun(), UserState :: any()}.
-type padding_check() :: boolean().
-type beast_mitigation() :: one_n_minus_one | zero_n | disabled.
@@ -435,6 +439,7 @@
elliptic_curves => [public_key:oid()],
sni => hostname()}. % exported
%% -------------------------------------------------------------------------------------------------------
+-define(SSL_OPTIONS, record_info(fields, ssl_options)).
%%%--------------------------------------------------------------------
%%% API
@@ -781,7 +786,13 @@ close(#sslsocket{pid = [TLSPid|_]},
{Pid, Timeout} = DownGrade) when is_pid(TLSPid),
is_pid(Pid),
(is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity) ->
- ssl_connection:close(TLSPid, {close, DownGrade});
+ case ssl_connection:close(TLSPid, {close, DownGrade}) of
+ ok -> %% In normal close {error, closed} is regarded as ok, as it is not interesting which side
+ %% that got to do the actual close. But in the downgrade case only {ok, Port} is a sucess.
+ {error, closed};
+ Other ->
+ Other
+ end;
close(#sslsocket{pid = [TLSPid|_]}, Timeout) when is_pid(TLSPid),
(is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity) ->
ssl_connection:close(TLSPid, {close, Timeout});
@@ -979,7 +990,8 @@ cipher_suites(all) ->
%% Description: Returns all default and all supported cipher suites for a
%% TLS/DTLS version
%%--------------------------------------------------------------------
-cipher_suites(Base, Version) when Version == 'tlsv1.2';
+cipher_suites(Base, Version) when Version == 'tlsv1.3';
+ Version == 'tlsv1.2';
Version == 'tlsv1.1';
Version == tlsv1;
Version == sslv3 ->
@@ -1448,15 +1460,18 @@ do_listen(Port, #config{transport_info = {Transport, _, _, _,_}} = Config, tls_c
do_listen(Port, Config, dtls_connection) ->
dtls_socket:listen(Port, Config).
-%% Handle extra ssl options given to ssl_accept
--spec handle_options([any()], #ssl_options{}) -> #ssl_options{}
- ; ([any()], client | server) -> {ok, #config{}}.
+
+-spec handle_options([any()], client | server) -> {ok, #config{}};
+ ([any()], ssl_options()) -> ssl_options().
+
handle_options(Opts, Role) ->
handle_options(Opts, Role, undefined).
-handle_options(Opts0, #ssl_options{protocol = Protocol, cacerts = CaCerts0,
- cacertfile = CaCertFile0} = InheritedSslOpts, _) ->
+%% Handle ssl options at handshake_continue
+handle_options(Opts0, #{protocol := Protocol,
+ cacerts := CaCerts0,
+ cacertfile := CaCertFile0} = InheritedSslOpts, _) ->
RecordCB = record_cb(Protocol),
CaCerts = handle_option(cacerts, Opts0, CaCerts0),
{Verify, FailIfNoPeerCert, CaCertDefault, VerifyFun, PartialChainHanlder,
@@ -1468,13 +1483,13 @@ handle_options(Opts0, #ssl_options{protocol = Protocol, cacerts = CaCerts0,
CAFile
end,
- NewVerifyOpts = InheritedSslOpts#ssl_options{cacerts = CaCerts,
- cacertfile = CaCertFile,
- verify = Verify,
- verify_fun = VerifyFun,
- partial_chain = PartialChainHanlder,
- fail_if_no_peer_cert = FailIfNoPeerCert,
- verify_client_once = VerifyClientOnce},
+ NewVerifyOpts = InheritedSslOpts#{cacerts => CaCerts,
+ cacertfile => CaCertFile,
+ verify => Verify,
+ verify_fun => VerifyFun,
+ partial_chain => PartialChainHanlder,
+ fail_if_no_peer_cert => FailIfNoPeerCert,
+ verify_client_once => VerifyClientOnce},
SslOpts1 = lists:foldl(fun(Key, PropList) ->
proplists:delete(Key, PropList)
end, Opts0, [cacerts, cacertfile, verify, verify_fun, partial_chain,
@@ -1486,10 +1501,10 @@ handle_options(Opts0, #ssl_options{protocol = Protocol, cacerts = CaCerts0,
Versions0 = [RecordCB:protocol_version(Vsn) || Vsn <- Value],
Versions1 = lists:sort(fun RecordCB:is_higher/2, Versions0),
new_ssl_options(proplists:delete(versions, SslOpts1),
- NewVerifyOpts#ssl_options{versions = Versions1}, record_cb(Protocol))
+ NewVerifyOpts#{versions => Versions1}, record_cb(Protocol))
end;
-%% Handle all options in listen and connect
+%% Handle all options in listen, connect and handshake
handle_options(Opts0, Role, Host) ->
Opts = proplists:expand([{binary, [{mode, binary}]},
{list, [{mode, list}]}], Opts0),
@@ -1540,36 +1555,21 @@ handle_options(Opts0, Role, Host) ->
user_lookup_fun = handle_option(user_lookup_fun, Opts, undefined),
psk_identity = handle_option(psk_identity, Opts, undefined),
srp_identity = handle_option(srp_identity, Opts, undefined),
- ciphers = handle_cipher_option(proplists:get_value(ciphers, Opts, []),
- HighestVersion),
- eccs = handle_eccs_option(proplists:get_value(eccs, Opts, eccs()),
- HighestVersion),
- supported_groups = handle_supported_groups_option(
- proplists:get_value(supported_groups, Opts, groups(default)),
- HighestVersion),
- signature_algs =
- handle_hashsigns_option(
- proplists:get_value(
- signature_algs,
- Opts,
- default_option_role_sign_algs(server,
- tls_v1:default_signature_algs(HighestVersion),
- Role,
- HighestVersion)),
- tls_version(HighestVersion)),
- signature_algs_cert =
- handle_signature_algorithms_option(
- proplists:get_value(
- signature_algs_cert,
- Opts,
- undefined), %% Do not send by default
- tls_version(HighestVersion)),
- reuse_sessions = handle_reuse_sessions_option(reuse_sessions, Opts, Role),
- reuse_session = handle_reuse_session_option(reuse_session, Opts, Role),
+ ciphers = handle_option(ciphers, Opts, undefined, undefined, undefined, HighestVersion),
+
+ eccs = handle_option(eccs, Opts, undefined, undefined, undefined, HighestVersion),
+
+ supported_groups = handle_option(supported_groups, Opts, undefined, undefined, undefined,
+ HighestVersion),
+ signature_algs = handle_option(signature_algs, Opts, undefined, Role, undefined, HighestVersion),
+ signature_algs_cert = handle_option(signature_algs_cert, Opts, undefined, undefined, undefined,
+ HighestVersion),
+ reuse_sessions = handle_option(reuse_sessions, Opts, undefined, Role),
+
+ reuse_session = handle_option(reuse_session, Opts, undefined, Role),
+
secure_renegotiate = handle_option(secure_renegotiate, Opts, true),
- client_renegotiation = handle_option(client_renegotiation, Opts,
- default_option_role(server, true, Role),
- server, Role),
+ client_renegotiation = handle_option(client_renegotiation, Opts, undefined, Role),
renegotiate_at = handle_option(renegotiate_at, Opts, ?DEFAULT_RENEGOTIATE_AT),
hibernate_after = handle_option(hibernate_after, Opts, infinity),
erl_dist = handle_option(erl_dist, Opts, false),
@@ -1580,71 +1580,61 @@ handle_options(Opts0, Role, Host) ->
next_protocols_advertised =
handle_option(next_protocols_advertised, Opts, undefined),
next_protocol_selector =
- make_next_protocol_selector(
- handle_option(client_preferred_next_protocols, Opts, undefined)),
- server_name_indication = handle_option(server_name_indication, Opts,
- default_option_role(client,
- server_name_indication_default(Host), Role)),
+ handle_option(next_protocol_selector, Opts, undefined),
+ server_name_indication =
+ handle_option(server_name_indication, Opts, undefined, Role, Host),
sni_hosts = handle_option(sni_hosts, Opts, []),
sni_fun = handle_option(sni_fun, Opts, undefined),
- honor_cipher_order = handle_option(honor_cipher_order, Opts,
- default_option_role(server, false, Role),
- server, Role),
- honor_ecc_order = handle_option(honor_ecc_order, Opts,
- default_option_role(server, false, Role),
- server, Role),
+ honor_cipher_order = handle_option(honor_cipher_order, Opts, undefined, Role),
+ honor_ecc_order = handle_option(honor_ecc_order, Opts, undefined, Role),
protocol = Protocol,
padding_check = proplists:get_value(padding_check, Opts, true),
- beast_mitigation = handle_option(beast_mitigation, Opts, one_n_minus_one),
- fallback = handle_option(fallback, Opts,
- proplists:get_value(fallback, Opts,
- default_option_role(client,
- false, Role)),
- client, Role),
+ beast_mitigation = handle_option(beast_mitigation, Opts, one_n_minus_one),
+ fallback = handle_option(fallback, Opts, undefined, Role),
+
crl_check = handle_option(crl_check, Opts, false),
crl_cache = handle_option(crl_cache, Opts, {ssl_crl_cache, {internal, []}}),
max_handshake_size = handle_option(max_handshake_size, Opts, ?DEFAULT_MAX_HANDSHAKE_SIZE),
handshake = handle_option(handshake, Opts, full),
- customize_hostname_check = handle_option(customize_hostname_check, Opts, [])
+ customize_hostname_check = handle_option(customize_hostname_check, Opts, [])
},
LogLevel = handle_option(log_alert, Opts, true),
- SSLOptions = SSLOptions0#ssl_options{
+ SSLOptions1 = SSLOptions0#ssl_options{
log_level = handle_option(log_level, Opts, LogLevel)
},
CbInfo = handle_option(cb_info, Opts, default_cb_info(Protocol)),
- 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,
- user_lookup_fun, psk_identity, srp_identity, ciphers,
- reuse_session, reuse_sessions, ssl_imp, client_renegotiation,
- cb_info, renegotiate_at, secure_renegotiate, hibernate_after,
- erl_dist, alpn_advertised_protocols, sni_hosts, sni_fun,
- alpn_preferred_protocols, next_protocols_advertised,
- client_preferred_next_protocols, log_alert, log_level,
- server_name_indication, honor_cipher_order, padding_check, crl_check, crl_cache,
- fallback, signature_algs, signature_algs_cert, eccs, honor_ecc_order,
- beast_mitigation, max_handshake_size, handshake, customize_hostname_check,
- supported_groups],
SockOpts = lists:foldl(fun(Key, PropList) ->
proplists:delete(Key, PropList)
- end, Opts, SslOptions),
+ end, Opts, ?SSL_OPTIONS ++
+ [ssl_imp, %% TODO: remove ssl_imp
+ client_preferred_next_protocols, %% next_protocol_selector
+ log_alert,
+ cb_info]),
{Sock, Emulated} = emulated_options(Protocol, SockOpts),
ConnetionCb = connection_cb(Opts),
-
+ SSLOptions = options_to_map(SSLOptions1),
{ok, #config{ssl = SSLOptions, emulated = Emulated, inet_ssl = Sock,
inet_user = Sock, transport_info = CbInfo, connection_cb = ConnetionCb
}}.
-handle_option(OptionName, Opts, Default, Role, Role) ->
- handle_option(OptionName, Opts, Default);
-handle_option(_, _, undefined = Value, _, _) ->
- Value.
+%% handle_option(OptionName, Opts, Default, Role, Role) ->
+%% handle_option(OptionName, Opts, Default);
+%% handle_option(_, _, undefined = Value, _, _) ->
+%% Value.
-handle_option(sni_fun, Opts, Default) ->
+handle_option(OptionName, Opts, Default) ->
+ handle_option(OptionName, Opts, Default, undefined, undefined, undefined).
+%%
+handle_option(OptionName, Opts, Default, Role) ->
+ handle_option(OptionName, Opts, Default, Role, undefined, undefined).
+%%
+handle_option(OptionName, Opts, Default, Role, Host) ->
+ handle_option(OptionName, Opts, Default, Role, Host, undefined).
+%%
+handle_option(sni_fun, Opts, Default, _Role, _Host, _Version) ->
OptFun = validate_option(sni_fun,
proplists:get_value(sni_fun, Opts, Default)),
OptHosts = proplists:get_value(sni_hosts, Opts, undefined),
@@ -1656,14 +1646,91 @@ handle_option(sni_fun, Opts, Default) ->
_ ->
throw({error, {conflict_options, [sni_fun, sni_hosts]}})
end;
-handle_option(cb_info, Opts, Default) ->
+handle_option(cb_info, Opts, Default, _Role, _Host, _Version) ->
CbInfo = proplists:get_value(cb_info, Opts, Default),
true = validate_option(cb_info, CbInfo),
handle_cb_info(CbInfo, Default);
-handle_option(OptionName, Opts, Default) ->
+handle_option(ciphers = Key, Opts, _Default, _Role, _Host, HighestVersion) ->
+ handle_cipher_option(proplists:get_value(Key, Opts, []),
+ HighestVersion);
+handle_option(eccs = Key, Opts, _Default, _Role, _Host, HighestVersion) ->
+ handle_eccs_option(proplists:get_value(Key, Opts, eccs()),
+ HighestVersion);
+handle_option(supported_groups = Key, Opts, _Default, _Role, _Host, HighestVersion) ->
+ handle_supported_groups_option(proplists:get_value(Key, Opts, groups(default)),
+ HighestVersion);
+handle_option(signature_algs = Key, Opts, _Default, Role, _Host, HighestVersion) ->
+ handle_hashsigns_option(
+ proplists:get_value(Key,
+ Opts,
+ default_option_role_sign_algs(server,
+ tls_v1:default_signature_algs(HighestVersion),
+ Role,
+ HighestVersion)),
+ tls_version(HighestVersion));
+handle_option(signature_algs_cert = Key, Opts, _Default, _Role, _Host, HighestVersion) ->
+ handle_signature_algorithms_option(
+ proplists:get_value(Key,
+ Opts,
+ undefined), %% Do not send by default
+ tls_version(HighestVersion));
+handle_option(reuse_sessions = Key, Opts, _Default, client, _Host, _Version) ->
+ Value = proplists:get_value(Key, Opts, true),
+ validate_option(Key, Value),
+ Value;
+handle_option(reuse_sessions = Key, Opts0, _Default, server, _Host, _Version) ->
+ Opts = proplists:delete({Key, save}, Opts0),
+ Value = proplists:get_value(Key, Opts, true),
+ validate_option(Key, Value),
+ Value;
+handle_option(reuse_session = Key, Opts, _Default, client, _Host, _Version) ->
+ Value = proplists:get_value(Key, Opts, undefined),
+ validate_option(Key, Value),
+ Value;
+handle_option(reuse_session = Key, Opts, _Default, server, _Host, _Version) ->
+ ReuseSessionFun = fun(_, _, _, _) -> true end,
+ Value = proplists:get_value(Key, Opts, ReuseSessionFun),
+ validate_option(Key, Value),
+ Value;
+handle_option(fallback = Key, Opts, _Default, Role, _Host, _Version) ->
+ Value = proplists:get_value(Key, Opts, default_option_role(client, false, Role)),
+ assert_role(client_only, Role, Key, Value),
+ validate_option(Key, Value);
+handle_option(client_renegotiation = Key, Opts, _Default, Role, _Host, _Version) ->
+ Value = proplists:get_value(Key, Opts, default_option_role(server, true, Role)),
+ assert_role(server_only, Role, Key, Value),
+ validate_option(Key, Value);
+handle_option(honor_cipher_order = Key, Opts, _Default, Role, _Host, _Version) ->
+ Value = proplists:get_value(Key, Opts, default_option_role(server, false, Role)),
+ assert_role(server_only, Role, Key, Value),
+ validate_option(Key, Value);
+handle_option(honor_ecc_order = Key, Opts, _Default, Role, _Host, _Version) ->
+ Value = proplists:get_value(Key, Opts, default_option_role(server, false, Role)),
+ assert_role(server_only, Role, Key, Value),
+ validate_option(Key, Value);
+handle_option(next_protocol_selector = _Key, Opts, _Default, _Role, _Host, _Version) ->
+ make_next_protocol_selector(
+ handle_option(client_preferred_next_protocols, Opts, undefined, undefined, undefined, undefined));
+handle_option(server_name_indication = Key, Opts, _Default, Role, Host, _Version) ->
+ Default = default_option_role(client, server_name_indication_default(Host), Role),
+ validate_option(Key, proplists:get_value(Key, Opts, Default));
+handle_option(OptionName, Opts, Default, _Role, _Host, _Version) ->
validate_option(OptionName,
proplists:get_value(OptionName, Opts, Default)).
+
+assert_role(client_only, client, _, _) ->
+ ok;
+assert_role(server_only, server, _, _) ->
+ ok;
+assert_role(client_only, _, _, undefined) ->
+ ok;
+assert_role(server_only, _, _, undefined) ->
+ ok;
+assert_role(Type, _, Key, _) ->
+ throw({error, {option, Type, Key}}).
+
+
validate_option(versions, Versions) ->
validate_versions(Versions, Versions);
validate_option(verify, Value)
@@ -1909,6 +1976,13 @@ validate_option(cb_info, {V1, V2, V3, V4, V5}) when is_atom(V1),
true;
validate_option(cb_info, _) ->
false;
+validate_option(Opt, undefined = Value) ->
+ case lists:member(Opt, ?SSL_OPTIONS) of
+ true ->
+ Value;
+ false ->
+ throw({error, {options, {Opt, Value}}})
+ end;
validate_option(Opt, Value) ->
throw({error, {options, {Opt, Value}}}).
@@ -1953,26 +2027,6 @@ handle_signature_algorithms_option(Value, Version) when is_list(Value)
handle_signature_algorithms_option(_, _Version) ->
undefined.
-handle_reuse_sessions_option(Key, Opts, client) ->
- Value = proplists:get_value(Key, Opts, true),
- validate_option(Key, Value),
- Value;
-handle_reuse_sessions_option(Key, Opts0, server) ->
- Opts = proplists:delete({Key, save}, Opts0),
- Value = proplists:get_value(Key, Opts, true),
- validate_option(Key, Value),
- Value.
-
-handle_reuse_session_option(Key, Opts, client) ->
- Value = proplists:get_value(Key, Opts, undefined),
- validate_option(Key, Value),
- Value;
-handle_reuse_session_option(Key, Opts, server) ->
- ReuseSessionFun = fun(_, _, _, _) -> true end,
- Value = proplists:get_value(Key, Opts, ReuseSessionFun),
- validate_option(Key, Value),
- Value.
-
validate_options([]) ->
[];
validate_options([{Opt, Value} | Tail]) ->
@@ -2221,101 +2275,104 @@ assert_proplist([inet6 | Rest]) ->
assert_proplist([Value | _]) ->
throw({option_not_a_key_value_tuple, Value}).
-new_ssl_options([], #ssl_options{} = Opts, _) ->
+new_ssl_options([], #{} = Opts, _) ->
Opts;
-new_ssl_options([{verify_client_once, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{verify_client_once =
- validate_option(verify_client_once, Value)}, RecordCB);
-new_ssl_options([{depth, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{depth = validate_option(depth, Value)}, RecordCB);
-new_ssl_options([{cert, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{cert = validate_option(cert, Value)}, RecordCB);
-new_ssl_options([{certfile, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{certfile = validate_option(certfile, Value)}, RecordCB);
-new_ssl_options([{key, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{key = validate_option(key, Value)}, RecordCB);
-new_ssl_options([{keyfile, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{keyfile = validate_option(keyfile, Value)}, RecordCB);
-new_ssl_options([{password, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{password = validate_option(password, Value)}, RecordCB);
-new_ssl_options([{dh, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{dh = validate_option(dh, Value)}, RecordCB);
-new_ssl_options([{dhfile, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{dhfile = validate_option(dhfile, Value)}, RecordCB);
-new_ssl_options([{user_lookup_fun, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{user_lookup_fun = validate_option(user_lookup_fun, Value)}, RecordCB);
-new_ssl_options([{psk_identity, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{psk_identity = validate_option(psk_identity, Value)}, RecordCB);
-new_ssl_options([{srp_identity, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{srp_identity = validate_option(srp_identity, Value)}, RecordCB);
-new_ssl_options([{ciphers, Value} | Rest], #ssl_options{versions = Versions} = Opts, RecordCB) ->
+new_ssl_options([{verify_client_once, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{verify_client_once =>
+ validate_option(verify_client_once, Value)}, RecordCB);
+new_ssl_options([{depth, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{depth => validate_option(depth, Value)}, RecordCB);
+new_ssl_options([{cert, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{cert => validate_option(cert, Value)}, RecordCB);
+new_ssl_options([{certfile, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{certfile => validate_option(certfile, Value)}, RecordCB);
+new_ssl_options([{key, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{key => validate_option(key, Value)}, RecordCB);
+new_ssl_options([{keyfile, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{keyfile => validate_option(keyfile, Value)}, RecordCB);
+new_ssl_options([{password, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{password => validate_option(password, Value)}, RecordCB);
+new_ssl_options([{dh, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{dh => validate_option(dh, Value)}, RecordCB);
+new_ssl_options([{dhfile, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{dhfile => validate_option(dhfile, Value)}, RecordCB);
+new_ssl_options([{user_lookup_fun, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{user_lookup_fun => validate_option(user_lookup_fun, Value)}, RecordCB);
+new_ssl_options([{psk_identity, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{psk_identity => validate_option(psk_identity, Value)}, RecordCB);
+new_ssl_options([{srp_identity, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{srp_identity => validate_option(srp_identity, Value)}, RecordCB);
+new_ssl_options([{ciphers, Value} | Rest], #{versions := Versions} = Opts, RecordCB) ->
Ciphers = handle_cipher_option(Value, RecordCB:highest_protocol_version(Versions)),
- new_ssl_options(Rest,
- Opts#ssl_options{ciphers = Ciphers}, RecordCB);
-new_ssl_options([{reuse_session, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{reuse_session = validate_option(reuse_session, Value)}, RecordCB);
-new_ssl_options([{reuse_sessions, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{reuse_sessions = validate_option(reuse_sessions, Value)}, RecordCB);
-new_ssl_options([{ssl_imp, _Value} | Rest], #ssl_options{} = Opts, RecordCB) -> %% Not used backwards compatibility
+ new_ssl_options(Rest, Opts#{ciphers => Ciphers}, RecordCB);
+new_ssl_options([{reuse_session, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{reuse_session => validate_option(reuse_session, Value)}, RecordCB);
+new_ssl_options([{reuse_sessions, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{reuse_sessions => validate_option(reuse_sessions, Value)}, RecordCB);
+new_ssl_options([{ssl_imp, _Value} | Rest], #{} = Opts, RecordCB) -> %% Not used backwards compatibility
new_ssl_options(Rest, Opts, RecordCB);
-new_ssl_options([{renegotiate_at, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{ renegotiate_at = validate_option(renegotiate_at, Value)}, RecordCB);
-new_ssl_options([{secure_renegotiate, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{secure_renegotiate = validate_option(secure_renegotiate, Value)}, RecordCB);
-new_ssl_options([{client_renegotiation, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{client_renegotiation = validate_option(client_renegotiation, Value)}, RecordCB);
-new_ssl_options([{hibernate_after, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{hibernate_after = validate_option(hibernate_after, Value)}, RecordCB);
-new_ssl_options([{alpn_advertised_protocols, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{alpn_advertised_protocols = validate_option(alpn_advertised_protocols, Value)}, RecordCB);
-new_ssl_options([{alpn_preferred_protocols, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{alpn_preferred_protocols = validate_option(alpn_preferred_protocols, Value)}, RecordCB);
-new_ssl_options([{next_protocols_advertised, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{next_protocols_advertised = validate_option(next_protocols_advertised, Value)}, RecordCB);
-new_ssl_options([{client_preferred_next_protocols, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{next_protocol_selector =
- make_next_protocol_selector(validate_option(client_preferred_next_protocols, Value))}, RecordCB);
-new_ssl_options([{log_alert, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{log_level = validate_option(log_alert, Value)}, RecordCB);
-new_ssl_options([{log_level, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{log_level = validate_option(log_level, Value)}, RecordCB);
-new_ssl_options([{server_name_indication, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{server_name_indication = validate_option(server_name_indication, Value)}, RecordCB);
-new_ssl_options([{honor_cipher_order, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{honor_cipher_order = validate_option(honor_cipher_order, Value)}, RecordCB);
-new_ssl_options([{honor_ecc_order, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{honor_ecc_order = validate_option(honor_ecc_order, Value)}, RecordCB);
-new_ssl_options([{eccs, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+new_ssl_options([{renegotiate_at, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{renegotiate_at => validate_option(renegotiate_at, Value)}, RecordCB);
+new_ssl_options([{secure_renegotiate, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{secure_renegotiate => validate_option(secure_renegotiate, Value)}, RecordCB);
+new_ssl_options([{client_renegotiation, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{client_renegotiation => validate_option(client_renegotiation, Value)}, RecordCB);
+new_ssl_options([{hibernate_after, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{hibernate_after => validate_option(hibernate_after, Value)}, RecordCB);
+new_ssl_options([{alpn_advertised_protocols, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{alpn_advertised_protocols => validate_option(alpn_advertised_protocols, Value)},
+ RecordCB);
+new_ssl_options([{alpn_preferred_protocols, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{alpn_preferred_protocols => validate_option(alpn_preferred_protocols, Value)},
+ RecordCB);
+new_ssl_options([{next_protocols_advertised, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{next_protocols_advertised => validate_option(next_protocols_advertised, Value)},
+ RecordCB);
+new_ssl_options([{client_preferred_next_protocols, Value} | Rest], #{} = Opts, RecordCB) ->
new_ssl_options(Rest,
- Opts#ssl_options{eccs =
- handle_eccs_option(Value, RecordCB:highest_protocol_version())
+ Opts#{next_protocol_selector =>
+ make_next_protocol_selector(validate_option(client_preferred_next_protocols, Value))},
+ RecordCB);
+new_ssl_options([{log_alert, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{log_level => validate_option(log_alert, Value)}, RecordCB);
+new_ssl_options([{log_level, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{log_level => validate_option(log_level, Value)}, RecordCB);
+new_ssl_options([{server_name_indication, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{server_name_indication => validate_option(server_name_indication, Value)}, RecordCB);
+new_ssl_options([{honor_cipher_order, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{honor_cipher_order => validate_option(honor_cipher_order, Value)}, RecordCB);
+new_ssl_options([{honor_ecc_order, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#{honor_ecc_order => validate_option(honor_ecc_order, Value)}, RecordCB);
+new_ssl_options([{eccs, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest,
+ Opts#{eccs => handle_eccs_option(Value, RecordCB:highest_protocol_version())
},
RecordCB);
-new_ssl_options([{supported_groups, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+new_ssl_options([{supported_groups, Value} | Rest], #{} = Opts, RecordCB) ->
new_ssl_options(Rest,
- Opts#ssl_options{supported_groups =
- handle_supported_groups_option(Value, RecordCB:highest_protocol_version())
+ Opts#{supported_groups =>
+ handle_supported_groups_option(Value, RecordCB:highest_protocol_version())
},
RecordCB);
-new_ssl_options([{signature_algs, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest,
- Opts#ssl_options{signature_algs =
- handle_hashsigns_option(Value,
- tls_version(RecordCB:highest_protocol_version()))},
+new_ssl_options([{signature_algs, Value} | Rest], #{} = Opts, RecordCB) ->
+ new_ssl_options(Rest,
+ Opts#{signature_algs =>
+ handle_hashsigns_option(Value,
+ tls_version(RecordCB:highest_protocol_version()))},
RecordCB);
-new_ssl_options([{signature_algs_cert, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+new_ssl_options([{signature_algs_cert, Value} | Rest], #{} = Opts, RecordCB) ->
new_ssl_options(
Rest,
- Opts#ssl_options{signature_algs_cert =
- handle_signature_algorithms_option(
- Value,
- tls_version(RecordCB:highest_protocol_version()))},
+ Opts#{signature_algs_cert =>
+ handle_signature_algorithms_option(
+ Value,
+ tls_version(RecordCB:highest_protocol_version()))},
RecordCB);
-new_ssl_options([{protocol, dtls = Value} | Rest], #ssl_options{} = Opts, dtls_record = RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{protocol = Value}, RecordCB);
-new_ssl_options([{protocol, tls = Value} | Rest], #ssl_options{} = Opts, tls_record = RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{protocol = Value}, RecordCB);
-new_ssl_options([{Key, Value} | _Rest], #ssl_options{}, _) ->
+new_ssl_options([{protocol, dtls = Value} | Rest], #{} = Opts, dtls_record = RecordCB) ->
+ new_ssl_options(Rest, Opts#{protocol => Value}, RecordCB);
+new_ssl_options([{protocol, tls = Value} | Rest], #{} = Opts, tls_record = RecordCB) ->
+ new_ssl_options(Rest, Opts#{protocol => Value}, RecordCB);
+new_ssl_options([{Key, Value} | _Rest], #{}, _) ->
throw({error, {options, {Key, Value}}}).
@@ -2429,3 +2486,10 @@ add_filter(undefined, Filters) ->
Filters;
add_filter(Filter, Filters) ->
[Filter | Filters].
+
+%% Convert the record #ssl_options{} into a map for internal usage
+options_to_map(Options) ->
+ Fields = record_info(fields, ssl_options),
+ [_Tag| Values] = tuple_to_list(Options),
+ L = lists:zip(Fields, Values),
+ maps:from_list(L).
diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl
index f4a91cac52..c16e2331ff 100644
--- a/lib/ssl/src/ssl_cipher.erl
+++ b/lib/ssl/src/ssl_cipher.erl
@@ -38,7 +38,7 @@
cipher_init/3, nonce_seed/2, decipher/6, cipher/5, aead_encrypt/6, aead_decrypt/6,
suites/1, all_suites/1, crypto_support_filters/0,
chacha_suites/1, anonymous_suites/1, psk_suites/1, psk_suites_anon/1,
- srp_suites/0, srp_suites_anon/0,
+ srp_suites/1, srp_suites_anon/1,
rc4_suites/1, des_suites/1, rsa_suites/1,
filter/3, filter_suites/1, filter_suites/2,
hash_algorithm/1, sign_algorithm/1, is_acceptable_hash/2, is_fallback/1,
@@ -284,7 +284,7 @@ all_suites({3, _} = Version) ->
suites(Version)
++ chacha_suites(Version)
++ psk_suites(Version)
- ++ srp_suites()
+ ++ srp_suites(Version)
++ rc4_suites(Version)
++ des_suites(Version)
++ rsa_suites(Version);
@@ -313,8 +313,8 @@ chacha_suites(_) ->
%% Description: Returns a list of the anonymous cipher suites, only supported
%% if explicitly set by user. Intended only for testing.
%%--------------------------------------------------------------------
-anonymous_suites({3, N}) ->
- srp_suites_anon() ++ anonymous_suites(N);
+anonymous_suites({3, N} = Version) ->
+ srp_suites_anon(Version) ++ anonymous_suites(N);
anonymous_suites({254, _} = Version) ->
dtls_v1:anonymous_suites(Version);
anonymous_suites(4) ->
@@ -375,7 +375,7 @@ psk_suites(_) ->
%%--------------------------------------------------------------------
psk_suites_anon({3, N}) ->
psk_suites_anon(N);
-psk_suites_anon(3) ->
+psk_suites_anon(3 = N) ->
[
?TLS_DHE_PSK_WITH_AES_256_GCM_SHA384,
?TLS_PSK_WITH_AES_256_GCM_SHA384,
@@ -401,8 +401,8 @@ psk_suites_anon(3) ->
?TLS_PSK_WITH_AES_128_CCM,
?TLS_PSK_WITH_AES_128_CCM_8,
?TLS_ECDHE_PSK_WITH_RC4_128_SHA
- ] ++ psk_suites_anon(0);
-psk_suites_anon(_) ->
+ ] ++ psk_suites_anon(N-1);
+psk_suites_anon(N) when N > 0 ->
[?TLS_DHE_PSK_WITH_AES_256_CBC_SHA,
?TLS_PSK_WITH_AES_256_CBC_SHA,
?TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA,
@@ -413,14 +413,18 @@ psk_suites_anon(_) ->
?TLS_PSK_WITH_3DES_EDE_CBC_SHA,
?TLS_ECDHE_PSK_WITH_RC4_128_SHA,
?TLS_DHE_PSK_WITH_RC4_128_SHA,
- ?TLS_PSK_WITH_RC4_128_SHA].
+ ?TLS_PSK_WITH_RC4_128_SHA];
+psk_suites_anon(0) ->
+ [].
%%--------------------------------------------------------------------
--spec srp_suites() -> [ssl_cipher_format:cipher_suite()].
+-spec srp_suites(tls_record:tls_version()) -> [ssl_cipher_format:cipher_suite()].
%%
%% Description: Returns a list of the SRP cipher suites, only supported
%% if explicitly set by user.
%%--------------------------------------------------------------------
-srp_suites() ->
+srp_suites({3,0}) ->
+ [];
+srp_suites(_) ->
[?TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA,
?TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA,
?TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA,
@@ -429,12 +433,14 @@ srp_suites() ->
?TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA].
%%--------------------------------------------------------------------
--spec srp_suites_anon() -> [ssl_cipher_format:cipher_suite()].
+-spec srp_suites_anon(tls_record:tls_version()) -> [ssl_cipher_format:cipher_suite()].
%%
%% Description: Returns a list of the SRP anonymous cipher suites, only supported
%% if explicitly set by user.
%%--------------------------------------------------------------------
-srp_suites_anon() ->
+srp_suites_anon({3,0}) ->
+ [];
+srp_suites_anon(_) ->
[?TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA,
?TLS_SRP_SHA_WITH_AES_128_CBC_SHA,
?TLS_SRP_SHA_WITH_AES_256_CBC_SHA].
diff --git a/lib/ssl/src/ssl_config.erl b/lib/ssl/src/ssl_config.erl
index 1e6dab9276..7d1e80c1cc 100644
--- a/lib/ssl/src/ssl_config.erl
+++ b/lib/ssl/src/ssl_config.erl
@@ -28,16 +28,20 @@
-export([init/2]).
-init(SslOpts, Role) ->
+init(#{erl_dist := ErlDist,
+ key := Key,
+ keyfile := KeyFile,
+ password := Password,
+ dh := DH,
+ dhfile := DHFile} = SslOpts, Role) ->
- init_manager_name(SslOpts#ssl_options.erl_dist),
+ init_manager_name(ErlDist),
{ok, #{pem_cache := PemCache} = Config}
= init_certificates(SslOpts, Role),
PrivateKey =
- init_private_key(PemCache, SslOpts#ssl_options.key, SslOpts#ssl_options.keyfile,
- SslOpts#ssl_options.password, Role),
- DHParams = init_diffie_hellman(PemCache, SslOpts#ssl_options.dh, SslOpts#ssl_options.dhfile, Role),
+ init_private_key(PemCache, Key, KeyFile, Password, Role),
+ DHParams = init_diffie_hellman(PemCache, DH, DHFile, Role),
{ok, Config#{private_key => PrivateKey, dh_params => DHParams}}.
init_manager_name(false) ->
@@ -47,12 +51,12 @@ init_manager_name(true) ->
put(ssl_manager, ssl_manager:name(dist)),
put(ssl_pem_cache, ssl_pem_cache:name(dist)).
-init_certificates(#ssl_options{cacerts = CaCerts,
- cacertfile = CACertFile,
- certfile = CertFile,
- cert = Cert,
- crl_cache = CRLCache
- }, Role) ->
+init_certificates(#{cacerts := CaCerts,
+ cacertfile := CACertFile,
+ certfile := CertFile,
+ cert := Cert,
+ crl_cache := CRLCache
+ }, Role) ->
{ok, Config} =
try
Certs = case CaCerts of
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index cc4d60389e..6789c2c23f 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -80,7 +80,7 @@
-spec connect(tls_connection | dtls_connection,
ssl:host(), inet:port_number(),
port() | {tuple(), port()}, %% TLS | DTLS
- {#ssl_options{}, #socket_options{},
+ {ssl_options(), #socket_options{},
%% Tracker only needed on server side
undefined},
pid(), tuple(), timeout()) ->
@@ -98,7 +98,7 @@ connect(Connection, Host, Port, Socket, Options, User, CbInfo, Timeout) ->
%%--------------------------------------------------------------------
-spec handshake(tls_connection | dtls_connection,
inet:port_number(), port(),
- {#ssl_options{}, #socket_options{}, undefined | pid()},
+ {ssl_options(), #socket_options{}, undefined | pid()},
pid(), tuple(), timeout()) ->
{ok, #sslsocket{}} | {error, reason()}.
%%
@@ -130,7 +130,7 @@ handshake(#sslsocket{pid = [Pid|_]} = Socket, Timeout) ->
end.
%%--------------------------------------------------------------------
--spec handshake(#sslsocket{}, {#ssl_options{},#socket_options{}}, timeout()) ->
+-spec handshake(#sslsocket{}, {ssl_options(),#socket_options{}}, timeout()) ->
{ok, #sslsocket{}} | {ok, #sslsocket{}, map()} | {error, reason()}.
%%
%% Description: Starts ssl handshake with some new options
@@ -331,7 +331,7 @@ prf(ConnectionPid, Secret, Label, Seed, WantedLength) ->
handle_own_alert(Alert0, _, StateName,
#state{static_env = #static_env{role = Role,
protocol_cb = Connection},
- ssl_options = SslOpts} = State) ->
+ ssl_options = #{log_level := LogLevel}} = State) ->
try %% Try to tell the other side
send_alert(Alert0, StateName, State)
catch _:_ -> %% Can crash if we are in a uninitialized state
@@ -339,7 +339,7 @@ handle_own_alert(Alert0, _, StateName,
end,
try %% Try to tell the local user
Alert = Alert0#alert{role = Role},
- log_alert(SslOpts#ssl_options.log_level, Role, Connection:protocol_name(), StateName, Alert),
+ log_alert(LogLevel, Role, Connection:protocol_name(), StateName, Alert),
handle_normal_shutdown(Alert,StateName, State)
catch _:_ ->
ok
@@ -376,13 +376,13 @@ handle_alert(#alert{level = ?FATAL} = Alert0, StateName,
transport_cb = Transport,
protocol_cb = Connection},
connection_env = #connection_env{user_application = {_Mon, Pid}},
- ssl_options = SslOpts,
+ ssl_options = #{log_level := LogLevel},
start_or_recv_from = From,
session = Session,
socket_options = Opts} = State) ->
invalidate_session(Role, Host, Port, Session),
Alert = Alert0#alert{role = opposite_role(Role)},
- log_alert(SslOpts#ssl_options.log_level, Role, Connection:protocol_name(),
+ log_alert(LogLevel, Role, Connection:protocol_name(),
StateName, Alert),
Pids = Connection:pids(State),
alert_user(Pids, Transport, Tracker, Socket, StateName, Opts, Pid, From, Alert, Role, StateName, Connection),
@@ -399,9 +399,9 @@ handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert0,
#state{static_env = #static_env{role = Role,
protocol_cb = Connection},
handshake_env = #handshake_env{renegotiation = {true, internal}},
- ssl_options = SslOpts} = State) ->
+ ssl_options = #{log_level := LogLevel}} = State) ->
Alert = Alert0#alert{role = opposite_role(Role)},
- log_alert(SslOpts#ssl_options.log_level, Role,
+ log_alert(LogLevel, Role,
Connection:protocol_name(), StateName, Alert),
handle_normal_shutdown(Alert, StateName, State),
{stop,{shutdown, peer_close}, State};
@@ -410,9 +410,9 @@ handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert,
#state{static_env = #static_env{role = Role,
protocol_cb = Connection},
handshake_env = #handshake_env{renegotiation = {true, From}} = HsEnv,
- ssl_options = SslOpts
+ ssl_options = #{log_level := LogLevel}
} = State0) ->
- log_alert(SslOpts#ssl_options.log_level, Role,
+ log_alert(LogLevel, Role,
Connection:protocol_name(), StateName, Alert#alert{role = opposite_role(Role)}),
gen_statem:reply(From, {error, renegotiation_rejected}),
State = Connection:reinit_handshake_data(State0),
@@ -422,9 +422,9 @@ handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert,
#state{static_env = #static_env{role = Role,
protocol_cb = Connection},
handshake_env = #handshake_env{renegotiation = {true, From}} = HsEnv,
- ssl_options = SslOpts
+ ssl_options = #{log_level := LogLevel}
} = State0) ->
- log_alert(SslOpts#ssl_options.log_level, Role,
+ log_alert(LogLevel, Role,
Connection:protocol_name(), StateName, Alert#alert{role = opposite_role(Role)}),
gen_statem:reply(From, {error, renegotiation_rejected}),
%% Go back to connection!
@@ -435,8 +435,8 @@ handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert,
handle_alert(#alert{level = ?WARNING} = Alert, StateName,
#state{static_env = #static_env{role = Role,
protocol_cb = Connection},
- ssl_options = SslOpts} = State) ->
- log_alert(SslOpts#ssl_options.log_level, Role,
+ ssl_options = #{log_level := LogLevel}} = State) ->
+ log_alert(LogLevel, Role,
Connection:protocol_name(), StateName,
Alert#alert{role = opposite_role(Role)}),
Connection:next_event(StateName, no_record, State).
@@ -614,7 +614,8 @@ read_application_dist_data(DHandle, Front0, BufferSize, Rear0, Bin0) ->
<<SizeA:32, DataA:SizeA/binary,
SizeB:32, DataB:SizeB/binary,
SizeC:32, DataC:SizeC/binary,
- SizeD:32, DataD:SizeD/binary, Rest/binary>> ->
+ SizeD:32, DataD:SizeD/binary, Rest/binary>>
+ when 0 < SizeA, 0 < SizeB, 0 < SizeC, 0 < SizeD ->
%% We have 4 complete packets in the first binary
erlang:dist_ctrl_put_data(DHandle, DataA),
erlang:dist_ctrl_put_data(DHandle, DataB),
@@ -624,7 +625,8 @@ read_application_dist_data(DHandle, Front0, BufferSize, Rear0, Bin0) ->
DHandle, Front0, BufferSize - (4*4+SizeA+SizeB+SizeC+SizeD), Rear0, Rest);
<<SizeA:32, DataA:SizeA/binary,
SizeB:32, DataB:SizeB/binary,
- SizeC:32, DataC:SizeC/binary, Rest/binary>> ->
+ SizeC:32, DataC:SizeC/binary, Rest/binary>>
+ when 0 < SizeA, 0 < SizeB, 0 < SizeC ->
%% We have 3 complete packets in the first binary
erlang:dist_ctrl_put_data(DHandle, DataA),
erlang:dist_ctrl_put_data(DHandle, DataB),
@@ -632,7 +634,8 @@ read_application_dist_data(DHandle, Front0, BufferSize, Rear0, Bin0) ->
read_application_dist_data(
DHandle, Front0, BufferSize - (3*4+SizeA+SizeB+SizeC), Rear0, Rest);
<<SizeA:32, DataA:SizeA/binary,
- SizeB:32, DataB:SizeB/binary, Rest/binary>> ->
+ SizeB:32, DataB:SizeB/binary, Rest/binary>>
+ when 0 < SizeA, 0 < SizeB ->
%% We have 2 complete packets in the first binary
erlang:dist_ctrl_put_data(DHandle, DataA),
erlang:dist_ctrl_put_data(DHandle, DataB),
@@ -643,13 +646,13 @@ read_application_dist_data(DHandle, Front0, BufferSize, Rear0, Bin0) ->
%% Basic one packet code path
<<Size:32, Data:Size/binary, Rest/binary>> ->
%% We have a complete packet in the first binary
- erlang:dist_ctrl_put_data(DHandle, Data),
+ 0 < Size andalso erlang:dist_ctrl_put_data(DHandle, Data),
read_application_dist_data(DHandle, Front0, BufferSize - (4+Size), Rear0, Rest);
<<Size:32, FirstData/binary>> when 4+Size =< BufferSize ->
%% We have a complete packet in the buffer
%% - fetch the missing content from the buffer front
{Data,Front,Rear} = iovec_from_front(Size - byte_size(FirstData), Front0, Rear0, [FirstData]),
- erlang:dist_ctrl_put_data(DHandle, Data),
+ 0 < Size andalso erlang:dist_ctrl_put_data(DHandle, Data),
read_application_dist_data(DHandle, Front, BufferSize - (4+Size), Rear);
<<Bin/binary>> ->
%% In OTP-21 the match context reuse optimization fails if we use Bin0 in recursion, so here we
@@ -665,23 +668,61 @@ read_application_dist_data(DHandle, Front0, BufferSize, Rear0, Bin0) ->
%% contains enough data to maybe form a packet
%% - fetch a tiny binary from the buffer front to complete the length field
{LengthField,Front,Rear} =
- iovec_from_front(4 - byte_size(IncompleteLengthField), Front0, Rear0, [IncompleteLengthField]),
+ case IncompleteLengthField of
+ <<>> ->
+ iovec_from_front(4, Front0, Rear0, []);
+ _ ->
+ iovec_from_front(
+ 4 - byte_size(IncompleteLengthField), Front0, Rear0, [IncompleteLengthField])
+ end,
LengthBin = iolist_to_binary(LengthField),
read_application_dist_data(DHandle, Front, BufferSize, Rear, LengthBin);
<<IncompleteLengthField/binary>> ->
%% We do not have enough data in the buffer to even form a length field - await more data
- {[IncompleteLengthField|Front0],BufferSize,Rear0}
+ case IncompleteLengthField of
+ <<>> ->
+ {Front0,BufferSize,Rear0};
+ _ ->
+ {[IncompleteLengthField|Front0],BufferSize,Rear0}
+ end
end
end.
+iovec_from_front(0, Front, Rear, Acc) ->
+ {lists:reverse(Acc),Front,Rear};
iovec_from_front(Size, [], Rear, Acc) ->
- iovec_from_front(Size, lists:reverse(Rear), [], Acc);
+ case Rear of
+ %% Avoid lists:reverse/1 for simple cases.
+ %% Case clause for [] to avoid infinite loop.
+ [_] ->
+ iovec_from_front(Size, Rear, [], Acc);
+ [Bin2,Bin1] ->
+ iovec_from_front(Size, [Bin1,Bin2], [], Acc);
+ [Bin3,Bin2,Bin1] ->
+ iovec_from_front(Size, [Bin1,Bin2,Bin3], [], Acc);
+ [_,_,_|_] = Rear ->
+ iovec_from_front(Size, lists:reverse(Rear), [], Acc)
+ end;
+iovec_from_front(Size, [Bin|Front], Rear, []) ->
+ case Bin of
+ <<Last:Size/binary>> -> % Just enough
+ {[Last],Front,Rear};
+ <<Last:Size/binary, Rest/binary>> -> % More than enough, split here
+ {[Last],[Rest|Front],Rear};
+ <<>> -> % Not enough, skip empty binaries
+ iovec_from_front(Size, Front, Rear, []);
+ <<_/binary>> -> % Not enough
+ BinSize = byte_size(Bin),
+ iovec_from_front(Size - BinSize, Front, Rear, [Bin])
+ end;
iovec_from_front(Size, [Bin|Front], Rear, Acc) ->
case Bin of
<<Last:Size/binary>> -> % Just enough
{lists:reverse(Acc, [Last]),Front,Rear};
<<Last:Size/binary, Rest/binary>> -> % More than enough, split here
{lists:reverse(Acc, [Last]),[Rest|Front],Rear};
+ <<>> -> % Not enough, skip empty binaries
+ iovec_from_front(Size, Front, Rear, Acc);
<<_/binary>> -> % Not enough
BinSize = byte_size(Bin),
iovec_from_front(Size - BinSize, Front, Rear, [Bin|Acc])
@@ -732,7 +773,7 @@ handle_session(#server_hello{cipher_suite = CipherSuite,
end.
%%--------------------------------------------------------------------
--spec ssl_config(#ssl_options{}, client | server, #state{}) -> #state{}.
+-spec ssl_config(ssl_options(), client | server, #state{}) -> #state{}.
%%--------------------------------------------------------------------
ssl_config(Opts, Role, #state{static_env = InitStatEnv0,
handshake_env = HsEnv,
@@ -829,7 +870,7 @@ user_hello({call, From}, {handshake_continue, NewOptions, Timeout},
#state{static_env = #static_env{role = Role},
handshake_env = #handshake_env{hello = Hello},
ssl_options = Options0} = State0, _Connection) ->
- Options = ssl:handle_options(NewOptions, Options0#ssl_options{handshake = full}),
+ Options = ssl:handle_options(NewOptions, Options0#{handshake => full}),
State = ssl_config(Options, Role, State0),
{next_state, hello, State#state{start_or_recv_from = From},
[{next_event, internal, Hello}, {{timeout, handshake}, Timeout, close}]};
@@ -921,21 +962,21 @@ certify(info, Msg, State, _) ->
certify(internal, #certificate{asn1_certificates = []},
#state{static_env = #static_env{role = server},
connection_env = #connection_env{negotiated_version = Version},
- ssl_options = #ssl_options{verify = verify_peer,
- fail_if_no_peer_cert = true}} =
+ ssl_options = #{verify := verify_peer,
+ fail_if_no_peer_cert := true}} =
State, _) ->
Alert = ?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE),
handle_own_alert(Alert, Version, ?FUNCTION_NAME, State);
certify(internal, #certificate{asn1_certificates = []},
#state{static_env = #static_env{role = server},
- ssl_options = #ssl_options{verify = verify_peer,
- fail_if_no_peer_cert = false}} =
+ ssl_options = #{verify := verify_peer,
+ fail_if_no_peer_cert := false}} =
State0, Connection) ->
Connection:next_event(?FUNCTION_NAME, no_record, State0#state{client_certificate_requested = false});
certify(internal, #certificate{},
#state{static_env = #static_env{role = server},
connection_env = #connection_env{negotiated_version = Version},
- ssl_options = #ssl_options{verify = verify_none}} =
+ ssl_options = #{verify := verify_none}} =
State, _) ->
Alert = ?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE, unrequested_certificate),
handle_own_alert(Alert, Version, ?FUNCTION_NAME, State);
@@ -1026,7 +1067,7 @@ certify(internal, #certificate_request{} = CertRequest,
handshake_env = HsEnv,
connection_env = #connection_env{negotiated_version = Version},
session = #session{own_certificate = Cert},
- ssl_options = #ssl_options{signature_algs = SupportedHashSigns}} = State, Connection) ->
+ ssl_options = #{signature_algs := SupportedHashSigns}} = State, Connection) ->
case ssl_handshake:select_hashsign(CertRequest, Cert,
SupportedHashSigns, ssl:tls_version(Version)) of
#alert {} = Alert ->
@@ -1044,7 +1085,7 @@ certify(internal, #server_hello_done{},
handshake_env = #handshake_env{kex_algorithm = KexAlg,
premaster_secret = undefined,
server_psk_identity = PSKIdentity} = HsEnv,
- ssl_options = #ssl_options{user_lookup_fun = PSKLookup}} = State0, Connection)
+ ssl_options = #{user_lookup_fun := PSKLookup}} = State0, Connection)
when KexAlg == psk ->
case ssl_handshake:premaster_secret({KexAlg, PSKIdentity}, PSKLookup) of
#alert{} = Alert ->
@@ -1062,7 +1103,7 @@ certify(internal, #server_hello_done{},
premaster_secret = undefined,
server_psk_identity = PSKIdentity} = HsEnv,
session = #session{master_secret = undefined},
- ssl_options = #ssl_options{user_lookup_fun = PSKLookup}} = State0, Connection)
+ ssl_options = #{user_lookup_fun := PSKLookup}} = State0, Connection)
when KexAlg == rsa_psk ->
Rand = ssl_cipher:random_bytes(?NUM_OF_PREMASTERSECRET_BYTES-2),
RSAPremasterSecret = <<?BYTE(Major), ?BYTE(Minor), Rand/binary>>,
@@ -1111,7 +1152,7 @@ certify(internal, #server_hello_done{},
certify(internal = Type, #client_key_exchange{} = Msg,
#state{static_env = #static_env{role = server},
client_certificate_requested = true,
- ssl_options = #ssl_options{fail_if_no_peer_cert = true}} = State,
+ ssl_options = #{fail_if_no_peer_cert := true}} = State,
Connection) ->
%% We expect a certificate here
handle_common_event(Type, Msg, ?FUNCTION_NAME, State, Connection);
@@ -1234,10 +1275,17 @@ connection({call, From}, {connection_information, false}, State, _) ->
Info = connection_info(State),
hibernate_after(?FUNCTION_NAME, State, [{reply, From, {ok, Info}}]);
connection({call, From}, negotiated_protocol,
- #state{handshake_env = #handshake_env{negotiated_protocol = undefined}} = State, _) ->
+ #state{handshake_env = #handshake_env{alpn = undefined,
+ negotiated_protocol = undefined}} = State, _) ->
hibernate_after(?FUNCTION_NAME, State, [{reply, From, {error, protocol_not_negotiated}}]);
connection({call, From}, negotiated_protocol,
- #state{handshake_env = #handshake_env{negotiated_protocol = SelectedProtocol}} = State, _) ->
+ #state{handshake_env = #handshake_env{alpn = undefined,
+ negotiated_protocol = SelectedProtocol}} = State, _) ->
+ hibernate_after(?FUNCTION_NAME, State,
+ [{reply, From, {ok, SelectedProtocol}}]);
+connection({call, From}, negotiated_protocol,
+ #state{handshake_env = #handshake_env{alpn = SelectedProtocol,
+ negotiated_protocol = undefined}} = State, _) ->
hibernate_after(?FUNCTION_NAME, State,
[{reply, From, {ok, SelectedProtocol}}]);
connection({call, From}, Msg, State, Connection) ->
@@ -1249,7 +1297,7 @@ connection(cast, {internal_renegotiate, WriteState}, #state{static_env = #static
Connection:renegotiate(State#state{handshake_env = HsEnv#handshake_env{renegotiation = {true, internal}},
connection_states = ConnectionStates#{current_write => WriteState}}, []);
connection(cast, {dist_handshake_complete, DHandle},
- #state{ssl_options = #ssl_options{erl_dist = true},
+ #state{ssl_options = #{erl_dist := true},
connection_env = CEnv,
socket_options = SockOpts} = State0, Connection) ->
process_flag(priority, normal),
@@ -1458,7 +1506,7 @@ handle_info({ErrorTag, Socket, Reason}, StateName, #state{static_env = #static_e
handle_info({'DOWN', MonitorRef, _, _, Reason}, _,
#state{connection_env = #connection_env{user_application = {MonitorRef, _Pid}},
- ssl_options = #ssl_options{erl_dist = true}}) ->
+ ssl_options = #{erl_dist := true}}) ->
{stop, {shutdown, Reason}};
handle_info({'DOWN', MonitorRef, _, _, _}, _,
#state{connection_env = #connection_env{user_application = {MonitorRef, _Pid}}}) ->
@@ -1524,7 +1572,7 @@ terminate(Reason, connection, #state{static_env = #static_env{
transport_cb = Transport,
socket = Socket},
connection_states = ConnectionStates,
- ssl_options = #ssl_options{padding_check = Check}
+ ssl_options = #{padding_check := Check}
} = State) ->
handle_trusted_certs_db(State),
Alert = terminate_alert(Reason),
@@ -1542,13 +1590,13 @@ format_status(normal, [_, StateName, State]) ->
[{data, [{"State", {StateName, State}}]}];
format_status(terminate, [_, StateName, State]) ->
SslOptions = (State#state.ssl_options),
- NewOptions = SslOptions#ssl_options{password = ?SECRET_PRINTOUT,
- cert = ?SECRET_PRINTOUT,
- cacerts = ?SECRET_PRINTOUT,
- key = ?SECRET_PRINTOUT,
- dh = ?SECRET_PRINTOUT,
- psk_identity = ?SECRET_PRINTOUT,
- srp_identity = ?SECRET_PRINTOUT},
+ NewOptions = SslOptions#{password => ?SECRET_PRINTOUT,
+ cert => ?SECRET_PRINTOUT,
+ cacerts => ?SECRET_PRINTOUT,
+ key => ?SECRET_PRINTOUT,
+ dh => ?SECRET_PRINTOUT,
+ psk_identity => ?SECRET_PRINTOUT,
+ srp_identity => ?SECRET_PRINTOUT},
[{data, [{"State", {StateName, State#state{connection_states = ?SECRET_PRINTOUT,
protocol_buffers = ?SECRET_PRINTOUT,
user_data_buffer = ?SECRET_PRINTOUT,
@@ -1603,7 +1651,7 @@ do_server_hello(Type, #{next_protocol_negotiation := NextProtocols} =
handshake_env = HsEnv,
session = #session{session_id = SessId},
connection_states = ConnectionStates0,
- ssl_options = #ssl_options{versions = [HighestVersion|_]}}
+ ssl_options = #{versions := [HighestVersion|_]}}
= State0, Connection) when is_atom(Type) ->
%% TLS 1.3 - Section 4.1.3
%% Override server random values for TLS 1.3 downgrade protection mechanism.
@@ -1837,7 +1885,7 @@ certify_client_key_exchange(#client_ec_diffie_hellman_public{dh_public = ClientP
calculate_master_secret(PremasterSecret, State, Connection, certify, cipher);
certify_client_key_exchange(#client_psk_identity{} = ClientKey,
#state{ssl_options =
- #ssl_options{user_lookup_fun = PSKLookup}} = State0,
+ #{user_lookup_fun := PSKLookup}} = State0,
Connection) ->
PremasterSecret = ssl_handshake:premaster_secret(ClientKey, PSKLookup),
calculate_master_secret(PremasterSecret, State0, Connection, certify, cipher);
@@ -1845,7 +1893,7 @@ certify_client_key_exchange(#client_dhe_psk_identity{} = ClientKey,
#state{handshake_env = #handshake_env{diffie_hellman_params = #'DHParameter'{} = Params,
kex_keys = {_, ServerDhPrivateKey}},
ssl_options =
- #ssl_options{user_lookup_fun = PSKLookup}} = State0,
+ #{user_lookup_fun := PSKLookup}} = State0,
Connection) ->
PremasterSecret =
ssl_handshake:premaster_secret(ClientKey, ServerDhPrivateKey, Params, PSKLookup),
@@ -1853,7 +1901,7 @@ certify_client_key_exchange(#client_dhe_psk_identity{} = ClientKey,
certify_client_key_exchange(#client_ecdhe_psk_identity{} = ClientKey,
#state{handshake_env = #handshake_env{kex_keys = ServerEcDhPrivateKey},
ssl_options =
- #ssl_options{user_lookup_fun = PSKLookup}} = State,
+ #{user_lookup_fun := PSKLookup}} = State,
Connection) ->
PremasterSecret =
ssl_handshake:premaster_secret(ClientKey, ServerEcDhPrivateKey, PSKLookup),
@@ -1861,7 +1909,7 @@ certify_client_key_exchange(#client_ecdhe_psk_identity{} = ClientKey,
certify_client_key_exchange(#client_rsa_psk_identity{} = ClientKey,
#state{connection_env = #connection_env{private_key = Key},
ssl_options =
- #ssl_options{user_lookup_fun = PSKLookup}} = State0,
+ #{user_lookup_fun := PSKLookup}} = State0,
Connection) ->
PremasterSecret = ssl_handshake:premaster_secret(ClientKey, Key, PSKLookup),
calculate_master_secret(PremasterSecret, State0, Connection, certify, cipher);
@@ -1947,10 +1995,10 @@ key_exchange(#state{static_env = #static_env{role = server},
State#state{handshake_env = HsEnv#handshake_env{kex_keys = ECDHKeys}};
key_exchange(#state{static_env = #static_env{role = server},
handshake_env = #handshake_env{kex_algorithm = psk},
- ssl_options = #ssl_options{psk_identity = undefined}} = State, _) ->
+ ssl_options = #{psk_identity := undefined}} = State, _) ->
State;
key_exchange(#state{static_env = #static_env{role = server},
- ssl_options = #ssl_options{psk_identity = PskIdentityHint},
+ ssl_options = #{psk_identity := PskIdentityHint},
handshake_env = #handshake_env{kex_algorithm = psk,
hashsign_algorithm = HashSignAlgo},
connection_env = #connection_env{negotiated_version = Version,
@@ -1967,7 +2015,7 @@ key_exchange(#state{static_env = #static_env{role = server},
PrivateKey}),
Connection:queue_handshake(Msg, State0);
key_exchange(#state{static_env = #static_env{role = server},
- ssl_options = #ssl_options{psk_identity = PskIdentityHint},
+ ssl_options = #{psk_identity := PskIdentityHint},
handshake_env = #handshake_env{kex_algorithm = dhe_psk,
diffie_hellman_params = #'DHParameter'{} = Params,
hashsign_algorithm = HashSignAlgo},
@@ -1989,7 +2037,7 @@ key_exchange(#state{static_env = #static_env{role = server},
#state{handshake_env = HsEnv} = State = Connection:queue_handshake(Msg, State0),
State#state{handshake_env = HsEnv#handshake_env{kex_keys = DHKeys}};
key_exchange(#state{static_env = #static_env{role = server},
- ssl_options = #ssl_options{psk_identity = PskIdentityHint},
+ ssl_options = #{psk_identity := PskIdentityHint},
handshake_env = #handshake_env{kex_algorithm = ecdhe_psk,
hashsign_algorithm = HashSignAlgo},
connection_env = #connection_env{negotiated_version = Version,
@@ -2012,10 +2060,10 @@ key_exchange(#state{static_env = #static_env{role = server},
State#state{handshake_env = HsEnv#handshake_env{kex_keys = ECDHKeys}};
key_exchange(#state{static_env = #static_env{role = server},
handshake_env = #handshake_env{kex_algorithm = rsa_psk},
- ssl_options = #ssl_options{psk_identity = undefined}} = State, _) ->
+ ssl_options = #{psk_identity := undefined}} = State, _) ->
State;
key_exchange(#state{static_env = #static_env{role = server},
- ssl_options = #ssl_options{psk_identity = PskIdentityHint},
+ ssl_options = #{psk_identity := PskIdentityHint},
handshake_env = #handshake_env{kex_algorithm = rsa_psk,
hashsign_algorithm = HashSignAlgo},
connection_env = #connection_env{negotiated_version = Version,
@@ -2033,7 +2081,7 @@ key_exchange(#state{static_env = #static_env{role = server},
PrivateKey}),
Connection:queue_handshake(Msg, State0);
key_exchange(#state{static_env = #static_env{role = server},
- ssl_options = #ssl_options{user_lookup_fun = LookupFun},
+ ssl_options = #{user_lookup_fun := LookupFun},
handshake_env = #handshake_env{kex_algorithm = KexAlg,
hashsign_algorithm = HashSignAlgo},
connection_env = #connection_env{negotiated_version = Version,
@@ -2098,28 +2146,28 @@ key_exchange(#state{static_env = #static_env{role = client},
key_exchange(#state{static_env = #static_env{role = client},
handshake_env = #handshake_env{kex_algorithm = psk},
connection_env = #connection_env{negotiated_version = Version},
- ssl_options = SslOpts} = State0, Connection) ->
+ ssl_options = #{psk_identity := PSKIdentity}} = State0, Connection) ->
Msg = ssl_handshake:key_exchange(client, ssl:tls_version(Version),
- {psk, SslOpts#ssl_options.psk_identity}),
+ {psk, PSKIdentity}),
Connection:queue_handshake(Msg, State0);
key_exchange(#state{static_env = #static_env{role = client},
handshake_env = #handshake_env{kex_algorithm = dhe_psk,
kex_keys = {DhPubKey, _}},
connection_env = #connection_env{negotiated_version = Version},
- ssl_options = SslOpts} = State0, Connection) ->
+ ssl_options = #{psk_identity := PSKIdentity}} = State0, Connection) ->
Msg = ssl_handshake:key_exchange(client, ssl:tls_version(Version),
{dhe_psk,
- SslOpts#ssl_options.psk_identity, DhPubKey}),
+ PSKIdentity, DhPubKey}),
Connection:queue_handshake(Msg, State0);
key_exchange(#state{static_env = #static_env{role = client},
handshake_env = #handshake_env{kex_algorithm = ecdhe_psk,
kex_keys = ECDHKeys},
connection_env = #connection_env{negotiated_version = Version},
- ssl_options = SslOpts} = State0, Connection) ->
+ ssl_options = #{psk_identity := PSKIdentity}} = State0, Connection) ->
Msg = ssl_handshake:key_exchange(client, ssl:tls_version(Version),
{ecdhe_psk,
- SslOpts#ssl_options.psk_identity, ECDHKeys}),
+ PSKIdentity, ECDHKeys}),
Connection:queue_handshake(Msg, State0);
key_exchange(#state{static_env = #static_env{role = client},
@@ -2127,9 +2175,9 @@ key_exchange(#state{static_env = #static_env{role = client},
public_key_info = PublicKeyInfo,
premaster_secret = PremasterSecret},
connection_env = #connection_env{negotiated_version = Version},
- ssl_options = SslOpts}
+ ssl_options = #{psk_identity := PSKIdentity}}
= State0, Connection) ->
- Msg = rsa_psk_key_exchange(ssl:tls_version(Version), SslOpts#ssl_options.psk_identity,
+ Msg = rsa_psk_key_exchange(ssl:tls_version(Version), PSKIdentity,
PremasterSecret, PublicKeyInfo),
Connection:queue_handshake(Msg, State0);
key_exchange(#state{static_env = #static_env{role = client},
@@ -2191,8 +2239,8 @@ request_client_cert(#state{handshake_env = #handshake_env{kex_algorithm = Alg}}
request_client_cert(#state{static_env = #static_env{cert_db = CertDbHandle,
cert_db_ref = CertDbRef},
connection_env = #connection_env{negotiated_version = Version},
- ssl_options = #ssl_options{verify = verify_peer,
- signature_algs = SupportedHashSigns},
+ ssl_options = #{verify := verify_peer,
+ signature_algs := SupportedHashSigns},
connection_states = ConnectionStates0} = State0, Connection) ->
#{security_parameters :=
#security_parameters{cipher_suite = CipherSuite}} =
@@ -2205,7 +2253,7 @@ request_client_cert(#state{static_env = #static_env{cert_db = CertDbHandle,
State = Connection:queue_handshake(Msg, State0),
State#state{client_certificate_requested = true};
-request_client_cert(#state{ssl_options = #ssl_options{verify = verify_none}} =
+request_client_cert(#state{ssl_options = #{verify := verify_none}} =
State, _) ->
State.
@@ -2305,7 +2353,7 @@ calculate_secret(#server_psk_params{
calculate_secret(#server_dhe_psk_params{
dh_params = #server_dh_params{dh_p = Prime, dh_g = Base}} = ServerKey,
#state{handshake_env = HsEnv,
- ssl_options = #ssl_options{user_lookup_fun = PSKLookup}} =
+ ssl_options = #{user_lookup_fun := PSKLookup}} =
State, Connection) ->
Keys = {_, PrivateDhKey} =
crypto:generate_key(dh, [Prime, Base]),
@@ -2315,7 +2363,7 @@ calculate_secret(#server_dhe_psk_params{
calculate_secret(#server_ecdhe_psk_params{
dh_params = #server_ecdh_params{curve = ECCurve}} = ServerKey,
- #state{ssl_options = #ssl_options{user_lookup_fun = PSKLookup}} =
+ #state{ssl_options = #{user_lookup_fun := PSKLookup}} =
#state{handshake_env = HsEnv,
session = Session} = State, Connection) ->
ECDHKeys = public_key:generate_key(ECCurve),
@@ -2328,7 +2376,7 @@ calculate_secret(#server_ecdhe_psk_params{
calculate_secret(#server_srp_params{srp_n = Prime, srp_g = Generator} = ServerKey,
#state{handshake_env = HsEnv,
- ssl_options = #ssl_options{srp_identity = SRPId}} = State,
+ ssl_options = #{srp_identity := SRPId}} = State,
Connection) ->
Keys = generate_srp_client_keys(Generator, Prime, 0),
PremasterSecret = ssl_handshake:premaster_secret(ServerKey, Keys, SRPId),
@@ -2562,7 +2610,7 @@ set_socket_opts(ConnectionCb, Transport, Socket, [Opt | Opts], SockOpts, Other)
hibernate_after(connection = StateName,
- #state{ssl_options=#ssl_options{hibernate_after = HibernateAfter}} = State,
+ #state{ssl_options= #{hibernate_after := HibernateAfter}} = State,
Actions) ->
{next_state, StateName, State, [{timeout, HibernateAfter, hibernate} | Actions]};
hibernate_after(StateName, State, Actions) ->
@@ -2578,12 +2626,12 @@ terminate_alert(_) ->
?ALERT_REC(?FATAL, ?INTERNAL_ERROR).
handle_trusted_certs_db(#state{ssl_options =
- #ssl_options{cacertfile = <<>>, cacerts = []}}) ->
+ #{cacertfile := <<>>, cacerts := []}}) ->
%% No trusted certs specified
ok;
handle_trusted_certs_db(#state{static_env = #static_env{cert_db_ref = Ref,
cert_db = CertDb},
- ssl_options = #ssl_options{cacertfile = <<>>}}) when CertDb =/= undefined ->
+ ssl_options = #{cacertfile := <<>>}}) when CertDb =/= undefined ->
%% Certs provided as DER directly can not be shared
%% with other connections and it is safe to delete them when the connection ends.
ssl_pkix_db:remove_trusted_certs(Ref, CertDb);
@@ -2593,7 +2641,7 @@ handle_trusted_certs_db(#state{static_env = #static_env{file_ref_db = undefined}
ok;
handle_trusted_certs_db(#state{static_env = #static_env{cert_db_ref = Ref,
file_ref_db = RefDb},
- ssl_options = #ssl_options{cacertfile = File}}) ->
+ ssl_options = #{cacertfile := File}}) ->
case ssl_pkix_db:ref_count(Ref, RefDb, -1) of
0 ->
ssl_manager:clean_cert_db(Ref, File);
@@ -2630,11 +2678,11 @@ session_handle_params(#server_ecdh_params{curve = ECCurve}, Session) ->
session_handle_params(_, Session) ->
Session.
-handle_session(Role = server, #ssl_options{reuse_sessions = true} = SslOpts,
+handle_session(Role = server, #{reuse_sessions := true} = SslOpts,
Host, Port, Session0) ->
register_session(Role, host_id(Role, Host, SslOpts), Port, Session0, true);
-handle_session(Role = client, #ssl_options{verify = verify_peer,
- reuse_sessions = Reuse} = SslOpts,
+handle_session(Role = client, #{verify := verify_peer,
+ reuse_sessions := Reuse} = SslOpts,
Host, Port, Session0) when Reuse =/= false ->
register_session(Role, host_id(Role, Host, SslOpts), Port, Session0, reg_type(Reuse));
handle_session(server, _, Host, Port, Session) ->
@@ -2661,7 +2709,7 @@ register_session(server, _, Port, #session{is_resumable = new} = Session0, _) ->
register_session(_, _, _, Session, _) ->
Session. %% Already registered
-host_id(client, _Host, #ssl_options{server_name_indication = Hostname}) when is_list(Hostname) ->
+host_id(client, _Host, #{server_name_indication := Hostname}) when is_list(Hostname) ->
Hostname;
host_id(_, Host, _) ->
Host.
@@ -2712,28 +2760,27 @@ negotiated_hashsign(HashSign = {_, _}, _, _, _) ->
HashSign.
ssl_options_list(SslOptions) ->
- Fileds = record_info(fields, ssl_options),
- Values = tl(tuple_to_list(SslOptions)),
- ssl_options_list(Fileds, Values, []).
+ L = maps:to_list(SslOptions),
+ ssl_options_list(L, []).
-ssl_options_list([],[], Acc) ->
+ssl_options_list([], Acc) ->
lists:reverse(Acc);
%% Skip internal options, only return user options
-ssl_options_list([protocol | Keys], [_ | Values], Acc) ->
- ssl_options_list(Keys, Values, Acc);
-ssl_options_list([erl_dist | Keys], [_ | Values], Acc) ->
- ssl_options_list(Keys, Values, Acc);
-ssl_options_list([renegotiate_at | Keys], [_ | Values], Acc) ->
- ssl_options_list(Keys, Values, Acc);
-ssl_options_list([ciphers = Key | Keys], [Value | Values], Acc) ->
- ssl_options_list(Keys, Values,
+ssl_options_list([{protocol, _}| T], Acc) ->
+ ssl_options_list(T, Acc);
+ssl_options_list([{erl_dist, _}|T], Acc) ->
+ ssl_options_list(T, Acc);
+ssl_options_list([{renegotiate_at, _}|T], Acc) ->
+ ssl_options_list(T, Acc);
+ssl_options_list([{ciphers = Key, Value}|T], Acc) ->
+ ssl_options_list(T,
[{Key, lists:map(
fun(Suite) ->
ssl_cipher_format:suite_bin_to_map(Suite)
end, Value)}
| Acc]);
-ssl_options_list([Key | Keys], [Value | Values], Acc) ->
- ssl_options_list(Keys, Values, [{Key, Value} | Acc]).
+ssl_options_list([{Key, Value}|T], Acc) ->
+ ssl_options_list(T, [{Key, Value} | Acc]).
handle_active_option(false, connection = StateName, To, Reply, State) ->
hibernate_after(StateName, State, [{reply, To, Reply}]);
@@ -2981,12 +3028,13 @@ handle_sni_extension(#sni{hostname = Hostname}, #state{static_env = #static_env{
}
end.
-update_ssl_options_from_sni(OrigSSLOptions, SNIHostname) ->
+update_ssl_options_from_sni(#{sni_fun := SNIFun,
+ sni_hosts := SNIHosts} = OrigSSLOptions, SNIHostname) ->
SSLOption =
- case OrigSSLOptions#ssl_options.sni_fun of
+ case SNIFun of
undefined ->
proplists:get_value(SNIHostname,
- OrigSSLOptions#ssl_options.sni_hosts);
+ SNIHosts);
SNIFun ->
SNIFun(SNIHostname)
end,
diff --git a/lib/ssl/src/ssl_connection.hrl b/lib/ssl/src/ssl_connection.hrl
index 844368c761..be94fd05bb 100644
--- a/lib/ssl/src/ssl_connection.hrl
+++ b/lib/ssl/src/ssl_connection.hrl
@@ -92,7 +92,7 @@
-record(state, {
static_env :: #static_env{},
connection_env :: #connection_env{} | secret_printout(),
- ssl_options :: #ssl_options{},
+ ssl_options :: ssl_options(),
socket_options :: #socket_options{},
%% Hanshake %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index c6698bc74a..a407694617 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -58,7 +58,7 @@
]).
%% Encode
--export([encode_handshake/2, encode_hello_extensions/1, encode_extensions/1, encode_extensions/2,
+-export([encode_handshake/2, encode_hello_extensions/2, encode_extensions/1, encode_extensions/2,
encode_client_protocol_negotiation/2, encode_protocols_advertised_on_server/1]).
%% Decode
-export([decode_handshake/3, decode_vector/1, decode_hello_extensions/4, decode_extensions/3,
@@ -336,25 +336,30 @@ next_protocol(SelectedProtocol) ->
%% Handle handshake messages
%%====================================================================
%%--------------------------------------------------------------------
--spec certify(#certificate{}, db_handle(), certdb_ref(), #ssl_options{}, term(),
+-spec certify(#certificate{}, db_handle(), certdb_ref(), ssl_options(), term(),
client | server, inet:hostname() | inet:ip_address()) -> {der_cert(), public_key_info()} | #alert{}.
%%
%% Description: Handles a certificate handshake message
%%--------------------------------------------------------------------
certify(#certificate{asn1_certificates = ASN1Certs}, CertDbHandle, CertDbRef,
- Opts, CRLDbHandle, Role, Host) ->
-
- ServerName = server_name(Opts#ssl_options.server_name_indication, Host, Role),
+ #{server_name_indication := ServerNameIndication,
+ partial_chain := PartialChain,
+ verify_fun := VerifyFun,
+ customize_hostname_check := CustomizeHostnameCheck,
+ crl_check := CrlCheck,
+ depth := Depth} = Opts, CRLDbHandle, Role, Host) ->
+
+ ServerName = server_name(ServerNameIndication, Host, Role),
[PeerCert | ChainCerts ] = ASN1Certs,
try
{TrustedCert, CertPath} =
ssl_certificate:trusted_cert_and_path(ASN1Certs, CertDbHandle, CertDbRef,
- Opts#ssl_options.partial_chain),
- ValidationFunAndState = validation_fun_and_state(Opts#ssl_options.verify_fun, Role,
+ PartialChain),
+ ValidationFunAndState = validation_fun_and_state(VerifyFun, Role,
CertDbHandle, CertDbRef, ServerName,
- Opts#ssl_options.customize_hostname_check,
- Opts#ssl_options.crl_check, CRLDbHandle, CertPath),
- Options = [{max_path_length, Opts#ssl_options.depth},
+ CustomizeHostnameCheck,
+ CrlCheck, CRLDbHandle, CertPath),
+ Options = [{max_path_length, Depth},
{verify_fun, ValidationFunAndState}],
case public_key:pkix_path_validation(TrustedCert, CertPath, Options) of
{ok, {PublicKeyInfo,_}} ->
@@ -534,14 +539,14 @@ encode_handshake(#next_protocol{selected_protocol = SelectedProtocol}, _Version)
PaddingLength = 32 - ((byte_size(SelectedProtocol) + 2) rem 32),
{?NEXT_PROTOCOL, <<?BYTE((byte_size(SelectedProtocol))), SelectedProtocol/binary,
?BYTE(PaddingLength), 0:(PaddingLength * 8)>>};
-encode_handshake(#server_hello{server_version = {Major, Minor},
+encode_handshake(#server_hello{server_version = {Major, Minor} = Version,
random = Random,
session_id = Session_ID,
cipher_suite = CipherSuite,
compression_method = Comp_method,
extensions = Extensions}, _Version) ->
SID_length = byte_size(Session_ID),
- ExtensionsBin = encode_hello_extensions(Extensions),
+ ExtensionsBin = encode_hello_extensions(Extensions, Version),
{?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
?BYTE(SID_length), Session_ID/binary,
CipherSuite/binary, ?BYTE(Comp_method), ExtensionsBin/binary>>};
@@ -589,7 +594,9 @@ encode_handshake(#certificate_verify{signature = BinSig, hashsign_algorithm = Ha
encode_handshake(#finished{verify_data = VerifyData}, _Version) ->
{?FINISHED, VerifyData}.
-encode_hello_extensions(Extensions) ->
+encode_hello_extensions(_, {3, 0}) ->
+ <<>>;
+encode_hello_extensions(Extensions, _) ->
encode_extensions(hello_extensions_list(Extensions), <<>>).
encode_extensions(Exts) ->
@@ -707,7 +714,25 @@ encode_extensions([#key_share_server_hello{server_share = ServerShare0} | Rest],
encode_extensions([#key_share_hello_retry_request{selected_group = Group0} | Rest], Acc) ->
Group = tls_v1:group_to_enum(Group0),
encode_extensions(Rest, <<?UINT16(?KEY_SHARE_EXT),
- ?UINT16(2), ?UINT16(Group), Acc/binary>>).
+ ?UINT16(2), ?UINT16(Group), Acc/binary>>);
+encode_extensions([#psk_key_exchange_modes{ke_modes = KEModes0} | Rest], Acc) ->
+ KEModes = encode_psk_key_exchange_modes(KEModes0),
+ KEModesLen = byte_size(KEModes),
+ ExtLen = KEModesLen + 1,
+ encode_extensions(Rest, <<?UINT16(?PSK_KEY_EXCHANGE_MODES_EXT),
+ ?UINT16(ExtLen), ?BYTE(KEModesLen), KEModes/binary, Acc/binary>>);
+encode_extensions([#pre_shared_key_client_hello{
+ offered_psks = #offered_psks{
+ identities = Identities0,
+ binders = Binders0}} | Rest], Acc) ->
+ Identities = encode_psk_identities(Identities0),
+ Binders = encode_psk_binders(Binders0),
+ Len = byte_size(Identities) + byte_size(Binders),
+ encode_extensions(Rest, <<?UINT16(?PRE_SHARED_KEY_EXT),
+ ?UINT16(Len), Identities/binary, Binders/binary, Acc/binary>>);
+encode_extensions([#pre_shared_key_server_hello{selected_identity = Identity} | Rest], Acc) ->
+ encode_extensions(Rest, <<?UINT16(?PRE_SHARED_KEY_EXT),
+ ?UINT16(2), ?UINT16(Identity), Acc/binary>>).
encode_client_protocol_negotiation(undefined, _) ->
@@ -925,7 +950,7 @@ prf({3,_N}, PRFAlgo, Secret, Label, Seed, WantedLength) ->
select_session(SuggestedSessionId, CipherSuites, HashSigns, Compressions, Port, #session{ecc = ECCCurve0} =
Session, Version,
- #ssl_options{ciphers = UserSuites, honor_cipher_order = HonorCipherOrder} = SslOpts,
+ #{ciphers := UserSuites, honor_cipher_order := HonorCipherOrder} = SslOpts,
Cache, CacheCb, Cert) ->
{SessionId, Resumed} = ssl_session:server_id(Port, SuggestedSessionId,
SslOpts, Cert,
@@ -1051,27 +1076,29 @@ client_hello_extensions(Version, CipherSuites, SslOpts, ConnectionStates, Renego
add_tls12_extensions(_Version,
- SslOpts,
+ #{alpn_advertised_protocols := AlpnAdvertisedProtocols,
+ next_protocol_selector := NextProtocolSelector,
+ server_name_indication := ServerNameIndication} = SslOpts,
ConnectionStates,
Renegotiation) ->
SRP = srp_user(SslOpts),
#{renegotiation_info => renegotiation_info(tls_record, client,
ConnectionStates, Renegotiation),
srp => SRP,
- alpn => encode_alpn(SslOpts#ssl_options.alpn_advertised_protocols, Renegotiation),
+ alpn => encode_alpn(AlpnAdvertisedProtocols, Renegotiation),
next_protocol_negotiation =>
- encode_client_protocol_negotiation(SslOpts#ssl_options.next_protocol_selector,
+ encode_client_protocol_negotiation(NextProtocolSelector,
Renegotiation),
- sni => sni(SslOpts#ssl_options.server_name_indication)
+ sni => sni(ServerNameIndication)
}.
add_common_extensions({3,4},
HelloExtensions,
_CipherSuites,
- #ssl_options{eccs = SupportedECCs,
- supported_groups = Groups,
- signature_algs = SignatureSchemes}) ->
+ #{eccs := SupportedECCs,
+ supported_groups := Groups,
+ signature_algs := SignatureSchemes}) ->
{EcPointFormats, _} =
client_ecc_extensions(SupportedECCs),
HelloExtensions#{ec_point_formats => EcPointFormats,
@@ -1081,8 +1108,8 @@ add_common_extensions({3,4},
add_common_extensions(Version,
HelloExtensions,
CipherSuites,
- #ssl_options{eccs = SupportedECCs,
- signature_algs = SupportedHashSigns}) ->
+ #{eccs := SupportedECCs,
+ signature_algs := SupportedHashSigns}) ->
{EcPointFormats, EllipticCurves} =
case advertises_ec_ciphers(
@@ -1100,8 +1127,8 @@ add_common_extensions(Version,
maybe_add_tls13_extensions({3,4},
HelloExtensions0,
- #ssl_options{signature_algs_cert = SignatureSchemes,
- versions = SupportedVersions},
+ #{signature_algs_cert := SignatureSchemes,
+ versions := SupportedVersions},
KeyShare) ->
HelloExtensions =
HelloExtensions0#{client_hello_versions =>
@@ -1203,8 +1230,8 @@ signature_algs_cert(SignatureSchemes) ->
handle_client_hello_extensions(RecordCB, Random, ClientCipherSuites,
Exts, Version,
- #ssl_options{secure_renegotiate = SecureRenegotation,
- alpn_preferred_protocols = ALPNPreferredProtocols} = Opts,
+ #{secure_renegotiate := SecureRenegotation,
+ alpn_preferred_protocols := ALPNPreferredProtocols} = Opts,
#session{cipher_suite = NegotiatedCipherSuite,
compression_method = Compression} = Session0,
ConnectionStates0, Renegotiation) ->
@@ -1239,8 +1266,8 @@ handle_client_hello_extensions(RecordCB, Random, ClientCipherSuites,
handle_server_hello_extensions(RecordCB, Random, CipherSuite, Compression,
Exts, Version,
- #ssl_options{secure_renegotiate = SecureRenegotation,
- next_protocol_selector = NextProtoSelector},
+ #{secure_renegotiate := SecureRenegotation,
+ next_protocol_selector := NextProtoSelector},
ConnectionStates0, Renegotiation) ->
ConnectionStates = handle_renegotiation_extension(client, RecordCB, Version,
maps:get(renegotiation_info, Exts, undefined), Random,
@@ -1256,6 +1283,8 @@ handle_server_hello_extensions(RecordCB, Random, CipherSuite, Compression,
%% We also ignore the ALPN extension during renegotiation (see encode_alpn/2).
[Protocol] when not Renegotiation ->
{ConnectionStates, alpn, Protocol};
+ [_] when Renegotiation ->
+ {ConnectionStates, alpn, undefined};
undefined ->
NextProtocolNegotiation = maps:get(next_protocol_negotiation, Exts, undefined),
Protocol = handle_next_protocol(NextProtocolNegotiation, NextProtoSelector, Renegotiation),
@@ -1455,7 +1484,7 @@ select_hashsign_algs(undefined, ?rsaEncryption, _) ->
select_hashsign_algs(undefined, ?'id-dsa', _) ->
{sha, dsa}.
-srp_user(#ssl_options{srp_identity = {UserName, _}}) ->
+srp_user(#{srp_identity := {UserName, _}}) ->
#srp{username = UserName};
srp_user(_) ->
undefined.
@@ -1486,8 +1515,12 @@ extension_value(#signature_algorithms_cert{signature_scheme_list = Schemes}) ->
Schemes;
extension_value(#key_share_client_hello{client_shares = ClientShares}) ->
ClientShares;
+extension_value(#key_share_server_hello{server_share = ServerShare}) ->
+ ServerShare;
extension_value(#client_hello_versions{versions = Versions}) ->
- Versions.
+ Versions;
+extension_value(#server_hello_selected_version{selected_version = SelectedVersion}) ->
+ SelectedVersion.
%%--------------------------------------------------------------------
@@ -1618,12 +1651,12 @@ handle_path_validation_error({bad_cert, unknown_ca} = Reason, PeerCert, Chain,
Opts, Options, CertDbHandle, CertsDbRef) ->
handle_incomplete_chain(PeerCert, Chain, Opts, Options, CertDbHandle, CertsDbRef, Reason);
handle_path_validation_error({bad_cert, invalid_issuer} = Reason, PeerCert, Chain0,
- Opts, Options, CertDbHandle, CertsDbRef) ->
+ #{partial_chain := PartialChain} = Opts, Options, CertDbHandle, CertsDbRef) ->
case ssl_certificate:certificate_chain(PeerCert, CertDbHandle, CertsDbRef, Chain0) of
{ok, _, [PeerCert | Chain] = OrdedChain} when Chain =/= Chain0 -> %% Chain appaears to be unorded
{Trusted, Path} = ssl_certificate:trusted_cert_and_path(OrdedChain,
CertDbHandle, CertsDbRef,
- Opts#ssl_options.partial_chain),
+ PartialChain),
case public_key:pkix_path_validation(Trusted, Path, Options) of
{ok, {PublicKeyInfo,_}} ->
{PeerCert, PublicKeyInfo};
@@ -1637,12 +1670,13 @@ handle_path_validation_error({bad_cert, invalid_issuer} = Reason, PeerCert, Chai
handle_path_validation_error(Reason, _, _, _, _,_, _) ->
path_validation_alert(Reason).
-handle_incomplete_chain(PeerCert, Chain0, Opts, Options, CertDbHandle, CertsDbRef, PathError0) ->
+handle_incomplete_chain(PeerCert, Chain0,
+ #{partial_chain := PartialChain}, Options, CertDbHandle, CertsDbRef, PathError0) ->
case ssl_certificate:certificate_chain(PeerCert, CertDbHandle, CertsDbRef) of
{ok, _, [PeerCert | _] = Chain} when Chain =/= Chain0 -> %% Chain candidate found
{Trusted, Path} = ssl_certificate:trusted_cert_and_path(Chain,
CertDbHandle, CertsDbRef,
- Opts#ssl_options.partial_chain),
+ PartialChain),
case public_key:pkix_path_validation(Trusted, Path, Options) of
{ok, {PublicKeyInfo,_}} ->
{PeerCert, PublicKeyInfo};
@@ -2089,6 +2123,41 @@ encode_key_share_entry(#key_share_entry{
Len = byte_size(KeyExchange),
<<?UINT16((tls_v1:group_to_enum(Group))),?UINT16(Len),KeyExchange/binary>>.
+encode_psk_key_exchange_modes(KEModes) ->
+ encode_psk_key_exchange_modes(lists:reverse(KEModes), <<>>).
+%%
+encode_psk_key_exchange_modes([], Acc) ->
+ Acc;
+encode_psk_key_exchange_modes([psk_ke|T], Acc) ->
+ encode_psk_key_exchange_modes(T, <<?BYTE(?PSK_KE),Acc/binary>>);
+encode_psk_key_exchange_modes([psk_dhe_ke|T], Acc) ->
+ encode_psk_key_exchange_modes(T, <<?BYTE(?PSK_DHE_KE),Acc/binary>>).
+
+
+encode_psk_identities(Identities) ->
+ encode_psk_identities(Identities, <<>>).
+%%
+encode_psk_identities([], Acc) ->
+ Len = byte_size(Acc),
+ <<?UINT16(Len), Acc/binary>>;
+encode_psk_identities([#psk_identity{
+ identity = Identity,
+ obfuscated_ticket_age = Age}|T], Acc) ->
+ IdLen = byte_size(Identity),
+ encode_psk_identities(T, <<Acc/binary,?UINT16(IdLen),Identity/binary,Age/binary>>).
+
+
+encode_psk_binders(Binders) ->
+ encode_psk_binders(Binders, <<>>).
+%%
+encode_psk_binders([], Acc) ->
+ Len = byte_size(Acc),
+ <<?UINT16(Len), Acc/binary>>;
+encode_psk_binders([Binder|T], Acc) ->
+ Len = byte_size(Binder),
+ encode_psk_binders(T, <<Acc/binary,?BYTE(Len),Binder/binary>>).
+
+
hello_extensions_list(HelloExtensions) ->
[Ext || {_, Ext} <- maps:to_list(HelloExtensions), Ext =/= undefined].
@@ -2441,6 +2510,33 @@ decode_extensions(<<?UINT16(?KEY_SHARE_EXT), ?UINT16(Len),
#key_share_hello_retry_request{
selected_group = tls_v1:enum_to_group(Group)}});
+decode_extensions(<<?UINT16(?PSK_KEY_EXCHANGE_MODES_EXT), ?UINT16(Len),
+ ExtData:Len/binary, Rest/binary>>, Version, MessageType, Acc) ->
+ <<?BYTE(PLen),KEModes:PLen/binary>> = ExtData,
+ decode_extensions(Rest, Version, MessageType,
+ Acc#{psk_key_exchange_modes =>
+ #psk_key_exchange_modes{
+ ke_modes = decode_psk_key_exchange_modes(KEModes)}});
+
+decode_extensions(<<?UINT16(?PRE_SHARED_KEY_EXT), ?UINT16(Len),
+ ExtData:Len/binary, Rest/binary>>,
+ Version, MessageType = client_hello, Acc) ->
+ <<?UINT16(IdLen),Identities:IdLen/binary,?UINT16(BLen),Binders:BLen/binary>> = ExtData,
+ decode_extensions(Rest, Version, MessageType,
+ Acc#{pre_shared_key =>
+ #pre_shared_key_client_hello{
+ offered_psks = #offered_psks{
+ identities = decode_psk_identities(Identities),
+ binders = decode_psk_binders(Binders)}}});
+
+decode_extensions(<<?UINT16(?PRE_SHARED_KEY_EXT), ?UINT16(Len),
+ ExtData:Len/binary, Rest/binary>>,
+ Version, MessageType = server_hello, Acc) ->
+ <<?UINT16(Identity)>> = ExtData,
+ decode_extensions(Rest, Version, MessageType,
+ Acc#{pre_shared_key =>
+ #pre_shared_key_server_hello{
+ selected_identity = Identity}});
%% Ignore data following the ClientHello (i.e.,
%% extensions) if not understood.
@@ -2500,6 +2596,38 @@ decode_protocols(<<?BYTE(Len), Protocol:Len/binary, Rest/binary>>, Acc) ->
decode_protocols(_Bytes, _Acc) ->
{error, invalid_protocols}.
+
+decode_psk_key_exchange_modes(KEModes) ->
+ decode_psk_key_exchange_modes(KEModes, []).
+%%
+decode_psk_key_exchange_modes(<<>>, Acc) ->
+ lists:reverse(Acc);
+decode_psk_key_exchange_modes(<<?BYTE(?PSK_KE), Rest/binary>>, Acc) ->
+ decode_psk_key_exchange_modes(Rest, [psk_ke|Acc]);
+decode_psk_key_exchange_modes(<<?BYTE(?PSK_DHE_KE), Rest/binary>>, Acc) ->
+ decode_psk_key_exchange_modes(Rest, [psk_dhe_ke|Acc]).
+
+
+decode_psk_identities(Identities) ->
+ decode_psk_identities(Identities, []).
+%%
+decode_psk_identities(<<>>, Acc) ->
+ lists:reverse(Acc);
+decode_psk_identities(<<?UINT16(Len), Identity:Len/binary, Age:4/binary, Rest/binary>>, Acc) ->
+ decode_psk_identities(Rest, [#psk_identity{
+ identity = Identity,
+ obfuscated_ticket_age = Age}|Acc]).
+
+
+decode_psk_binders(Binders) ->
+ decode_psk_binders(Binders, []).
+%%
+decode_psk_binders(<<>>, Acc) ->
+ lists:reverse(Acc);
+decode_psk_binders(<<?BYTE(Len), Binder:Len/binary, Rest/binary>>, Acc) ->
+ decode_psk_binders(Rest, [Binder|Acc]).
+
+
%% encode/decode stream of certificate data to/from list of certificate data
certs_to_list(ASN1Certs) ->
certs_to_list(ASN1Certs, []).
@@ -2666,7 +2794,7 @@ filter_unavailable_ecc_suites(_, Suites) ->
handle_renegotiation_extension(Role, RecordCB, Version, Info, Random, NegotiatedCipherSuite,
ClientCipherSuites, Compression,
ConnectionStates0, Renegotiation, SecureRenegotation) ->
- {ok, ConnectionStates} = handle_renegotiation_info(RecordCB, Role, Info, ConnectionStates0,
+ {ok, ConnectionStates} = handle_renegotiation_info(Version, RecordCB, Role, Info, ConnectionStates0,
Renegotiation, SecureRenegotation,
ClientCipherSuites),
hello_pending_connection_states(RecordCB, Role,
@@ -2714,7 +2842,7 @@ handle_next_protocol_on_server(undefined, _Renegotiation, _SslOpts) ->
undefined;
handle_next_protocol_on_server(#next_protocol_negotiation{extension_data = <<>>},
- false, #ssl_options{next_protocols_advertised = Protocols}) ->
+ false, #{next_protocols_advertised := Protocols}) ->
Protocols;
handle_next_protocol_on_server(_Hello, _Renegotiation, _SSLOpts) ->
@@ -2936,11 +3064,11 @@ renegotiation_info(_RecordCB, server, ConnectionStates, true) ->
#renegotiation_info{renegotiated_connection = undefined}
end.
-handle_renegotiation_info(_RecordCB, _, #renegotiation_info{renegotiated_connection = ?byte(0)},
+handle_renegotiation_info(_, _RecordCB, _, #renegotiation_info{renegotiated_connection = ?byte(0)},
ConnectionStates, false, _, _) ->
{ok, ssl_record:set_renegotiation_flag(true, ConnectionStates)};
-handle_renegotiation_info(_RecordCB, server, undefined, ConnectionStates, _, _, CipherSuites) ->
+handle_renegotiation_info(_, _RecordCB, server, undefined, ConnectionStates, _, _, CipherSuites) ->
case is_member(?TLS_EMPTY_RENEGOTIATION_INFO_SCSV, CipherSuites) of
true ->
{ok, ssl_record:set_renegotiation_flag(true, ConnectionStates)};
@@ -2948,10 +3076,10 @@ handle_renegotiation_info(_RecordCB, server, undefined, ConnectionStates, _, _,
{ok, ssl_record:set_renegotiation_flag(false, ConnectionStates)}
end;
-handle_renegotiation_info(_RecordCB, _, undefined, ConnectionStates, false, _, _) ->
+handle_renegotiation_info(_, _RecordCB, _, undefined, ConnectionStates, false, _, _) ->
{ok, ssl_record:set_renegotiation_flag(false, ConnectionStates)};
-handle_renegotiation_info(_RecordCB, client, #renegotiation_info{renegotiated_connection = ClientServerVerify},
+handle_renegotiation_info(_, _RecordCB, client, #renegotiation_info{renegotiated_connection = ClientServerVerify},
ConnectionStates, true, _, _) ->
ConnectionState = ssl_record:current_connection_state(ConnectionStates, read),
CData = maps:get(client_verify_data, ConnectionState),
@@ -2962,7 +3090,7 @@ handle_renegotiation_info(_RecordCB, client, #renegotiation_info{renegotiated_co
false ->
throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, client_renegotiation))
end;
-handle_renegotiation_info(_RecordCB, server, #renegotiation_info{renegotiated_connection = ClientVerify},
+handle_renegotiation_info(_, _RecordCB, server, #renegotiation_info{renegotiated_connection = ClientVerify},
ConnectionStates, true, _, CipherSuites) ->
case is_member(?TLS_EMPTY_RENEGOTIATION_INFO_SCSV, CipherSuites) of
@@ -2978,11 +3106,13 @@ handle_renegotiation_info(_RecordCB, server, #renegotiation_info{renegotiated_co
throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, server_renegotiation))
end
end;
+handle_renegotiation_info({3,0}, _RecordCB, client, undefined, ConnectionStates, true, _SecureRenegotation, _) ->
+ {ok, ssl_record:set_renegotiation_flag(true, ConnectionStates)};
-handle_renegotiation_info(RecordCB, client, undefined, ConnectionStates, true, SecureRenegotation, _) ->
+handle_renegotiation_info(_, RecordCB, client, undefined, ConnectionStates, true, SecureRenegotation, _) ->
handle_renegotiation_info(RecordCB, ConnectionStates, SecureRenegotation);
-handle_renegotiation_info(RecordCB, server, undefined, ConnectionStates, true, SecureRenegotation, CipherSuites) ->
+handle_renegotiation_info(_, RecordCB, server, undefined, ConnectionStates, true, SecureRenegotation, CipherSuites) ->
case is_member(?TLS_EMPTY_RENEGOTIATION_INFO_SCSV, CipherSuites) of
true ->
throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, {server_renegotiation, empty_renegotiation_info_scsv}));
@@ -3036,7 +3166,7 @@ empty_extensions({3,4}, client_hello) ->
%% padding => undefined,
key_share => undefined,
pre_shared_key => undefined,
- %% psk_key_exhange_modes => undefined,
+ psk_key_exchange_modes => undefined,
%% early_data => undefined,
%% cookie => undefined,
client_hello_versions => undefined,
@@ -3065,6 +3195,8 @@ empty_extensions({3,4}, hello_retry_request) ->
key_share => undefined,
pre_shared_key => undefined
};
+empty_extensions({3,0}, _) ->
+ empty_extensions();
empty_extensions(_, server_hello) ->
#{renegotiation_info => undefined,
alpn => undefined,
diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl
index 06c3ccae45..fc9f16a189 100644
--- a/lib/ssl/src/ssl_internal.hrl
+++ b/lib/ssl/src/ssl_internal.hrl
@@ -202,7 +202,7 @@
-type gen_fsm_state_return() :: {next_state, state_name(), any()} |
{next_state, state_name(), any(), timeout()} |
{stop, any(), any()}.
--type ssl_options() :: #ssl_options{}.
+-type ssl_options() :: map().
-endif. % -ifdef(ssl_internal).
diff --git a/lib/ssl/src/ssl_session.erl b/lib/ssl/src/ssl_session.erl
index 44305c65fe..fd012acd5e 100644
--- a/lib/ssl/src/ssl_session.erl
+++ b/lib/ssl/src/ssl_session.erl
@@ -48,13 +48,13 @@ is_new(_ClientSuggestion, _ServerDecision) ->
true.
%%--------------------------------------------------------------------
--spec client_id({ssl:host(), inet:port_number(), #ssl_options{}}, db_handle(), atom(),
+-spec client_id({ssl:host(), inet:port_number(), ssl_options()}, db_handle(), atom(),
undefined | binary()) -> binary().
%%
%% Description: Should be called by the client side to get an id
%% for the client hello message.
%%--------------------------------------------------------------------
-client_id({Host, Port, #ssl_options{reuse_session = SessionId}}, Cache, CacheCb, _) when is_binary(SessionId)->
+client_id({Host, Port, #{reuse_session := SessionId}}, Cache, CacheCb, _) when is_binary(SessionId)->
case CacheCb:lookup(Cache, {{Host, Port}, SessionId}) of
undefined ->
<<>>;
@@ -99,7 +99,7 @@ server_id(Port, SuggestedId, Options, Cert, Cache, CacheCb) ->
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
-select_session({_, _, #ssl_options{reuse_sessions = Reuse}}, _Cache, _CacheCb, _OwnCert) when Reuse =/= true ->
+select_session({_, _, #{reuse_sessions := Reuse}}, _Cache, _CacheCb, _OwnCert) when Reuse =/= true ->
%% If reuse_sessions == true | save a new session should be created
no_session;
select_session({HostIP, Port, SslOpts}, Cache, CacheCb, OwnCert) ->
@@ -108,7 +108,7 @@ select_session({HostIP, Port, SslOpts}, Cache, CacheCb, OwnCert) ->
select_session([], _, _) ->
no_session;
-select_session(Sessions, #ssl_options{ciphers = Ciphers}, OwnCert) ->
+select_session(Sessions, #{ciphers := Ciphers}, OwnCert) ->
IsNotResumable =
fun(Session) ->
not (resumable(Session#session.is_resumable) andalso
@@ -120,9 +120,9 @@ select_session(Sessions, #ssl_options{ciphers = Ciphers}, OwnCert) ->
[Session | _] -> Session#session.session_id
end.
-is_resumable(_, _, #ssl_options{reuse_sessions = false}, _, _, _, _) ->
+is_resumable(_, _, #{reuse_sessions := false}, _, _, _, _) ->
{false, undefined};
-is_resumable(SuggestedSessionId, Port, #ssl_options{reuse_session = ReuseFun} = Options, Cache,
+is_resumable(SuggestedSessionId, Port, #{reuse_session := ReuseFun} = Options, Cache,
CacheCb, SecondLifeTime, OwnCert) ->
case CacheCb:lookup(Cache, {Port, SuggestedSessionId}) of
#session{cipher_suite = CipherSuite,
@@ -149,8 +149,8 @@ resumable(new) ->
resumable(IsResumable) ->
IsResumable.
-reusable_options(#ssl_options{fail_if_no_peer_cert = true,
- verify = verify_peer}, Session) ->
+reusable_options(#{fail_if_no_peer_cert := true,
+ verify := verify_peer}, Session) ->
(Session#session.peer_certificate =/= undefined);
reusable_options(_,_) ->
true.
diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl
index 3998f03519..cf104cd805 100644
--- a/lib/ssl/src/tls_connection.erl
+++ b/lib/ssl/src/tls_connection.erl
@@ -98,7 +98,7 @@
%%====================================================================
%% Setup
%%====================================================================
-start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = false},_, Tracker} = Opts,
+start_fsm(Role, Host, Port, Socket, {#{erl_dist := false},_, Tracker} = Opts,
User, {CbModule, _,_, _, _} = CbInfo,
Timeout) ->
try
@@ -112,7 +112,7 @@ start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = false},_, Tracker}
Error
end;
-start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = true},_, Tracker} = Opts,
+start_fsm(Role, Host, Port, Socket, {#{erl_dist := true},_, Tracker} = Opts,
User, {CbModule, _,_, _, _} = CbInfo,
Timeout) ->
try
@@ -136,10 +136,10 @@ start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = true},_, Tracker} =
start_link(Role, Sender, Host, Port, Socket, Options, User, CbInfo) ->
{ok, proc_lib:spawn_link(?MODULE, init, [[Role, Sender, Host, Port, Socket, Options, User, CbInfo]])}.
-init([Role, Sender, Host, Port, Socket, {SslOpts, _, _} = Options, User, CbInfo]) ->
+init([Role, Sender, Host, Port, Socket, {#{erl_dist := ErlDist}, _, _} = Options, User, CbInfo]) ->
process_flag(trap_exit, true),
link(Sender),
- case SslOpts#ssl_options.erl_dist of
+ case ErlDist of
true ->
process_flag(priority, max);
_ ->
@@ -170,7 +170,7 @@ next_record(_, #state{handshake_env =
next_record(_, #state{protocol_buffers =
#protocol_buffers{tls_cipher_texts = [_|_] = CipherTexts},
connection_states = ConnectionStates,
- ssl_options = #ssl_options{padding_check = Check}} = State) ->
+ ssl_options = #{padding_check := Check}} = State) ->
next_record(State, CipherTexts, ConnectionStates, Check);
next_record(connection, #state{protocol_buffers = #protocol_buffers{tls_cipher_texts = []},
protocol_specific = #{active_n_toggle := true}
@@ -385,12 +385,12 @@ send_handshake(Handshake, State) ->
queue_handshake(Handshake, #state{handshake_env = #handshake_env{tls_handshake_history = Hist0} = HsEnv,
connection_env = #connection_env{negotiated_version = Version},
flight_buffer = Flight0,
- ssl_options = SslOpts,
+ ssl_options = #{log_level := LogLevel},
connection_states = ConnectionStates0} = State0) ->
{BinHandshake, ConnectionStates, Hist} =
encode_handshake(Handshake, Version, ConnectionStates0, Hist0),
- ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'handshake', Handshake),
- ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'record', BinHandshake),
+ ssl_logger:debug(LogLevel, outbound, 'handshake', Handshake),
+ ssl_logger:debug(LogLevel, outbound, 'record', BinHandshake),
State0#state{connection_states = ConnectionStates,
handshake_env = HsEnv#handshake_env{tls_handshake_history = Hist},
@@ -406,11 +406,11 @@ send_handshake_flight(#state{static_env = #static_env{socket = Socket,
queue_change_cipher(Msg, #state{connection_env = #connection_env{negotiated_version = Version},
flight_buffer = Flight0,
- ssl_options = SslOpts,
+ ssl_options = #{log_level := LogLevel},
connection_states = ConnectionStates0} = State0) ->
{BinChangeCipher, ConnectionStates} =
encode_change_cipher(Msg, Version, ConnectionStates0),
- ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'record', BinChangeCipher),
+ ssl_logger:debug(LogLevel, outbound, 'record', BinChangeCipher),
State0#state{connection_states = ConnectionStates,
flight_buffer = Flight0 ++ [BinChangeCipher]}.
@@ -454,12 +454,12 @@ encode_alert(#alert{} = Alert, Version, ConnectionStates) ->
send_alert(Alert, #state{static_env = #static_env{socket = Socket,
transport_cb = Transport},
connection_env = #connection_env{negotiated_version = Version},
- ssl_options = SslOpts,
+ ssl_options = #{log_level := LogLevel},
connection_states = ConnectionStates0} = StateData0) ->
{BinMsg, ConnectionStates} =
encode_alert(Alert, Version, ConnectionStates0),
tls_socket:send(Transport, Socket, BinMsg),
- ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'record', BinMsg),
+ ssl_logger:debug(LogLevel, outbound, 'record', BinMsg),
StateData0#state{connection_states = ConnectionStates}.
%% If an ALERT sent in the connection state, should cause the TLS
@@ -542,7 +542,8 @@ init({call, From}, {start, Timeout},
session_cache_cb = CacheCb},
handshake_env = #handshake_env{renegotiation = {Renegotiation, _}} = HsEnv,
connection_env = CEnv,
- ssl_options = SslOpts,
+ ssl_options = #{log_level := LogLevel,
+ versions := Versions} = SslOpts,
session = #session{own_certificate = Cert} = Session0,
connection_states = ConnectionStates0
} = State0) ->
@@ -550,13 +551,13 @@ init({call, From}, {start, Timeout},
Hello = tls_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts,
Cache, CacheCb, Renegotiation, Cert, KeyShare),
- HelloVersion = tls_record:hello_version(SslOpts#ssl_options.versions),
+ HelloVersion = tls_record:hello_version(Versions),
Handshake0 = ssl_handshake:init_handshake_history(),
{BinMsg, ConnectionStates, Handshake} =
encode_handshake(Hello, HelloVersion, ConnectionStates0, Handshake0),
tls_socket:send(Transport, Socket, BinMsg),
- ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'handshake', Hello),
- ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'record', BinMsg),
+ ssl_logger:debug(LogLevel, outbound, 'handshake', Hello),
+ ssl_logger:debug(LogLevel, outbound, 'record', BinMsg),
State = State0#state{connection_states = ConnectionStates,
connection_env = CEnv#connection_env{negotiated_version = HelloVersion}, %% Requested version
@@ -592,14 +593,14 @@ error(_, _, _) ->
gen_statem:state_function_result().
%%--------------------------------------------------------------------
hello(internal, #client_hello{extensions = Extensions} = Hello,
- #state{ssl_options = #ssl_options{handshake = hello},
+ #state{ssl_options = #{handshake := hello},
handshake_env = HsEnv,
start_or_recv_from = From} = State) ->
{next_state, user_hello, State#state{start_or_recv_from = undefined,
handshake_env = HsEnv#handshake_env{hello = Hello}},
[{reply, From, {ok, Extensions}}]};
hello(internal, #server_hello{extensions = Extensions} = Hello,
- #state{ssl_options = #ssl_options{handshake = hello},
+ #state{ssl_options = #{handshake := hello},
handshake_env = HsEnv,
start_or_recv_from = From} = State) ->
{next_state, user_hello, State#state{start_or_recv_from = undefined,
@@ -982,8 +983,9 @@ code_change(_OldVsn, StateName, State, _) ->
%%--------------------------------------------------------------------
initial_state(Role, Sender, Host, Port, Socket, {SSLOptions, SocketOptions, Tracker}, User,
{CbModule, DataTag, CloseTag, ErrorTag, PassiveTag}) ->
- #ssl_options{beast_mitigation = BeastMitigation,
- erl_dist = IsErlDist} = SSLOptions,
+ #{beast_mitigation := BeastMitigation,
+ erl_dist := IsErlDist,
+ client_renegotiation := ClientRenegotiation} = SSLOptions,
ConnectionStates = tls_record:init_connection_states(Role, BeastMitigation),
SessionCacheCb = case application:get_env(ssl, session_cb) of
{ok, Cb} when is_atom(Cb) ->
@@ -1017,7 +1019,7 @@ initial_state(Role, Sender, Host, Port, Socket, {SSLOptions, SocketOptions, Trac
handshake_env = #handshake_env{
tls_handshake_history = ssl_handshake:init_handshake_history(),
renegotiation = {false, first},
- allow_renegotiate = SSLOptions#ssl_options.client_renegotiation
+ allow_renegotiate = ClientRenegotiation
},
connection_env = #connection_env{user_application = {UserMonitor, User}},
socket_options = SocketOptions,
@@ -1042,8 +1044,8 @@ initialize_tls_sender(#state{static_env = #static_env{
},
connection_env = #connection_env{negotiated_version = Version},
socket_options = SockOpts,
- ssl_options = #ssl_options{renegotiate_at = RenegotiateAt,
- log_level = LogLevel},
+ ssl_options = #{renegotiate_at := RenegotiateAt,
+ log_level := LogLevel},
connection_states = #{current_write := ConnectionWriteState},
protocol_specific = #{sender := Sender}}) ->
Init = #{current_write => ConnectionWriteState,
@@ -1261,7 +1263,7 @@ unprocessed_events(Events) ->
assert_buffer_sanity(<<?BYTE(_Type), ?UINT24(Length), Rest/binary>>,
- #ssl_options{max_handshake_size = Max}) when
+ #{max_handshake_size := Max}) when
Length =< Max ->
case size(Rest) of
N when N < Length ->
@@ -1295,18 +1297,17 @@ ensure_sender_terminate(_, #state{protocol_specific = #{sender := Sender}}) ->
end,
spawn(Kill).
-maybe_generate_client_shares(#ssl_options{
- versions = [Version|_],
- supported_groups =
- #supported_groups{
- supported_groups = [Group|_]}})
+maybe_generate_client_shares(#{versions := [Version|_],
+ supported_groups :=
+ #supported_groups{
+ supported_groups = [Group|_]}})
when Version =:= {3,4} ->
%% Generate only key_share entry for the most preferred group
ssl_cipher:generate_client_shares([Group]);
maybe_generate_client_shares(_) ->
undefined.
-choose_tls_version(#ssl_options{versions = Versions},
+choose_tls_version(#{versions := Versions},
#client_hello{
extensions = #{client_hello_versions :=
#client_hello_versions{versions = ClientVersions}
@@ -1322,7 +1323,7 @@ choose_tls_version(_, _) ->
'tls_v1.2'.
-effective_version(undefined, #ssl_options{versions = [Version|_]}) ->
+effective_version(undefined, #{versions := [Version|_]}) ->
Version;
effective_version(Version, _) ->
Version.
diff --git a/lib/ssl/src/tls_handshake.erl b/lib/ssl/src/tls_handshake.erl
index c132f75eae..203f89a0b8 100644
--- a/lib/ssl/src/tls_handshake.erl
+++ b/lib/ssl/src/tls_handshake.erl
@@ -51,17 +51,17 @@
%%====================================================================
%%--------------------------------------------------------------------
-spec client_hello(ssl:host(), inet:port_number(), ssl_record:connection_states(),
- #ssl_options{}, integer(), atom(), boolean(), der_cert(),
+ ssl_options(), integer(), atom(), boolean(), der_cert(),
#key_share_client_hello{} | undefined) ->
#client_hello{}.
%%
%% Description: Creates a client hello message.
%%--------------------------------------------------------------------
client_hello(Host, Port, ConnectionStates,
- #ssl_options{versions = Versions,
- ciphers = UserSuites,
- fallback = Fallback
- } = SslOpts,
+ #{versions := Versions,
+ ciphers := UserSuites,
+ fallback := Fallback
+ } = SslOpts,
Cache, CacheCb, Renegotiation, OwnCert, KeyShare) ->
Version = tls_record:highest_protocol_version(Versions),
@@ -95,7 +95,7 @@ client_hello(Host, Port, ConnectionStates,
}.
%%--------------------------------------------------------------------
--spec hello(#server_hello{} | #client_hello{}, #ssl_options{},
+-spec hello(#server_hello{} | #client_hello{}, ssl_options(),
ssl_record:connection_states() | {inet:port_number(), #session{}, db_handle(),
atom(), ssl_record:connection_states(),
binary() | undefined, ssl:kex_algo()},
@@ -117,7 +117,7 @@ client_hello(Host, Port, ConnectionStates,
%% values.
hello(#server_hello{server_version = {Major, Minor},
random = <<_:24/binary,Down:8/binary>>},
- #ssl_options{versions = [{M,N}|_]}, _, _)
+ #{versions := [{M,N}|_]}, _, _)
when (M > 3 orelse M =:= 3 andalso N >= 4) andalso %% TLS 1.3 client
(Major =:= 3 andalso Minor =:= 3 andalso %% Negotiating TLS 1.2
Down =:= ?RANDOM_OVERRIDE_TLS12) orelse
@@ -131,7 +131,7 @@ hello(#server_hello{server_version = {Major, Minor},
%% equal to the second value if the ServerHello indicates TLS 1.1 or below.
hello(#server_hello{server_version = {Major, Minor},
random = <<_:24/binary,Down:8/binary>>},
- #ssl_options{versions = [{M,N}|_]}, _, _)
+ #{versions := [{M,N}|_]}, _, _)
when (M =:= 3 andalso N =:= 3) andalso %% TLS 1.2 client
(Major =:= 3 andalso Minor < 3 andalso %% Negotiating TLS 1.1 or prior
Down =:= ?RANDOM_OVERRIDE_TLS11) ->
@@ -156,7 +156,7 @@ hello(#server_hello{server_version = LegacyVersion,
extensions = #{server_hello_selected_version :=
#server_hello_selected_version{selected_version = Version} = HelloExt}
},
- #ssl_options{versions = SupportedVersions} = SslOpt,
+ #{versions := SupportedVersions} = SslOpt,
ConnectionStates0, Renegotiation) ->
%% In TLS 1.3, the TLS server indicates its version using the "supported_versions" extension
%% (Section 4.2.1), and the legacy_version field MUST be set to 0x0303, which is the version
@@ -190,7 +190,7 @@ hello(#server_hello{server_version = Version,
compression_method = Compression,
session_id = SessionId,
extensions = HelloExt},
- #ssl_options{versions = SupportedVersions} = SslOpt,
+ #{versions := SupportedVersions} = SslOpt,
ConnectionStates0, Renegotiation) ->
case tls_record:is_acceptable_version(Version, SupportedVersions) of
true ->
@@ -221,7 +221,7 @@ hello(#client_hello{client_version = _ClientVersion,
extensions = #{client_hello_versions :=
#client_hello_versions{versions = ClientVersions}
}} = Hello,
- #ssl_options{versions = Versions} = SslOpts,
+ #{versions := Versions} = SslOpts,
Info, Renegotiation) ->
try
Version = ssl_handshake:select_supported_version(ClientVersions, Versions),
@@ -233,7 +233,7 @@ hello(#client_hello{client_version = _ClientVersion,
hello(#client_hello{client_version = ClientVersion,
cipher_suites = CipherSuites} = Hello,
- #ssl_options{versions = Versions} = SslOpts,
+ #{versions := Versions} = SslOpts,
Info, Renegotiation) ->
try
Version = ssl_handshake:select_version(tls_record, ClientVersion, Versions),
@@ -269,7 +269,7 @@ encode_handshake(Package, Version) ->
%%--------------------------------------------------------------------
-spec get_tls_handshake(tls_record:tls_version(), binary(), binary() | iolist(),
- #ssl_options{}) ->
+ ssl_options()) ->
{[{tls_handshake(), binary()}], binary()}.
%%
%% Description: Given buffered and new data from ssl_record, collects
@@ -290,10 +290,10 @@ handle_client_hello(Version,
compression_methods = Compressions,
random = Random,
extensions = HelloExt},
- #ssl_options{versions = Versions,
- signature_algs = SupportedHashSigns,
- eccs = SupportedECCs,
- honor_ecc_order = ECCOrder} = SslOpts,
+ #{versions := Versions,
+ signature_algs := SupportedHashSigns,
+ eccs := SupportedECCs,
+ honor_ecc_order := ECCOrder} = SslOpts,
{Port, Session0, Cache, CacheCb, ConnectionStates0, Cert, _},
Renegotiation) ->
case tls_record:is_acceptable_version(Version, Versions) of
@@ -379,7 +379,7 @@ do_hello(Version, Versions, CipherSuites, Hello, SslOpts, Info, Renegotiation) -
%%--------------------------------------------------------------------
enc_handshake(#hello_request{}, {3, N}) when N < 4 ->
{?HELLO_REQUEST, <<>>};
-enc_handshake(#client_hello{client_version = {Major, Minor},
+enc_handshake(#client_hello{client_version = {Major, Minor} = Version,
random = Random,
session_id = SessionID,
cipher_suites = CipherSuites,
@@ -390,7 +390,7 @@ enc_handshake(#client_hello{client_version = {Major, Minor},
CmLength = byte_size(BinCompMethods),
BinCipherSuites = list_to_binary(CipherSuites),
CsLength = byte_size(BinCipherSuites),
- ExtensionsBin = ssl_handshake:encode_hello_extensions(HelloExtensions),
+ ExtensionsBin = ssl_handshake:encode_hello_extensions(HelloExtensions, Version),
{?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
?BYTE(SIDLength), SessionID/binary,
@@ -404,11 +404,11 @@ enc_handshake(HandshakeMsg, Version) ->
%%--------------------------------------------------------------------
get_tls_handshake_aux(Version, <<?BYTE(Type), ?UINT24(Length),
Body:Length/binary,Rest/binary>>,
- Opts, Acc) ->
+ #{log_level := LogLevel} = Opts, Acc) ->
Raw = <<?BYTE(Type), ?UINT24(Length), Body/binary>>,
try decode_handshake(Version, Type, Body) of
Handshake ->
- ssl_logger:debug(Opts#ssl_options.log_level, inbound, 'handshake', Handshake),
+ ssl_logger:debug(LogLevel, inbound, 'handshake', Handshake),
get_tls_handshake_aux(Version, Rest, Opts, [{Handshake,Raw} | Acc])
catch
_:_ ->
diff --git a/lib/ssl/src/tls_handshake_1_3.erl b/lib/ssl/src/tls_handshake_1_3.erl
index 49d20b3ec0..5f4c0b9a4a 100644
--- a/lib/ssl/src/tls_handshake_1_3.erl
+++ b/lib/ssl/src/tls_handshake_1_3.erl
@@ -39,7 +39,7 @@
%% Create handshake messages
-export([certificate/5,
certificate_verify/4,
- encrypted_extensions/0]).
+ encrypted_extensions/1]).
-export([do_start/2,
do_negotiated/2,
@@ -61,10 +61,10 @@
%% Create handshake messages
%%====================================================================
-server_hello(MsgType, SessionId, KeyShare, ConnectionStates, ALPN) ->
+server_hello(MsgType, SessionId, KeyShare, ConnectionStates) ->
#{security_parameters := SecParams} =
ssl_record:pending_connection_state(ConnectionStates, read),
- Extensions = server_hello_extensions(MsgType, KeyShare, ALPN),
+ Extensions = server_hello_extensions(MsgType, KeyShare),
#server_hello{server_version = {3,3}, %% legacy_version
cipher_suite = SecParams#security_parameters.cipher_suite,
compression_method = 0, %% legacy attribute
@@ -73,6 +73,7 @@ server_hello(MsgType, SessionId, KeyShare, ConnectionStates, ALPN) ->
extensions = Extensions
}.
+
%% The server's extensions MUST contain "supported_versions".
%% Additionally, it SHOULD contain the minimal set of extensions
%% necessary for the client to generate a correct ClientHello pair. As
@@ -80,18 +81,14 @@ server_hello(MsgType, SessionId, KeyShare, ConnectionStates, ALPN) ->
%% extensions that were not first offered by the client in its
%% ClientHello, with the exception of optionally the "cookie" (see
%% Section 4.2.2) extension.
-server_hello_extensions(hello_retry_request = MsgType, KeyShare, _) ->
+server_hello_extensions(hello_retry_request = MsgType, KeyShare) ->
SupportedVersions = #server_hello_selected_version{selected_version = {3,4}},
Extensions = #{server_hello_selected_version => SupportedVersions},
ssl_handshake:add_server_share(MsgType, Extensions, KeyShare);
-server_hello_extensions(MsgType, KeyShare, undefined) ->
+server_hello_extensions(MsgType, KeyShare) ->
SupportedVersions = #server_hello_selected_version{selected_version = {3,4}},
Extensions = #{server_hello_selected_version => SupportedVersions},
- ssl_handshake:add_server_share(MsgType, Extensions, KeyShare);
-server_hello_extensions(MsgType, KeyShare, ALPN0) ->
- Extensions0 = ssl_handshake:add_selected_version(#{}), %% {3,4} (TLS 1.3)
- Extensions1 = ssl_handshake:add_alpn(Extensions0, ALPN0),
- ssl_handshake:add_server_share(MsgType, Extensions1, KeyShare).
+ ssl_handshake:add_server_share(MsgType, Extensions, KeyShare).
server_hello_random(server_hello, #security_parameters{server_random = Random}) ->
@@ -107,10 +104,14 @@ server_hello_random(hello_retry_request, _) ->
?HELLO_RETRY_REQUEST_RANDOM.
-%% TODO: implement support for encrypted_extensions
-encrypted_extensions() ->
+encrypted_extensions(#state{handshake_env = #handshake_env{alpn = undefined}}) ->
#encrypted_extensions{
extensions = #{}
+ };
+encrypted_extensions(#state{handshake_env = #handshake_env{alpn = ALPNProtocol}}) ->
+ Extensions = ssl_handshake:add_alpn(#{}, ALPNProtocol),
+ #encrypted_extensions{
+ extensions = Extensions
}.
@@ -335,8 +336,9 @@ decode_handshake(?ENCRYPTED_EXTENSIONS, <<?UINT16(Size), EncExts:Size/binary>>)
extensions = decode_extensions(EncExts, encrypted_extensions)
};
decode_handshake(?NEW_SESSION_TICKET, <<?UINT32(LifeTime), ?UINT32(Age),
- ?BYTE(Nonce), ?UINT16(TicketSize), Ticket:TicketSize/binary,
- BinExts/binary>>) ->
+ ?BYTE(NonceSize), Nonce:NonceSize/binary,
+ ?UINT16(TicketSize), Ticket:TicketSize/binary,
+ ?UINT16(BinExtSize), BinExts:BinExtSize/binary>>) ->
Exts = decode_extensions(BinExts, encrypted_extensions),
#new_session_ticket{ticket_lifetime = LifeTime,
ticket_age_add = Age,
@@ -500,10 +502,11 @@ do_start(#client_hello{cipher_suites = ClientCiphers,
session_id = SessionId,
extensions = Extensions} = _Hello,
#state{connection_states = _ConnectionStates0,
- ssl_options = #ssl_options{ciphers = ServerCiphers,
- signature_algs = ServerSignAlgs,
- supported_groups = ServerGroups0,
- alpn_preferred_protocols = ALPNPreferredProtocols},
+ ssl_options = #{ciphers := ServerCiphers,
+ signature_algs := ServerSignAlgs,
+ supported_groups := ServerGroups0,
+ alpn_preferred_protocols := ALPNPreferredProtocols,
+ honor_cipher_order := HonorCipherOrder},
session = #session{own_certificate = Cert}} = State0) ->
ClientGroups0 = maps:get(elliptic_curves, Extensions, undefined),
ClientGroups = get_supported_groups(ClientGroups0),
@@ -530,7 +533,7 @@ do_start(#client_hello{cipher_suites = ClientCiphers,
%% cipher suite, an (EC)DHE group and key share for key establishment,
%% and a signature algorithm/certificate pair to authenticate itself to
%% the client.
- Cipher = Maybe(select_cipher_suite(ClientCiphers, ServerCiphers)),
+ Cipher = Maybe(select_cipher_suite(HonorCipherOrder, ClientCiphers, ServerCiphers)),
Groups = Maybe(select_common_groups(ServerGroups, ClientGroups)),
Maybe(validate_client_key_share(ClientGroups, ClientShares)),
@@ -598,8 +601,10 @@ do_start(#server_hello{cipher_suite = SelectedCipherSuite,
handshake_env = #handshake_env{renegotiation = {Renegotiation, _},
tls_handshake_history = _HHistory} = HsEnv,
connection_env = CEnv,
- ssl_options = #ssl_options{ciphers = ClientCiphers,
- supported_groups = ClientGroups0} = SslOpts,
+ ssl_options = #{ciphers := ClientCiphers,
+ supported_groups := ClientGroups0,
+ versions := Versions,
+ log_level := LogLevel} = SslOpts,
session = #session{own_certificate = Cert} = Session0,
connection_states = ConnectionStates0
} = State0) ->
@@ -630,7 +635,7 @@ do_start(#server_hello{cipher_suite = SelectedCipherSuite,
Hello = tls_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts,
Cache, CacheCb, Renegotiation, Cert, ClientKeyShare),
- HelloVersion = tls_record:hello_version(SslOpts#ssl_options.versions),
+ HelloVersion = tls_record:hello_version(Versions),
%% Update state
State1 = update_start_state(State0,
@@ -646,8 +651,8 @@ do_start(#server_hello{cipher_suite = SelectedCipherSuite,
{BinMsg, ConnectionStates, Handshake} =
tls_connection:encode_handshake(Hello, HelloVersion, ConnectionStates0, HHistory),
tls_socket:send(Transport, Socket, BinMsg),
- ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'handshake', Hello),
- ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'record', BinMsg),
+ ssl_logger:debug(LogLevel, outbound, 'handshake', Hello),
+ ssl_logger:debug(LogLevel, outbound, 'record', BinMsg),
State = State2#state{
connection_states = ConnectionStates,
@@ -671,10 +676,9 @@ do_negotiated(start_handshake,
ecc = SelectedGroup,
sign_alg = SignatureScheme,
dh_public_value = ClientPublicKey},
- ssl_options = #ssl_options{} = SslOpts,
+ ssl_options = #{} = SslOpts,
key_share = KeyShare,
- handshake_env = #handshake_env{tls_handshake_history = _HHistory0,
- alpn = ALPN},
+ handshake_env = #handshake_env{tls_handshake_history = _HHistory0},
connection_env = #connection_env{private_key = CertPrivateKey},
static_env = #static_env{
cert_db = CertDbHandle,
@@ -689,7 +693,7 @@ do_negotiated(start_handshake,
try
%% Create server_hello
%% Extensions: supported_versions, key_share, (pre_shared_key)
- ServerHello = server_hello(server_hello, SessionId, KeyShare, ConnectionStates0, ALPN),
+ ServerHello = server_hello(server_hello, SessionId, KeyShare, ConnectionStates0),
{State1, _} = tls_connection:send_handshake(ServerHello, State0),
@@ -699,7 +703,7 @@ do_negotiated(start_handshake,
State3 = ssl_record:step_encryption_state(State2),
%% Create EncryptedExtensions
- EncryptedExtensions = encrypted_extensions(),
+ EncryptedExtensions = encrypted_extensions(State2),
%% Encode EncryptedExtensions
State4 = tls_connection:queue_handshake(EncryptedExtensions, State3),
@@ -830,8 +834,8 @@ do_wait_sh(#server_hello{cipher_suite = SelectedCipherSuite,
session_id = SessionId,
extensions = Extensions} = ServerHello,
#state{key_share = ClientKeyShare0,
- ssl_options = #ssl_options{ciphers = ClientCiphers,
- supported_groups = ClientGroups0}} = State0) ->
+ ssl_options = #{ciphers := ClientCiphers,
+ supported_groups := ClientGroups0}} = State0) ->
ClientGroups = get_supported_groups(ClientGroups0),
ServerKeyShare0 = maps:get(key_share, Extensions, undefined),
ClientKeyShare = get_key_shares(ClientKeyShare0),
@@ -881,12 +885,19 @@ do_wait_sh(#server_hello{cipher_suite = SelectedCipherSuite,
end.
-do_wait_ee(#encrypted_extensions{extensions = _Extensions}, State0) ->
+do_wait_ee(#encrypted_extensions{extensions = Extensions}, State0) ->
+
+ ALPNProtocol0 = maps:get(alpn, Extensions, undefined),
+ ALPNProtocol = get_alpn(ALPNProtocol0),
{Ref,_Maybe} = maybe(),
try
- {State0, wait_cert_cr}
+ %% Update state
+ #state{handshake_env = HsEnv} = State0,
+ State1 = State0#state{handshake_env = HsEnv#handshake_env{alpn = ALPNProtocol}},
+
+ {State1, wait_cert_cr}
catch
{Ref, {insufficient_security, no_suitable_groups}} ->
?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_groups);
@@ -968,7 +979,7 @@ maybe_queue_cert_cert_cv(#state{client_certificate_requested = false} = State) -
maybe_queue_cert_cert_cv(#state{connection_states = _ConnectionStates0,
session = #session{session_id = _SessionId,
own_certificate = OwnCert},
- ssl_options = #ssl_options{} = _SslOpts,
+ ssl_options = #{} = _SslOpts,
key_share = _KeyShare,
handshake_env = #handshake_env{tls_handshake_history = _HHistory0},
static_env = #static_env{
@@ -1041,10 +1052,9 @@ compare_verify_data(_, _) ->
{error, decrypt_error}.
-send_hello_retry_request(#state{connection_states = ConnectionStates0,
- handshake_env = #handshake_env{alpn = ALPN}} = State0,
+send_hello_retry_request(#state{connection_states = ConnectionStates0} = State0,
no_suitable_key, KeyShare, SessionId) ->
- ServerHello = server_hello(hello_retry_request, SessionId, KeyShare, ConnectionStates0, ALPN),
+ ServerHello = server_hello(hello_retry_request, SessionId, KeyShare, ConnectionStates0),
{State1, _} = tls_connection:send_handshake(ServerHello, State0),
%% Update handshake history
@@ -1056,12 +1066,11 @@ send_hello_retry_request(State0, _, _, _) ->
{ok, {State0, negotiated}}.
-maybe_send_certificate_request(State, #ssl_options{verify = verify_none}) ->
+maybe_send_certificate_request(State, #{verify := verify_none}) ->
{State, wait_finished};
-maybe_send_certificate_request(State, #ssl_options{
- verify = verify_peer,
- signature_algs = SignAlgs,
- signature_algs_cert = SignAlgsCert}) ->
+maybe_send_certificate_request(State, #{verify := verify_peer,
+ signature_algs := SignAlgs,
+ signature_algs_cert := SignAlgsCert}) ->
CertificateRequest = certificate_request(SignAlgs, SignAlgsCert),
{tls_connection:queue_handshake(CertificateRequest, State), wait_cert}.
@@ -1095,15 +1104,13 @@ process_certificate(#certificate_1_3{
certificate_request_context = <<>>,
certificate_list = []},
#state{ssl_options =
- #ssl_options{
- fail_if_no_peer_cert = false}} = State) ->
+ #{fail_if_no_peer_cert := false}} = State) ->
{ok, {State, wait_finished}};
process_certificate(#certificate_1_3{
certificate_request_context = <<>>,
certificate_list = []},
#state{ssl_options =
- #ssl_options{
- fail_if_no_peer_cert = true}} = State0) ->
+ #{fail_if_no_peer_cert := true}} = State0) ->
%% At this point the client believes that the connection is up and starts using
%% its traffic secrets. In order to be able send an proper Alert to the client
@@ -1114,8 +1121,8 @@ process_certificate(#certificate_1_3{
{error, {certificate_required, State}};
process_certificate(#certificate_1_3{certificate_list = Certs0},
#state{ssl_options =
- #ssl_options{signature_algs = SignAlgs,
- signature_algs_cert = SignAlgsCert} = SslOptions,
+ #{signature_algs := SignAlgs,
+ signature_algs_cert := SignAlgsCert} = SslOptions,
static_env =
#static_env{
role = Role,
@@ -1173,19 +1180,26 @@ update_encryption_state(client, State) ->
State.
-validate_certificate_chain(Certs, CertDbHandle, CertDbRef, SslOptions, CRLDbHandle, Role, Host) ->
- ServerName = ssl_handshake:server_name(SslOptions#ssl_options.server_name_indication, Host, Role),
+validate_certificate_chain(Certs, CertDbHandle, CertDbRef,
+ #{server_name_indication := ServerNameIndication,
+ partial_chain := PartialChain,
+ verify_fun := VerifyFun,
+ customize_hostname_check := CustomizeHostnameCheck,
+ crl_check := CrlCheck,
+ depth := Depth} = SslOptions,
+ CRLDbHandle, Role, Host) ->
+ ServerName = ssl_handshake:server_name(ServerNameIndication, Host, Role),
[PeerCert | ChainCerts ] = Certs,
try
{TrustedCert, CertPath} =
ssl_certificate:trusted_cert_and_path(Certs, CertDbHandle, CertDbRef,
- SslOptions#ssl_options.partial_chain),
+ PartialChain),
ValidationFunAndState =
- ssl_handshake:validation_fun_and_state(SslOptions#ssl_options.verify_fun, Role,
+ ssl_handshake:validation_fun_and_state(VerifyFun, Role,
CertDbHandle, CertDbRef, ServerName,
- SslOptions#ssl_options.customize_hostname_check,
- SslOptions#ssl_options.crl_check, CRLDbHandle, CertPath),
- Options = [{max_path_length, SslOptions#ssl_options.depth},
+ CustomizeHostnameCheck,
+ CrlCheck, CRLDbHandle, CertPath),
+ Options = [{max_path_length, Depth},
{verify_fun, ValidationFunAndState}],
%% TODO: Validate if Certificate is using a supported signature algorithm
%% (signature_algs_cert)!
@@ -1524,9 +1538,7 @@ get_handshake_context_client(L) ->
%% CertificateRequest message.
verify_signature_algorithm(#state{
static_env = #static_env{role = Role},
- ssl_options =
- #ssl_options{
- signature_algs = LocalSignAlgs}} = State0,
+ ssl_options = #{signature_algs := LocalSignAlgs}} = State0,
#certificate_verify_1_3{algorithm = PeerSignAlg}) ->
case lists:member(PeerSignAlg, LocalSignAlgs) of
true ->
@@ -1725,15 +1737,19 @@ handle_alpn([ServerProtocol|T], ClientProtocols) ->
end.
-select_cipher_suite([], _) ->
+select_cipher_suite(_, [], _) ->
{error, no_suitable_cipher};
-select_cipher_suite([Cipher|ClientCiphers], ServerCiphers) ->
+%% If honor_cipher_order is set to true, use the server's preference for
+%% cipher suite selection.
+select_cipher_suite(true, ClientCiphers, ServerCiphers) ->
+ select_cipher_suite(false, ServerCiphers, ClientCiphers);
+select_cipher_suite(false, [Cipher|ClientCiphers], ServerCiphers) ->
case lists:member(Cipher, tls_v1:suites('TLS_v1.3')) andalso
lists:member(Cipher, ServerCiphers) of
true ->
{ok, Cipher};
false ->
- select_cipher_suite(ClientCiphers, ServerCiphers)
+ select_cipher_suite(false, ClientCiphers, ServerCiphers)
end.
@@ -1869,6 +1885,14 @@ get_key_shares(#key_share_server_hello{server_share = ServerShare}) ->
get_selected_group(#key_share_hello_retry_request{selected_group = SelectedGroup}) ->
SelectedGroup.
+get_alpn(ALPNProtocol0) ->
+ case ssl_handshake:decode_alpn(ALPNProtocol0) of
+ undefined ->
+ undefined;
+ [ALPNProtocol] ->
+ ALPNProtocol
+ end.
+
maybe() ->
Ref = erlang:make_ref(),
Ok = fun(ok) -> ok;
diff --git a/lib/ssl/src/tls_handshake_1_3.hrl b/lib/ssl/src/tls_handshake_1_3.hrl
index 7ae1b93e1c..eb85f216c8 100644
--- a/lib/ssl/src/tls_handshake_1_3.hrl
+++ b/lib/ssl/src/tls_handshake_1_3.hrl
@@ -74,29 +74,52 @@
y % opaque Y[coordinate_length];
}).
+%% RFC 8446 4.2.9. Pre-Shared Key Exchange Modes
+
+%% enum { psk_ke(0), psk_dhe_ke(1), (255) } PskKeyExchangeMode;
-define(PSK_KE, 0).
-define(PSK_DHE_KE, 1).
--record(psk_keyexchange_modes, {
+-record(psk_key_exchange_modes, {
ke_modes % ke_modes<1..255>
}).
+
+%% RFC 8446 4.2.10. Early Data Indication
-record(empty, {
}).
-record(early_data_indication, {
indication % uint32 max_early_data_size (new_session_ticket) |
%% #empty{} (client_hello, encrypted_extensions)
}).
--record(psk_identity, {
- identity, % opaque identity<1..2^16-1>
- obfuscated_ticket_age % uint32
- }).
--record(offered_psks, {
- psk_identity, %identities<7..2^16-1>;
- psk_binder_entry %binders<33..2^16-1>, opaque PskBinderEntry<32..255>
- }).
--record(pre_shared_keyextension,{
- extension %OfferedPsks (client_hello) | uint16 selected_identity (server_hello)
- }).
+
+%% RFC 8446 4.2.11. Pre-Shared Key Extension
+-record(psk_identity,
+ {
+ identity, % opaque identity<1..2^16-1>
+ obfuscated_ticket_age % uint32
+ }).
+
+-record(offered_psks,
+ {
+ identities, % PskIdentity identities<7..2^16-1>;
+ binders % PskBinderEntry binders<33..2^16-1>; opaque PskBinderEntry<32..255>
+ }).
+
+%% struct {
+%% select (Handshake.msg_type) {
+%% case client_hello: OfferedPsks;
+%% case server_hello: uint16 selected_identity;
+%% };
+%% } PreSharedKeyExtension;
+-record(pre_shared_key_client_hello,
+ {
+ offered_psks
+ }).
+
+-record(pre_shared_key_server_hello,
+ {
+ selected_identity
+ }).
%% RFC 8446 B.3.1.2.
-record(cookie, {
diff --git a/lib/ssl/src/tls_record.erl b/lib/ssl/src/tls_record.erl
index a5c550a429..cfd75076b3 100644
--- a/lib/ssl/src/tls_record.erl
+++ b/lib/ssl/src/tls_record.erl
@@ -83,7 +83,7 @@ init_connection_states(Role, BeastMitigation) ->
binary(),
[tls_version()] | tls_version(),
Buffer0 :: binary() | {'undefined' | #ssl_tls{}, {[binary()],non_neg_integer(),[binary()]}},
- #ssl_options{}) ->
+ ssl_options()) ->
{Records :: [#ssl_tls{}],
Buffer :: {'undefined' | #ssl_tls{}, {[binary()],non_neg_integer(),[binary()]}}} |
#alert{}.
@@ -495,7 +495,9 @@ validate_tls_record_version(Versions, Q, SslOpts, Acc, Type, Version, Length) ->
validate_tls_record_length(_Versions, Q, _SslOpts, Acc, Type, Version, undefined) ->
{lists:reverse(Acc),
{#ssl_tls{type = Type, version = Version, fragment = undefined}, Q}};
-validate_tls_record_length(Versions, {_,Size0,_} = Q0, SslOpts, Acc, Type, Version, Length) ->
+validate_tls_record_length(Versions, {_,Size0,_} = Q0,
+ #{log_level := LogLevel} = SslOpts,
+ Acc, Type, Version, Length) ->
if
Length =< ?MAX_CIPHER_TEXT_LENGTH ->
if
@@ -503,7 +505,7 @@ validate_tls_record_length(Versions, {_,Size0,_} = Q0, SslOpts, Acc, Type, Versi
%% Complete record
{Fragment, Q} = binary_from_front(Length, Q0),
Record = #ssl_tls{type = Type, version = Version, fragment = Fragment},
- ssl_logger:debug(SslOpts#ssl_options.log_level, inbound, 'record', Record),
+ ssl_logger:debug(LogLevel, inbound, 'record', Record),
decode_tls_records(Versions, Q, SslOpts, [Record|Acc], undefined, undefined, undefined);
true ->
{lists:reverse(Acc),
@@ -514,16 +516,27 @@ validate_tls_record_length(Versions, {_,Size0,_} = Q0, SslOpts, Acc, Type, Versi
end.
-binary_from_front(SplitSize, {Front,Size,Rear}) ->
+binary_from_front(0, Q) ->
+ {<<>>, Q};
+binary_from_front(SplitSize, {Front,Size,Rear}) when SplitSize =< Size ->
binary_from_front(SplitSize, Front, Size, Rear, []).
%%
-binary_from_front(SplitSize, [], Size, [_] = Rear, Acc) ->
- %% Optimize a simple case
- binary_from_front(SplitSize, Rear, Size, [], Acc);
+%% SplitSize > 0 and there is at least SplitSize bytes buffered in Front and Rear
binary_from_front(SplitSize, [], Size, Rear, Acc) ->
- binary_from_front(SplitSize, lists:reverse(Rear), Size, [], Acc);
+ case Rear of
+ %% Avoid lists:reverse/1 for simple cases.
+ %% Case clause for [] to avoid infinite loop.
+ [_] ->
+ binary_from_front(SplitSize, Rear, Size, [], Acc);
+ [Bin2,Bin1] ->
+ binary_from_front(SplitSize, [Bin1,Bin2], Size, [], Acc);
+ [Bin3,Bin2,Bin1] ->
+ binary_from_front(SplitSize, [Bin1,Bin2,Bin3], Size, [], Acc);
+ [_,_,_|_] ->
+ binary_from_front(SplitSize, lists:reverse(Rear), Size, [], Acc)
+ end;
binary_from_front(SplitSize, [Bin|Front], Size, Rear, []) ->
- %% Optimize a frequent case
+ %% Optimize the frequent case when the accumulator is empty
BinSize = byte_size(Bin),
if
SplitSize < BinSize ->
diff --git a/lib/ssl/src/tls_record_1_3.erl b/lib/ssl/src/tls_record_1_3.erl
index 74321a1ae2..d713062284 100644
--- a/lib/ssl/src/tls_record_1_3.erl
+++ b/lib/ssl/src/tls_record_1_3.erl
@@ -138,6 +138,15 @@ decode_cipher_text(#ssl_tls{type = ?ALERT,
{#ssl_tls{type = ?ALERT,
version = {3,4}, %% Internally use real version
fragment = <<2,47>>}, ConnectionStates0};
+%% TLS 1.3 server can receive a User Cancelled Alert when handshake is
+%% paused and then cancelled on the client side.
+decode_cipher_text(#ssl_tls{type = ?ALERT,
+ version = ?LEGACY_VERSION,
+ fragment = <<2,90>>},
+ ConnectionStates0) ->
+ {#ssl_tls{type = ?ALERT,
+ version = {3,4}, %% Internally use real version
+ fragment = <<2,90>>}, ConnectionStates0};
%% RFC8446 - TLS 1.3
%% D.4. Middlebox Compatibility Mode
%% - If not offering early data, the client sends a dummy
diff --git a/lib/ssl/src/tls_socket.erl b/lib/ssl/src/tls_socket.erl
index 6c32e6fa04..acd907905c 100644
--- a/lib/ssl/src/tls_socket.erl
+++ b/lib/ssl/src/tls_socket.erl
@@ -210,9 +210,9 @@ internal_inet_values() ->
default_inet_values() ->
[{packet_size, 0}, {packet,0}, {header, 0}, {active, true}, {mode, list}].
-inherit_tracker(ListenSocket, EmOpts, #ssl_options{erl_dist = false} = SslOpts) ->
+inherit_tracker(ListenSocket, EmOpts, #{erl_dist := false} = SslOpts) ->
ssl_listen_tracker_sup:start_child([ListenSocket, EmOpts, SslOpts]);
-inherit_tracker(ListenSocket, EmOpts, #ssl_options{erl_dist = true} = SslOpts) ->
+inherit_tracker(ListenSocket, EmOpts, #{erl_dist := true} = SslOpts) ->
ssl_listen_tracker_sup:start_child_dist([ListenSocket, EmOpts, SslOpts]).
get_emulated_opts(TrackerPid) ->
diff --git a/lib/ssl/test/Makefile b/lib/ssl/test/Makefile
index dba90aaff0..ec0addac59 100644
--- a/lib/ssl/test/Makefile
+++ b/lib/ssl/test/Makefile
@@ -37,27 +37,41 @@ VSN=$(SSL_VSN)
MODULES = \
ssl_test_lib \
+ ssl_app_env_SUITE\
+ ssl_alert_SUITE\
ssl_bench_test_lib \
ssl_dist_test_lib \
- ssl_alpn_handshake_SUITE \
+ ssl_api_SUITE\
+ tls_api_SUITE\
ssl_basic_SUITE \
ssl_bench_SUITE \
ssl_cipher_SUITE \
ssl_cipher_suite_SUITE \
- openssl_server_cipher_suite_SUITE\
- ssl_certificate_verify_SUITE\
+ openssl_cipher_suite_SUITE\
+ ssl_alpn_SUITE \
+ openssl_alpn_SUITE\
+ ssl_npn_SUITE \
+ openssl_npn_SUITE\
+ openssl_sni_SUITE\
+ ssl_renegotiate_SUITE\
+ openssl_renegotiate_SUITE\
+ openssl_reject_SUITE\
+ ssl_cert_tests\
+ ssl_cert_SUITE\
+ openssl_server_cert_SUITE\
+ openssl_client_cert_SUITE\
ssl_crl_SUITE\
ssl_dist_SUITE \
ssl_dist_bench_SUITE \
ssl_engine_SUITE\
ssl_handshake_SUITE \
ssl_npn_hello_SUITE \
- ssl_npn_handshake_SUITE \
ssl_packet_SUITE \
ssl_payload_SUITE \
ssl_pem_cache_SUITE \
+ ssl_session_SUITE \
ssl_session_cache_SUITE \
- ssl_to_openssl_SUITE \
+ openssl_session_SUITE \
ssl_ECC_SUITE \
ssl_ECC_openssl_SUITE \
ssl_ECC\
@@ -65,6 +79,10 @@ MODULES = \
ssl_sni_SUITE \
ssl_eqc_SUITE \
ssl_rfc_5869_SUITE \
+ tls_1_3_record_SUITE\
+ openssl_tls_1_3_version_SUITE\
+ tls_1_3_version_SUITE\
+ ssl_socket_SUITE\
make_certs \
x509_test \
inet_crypto_dist
diff --git a/lib/ssl/test/openssl_alpn_SUITE.erl b/lib/ssl/test/openssl_alpn_SUITE.erl
new file mode 100644
index 0000000000..a54286c2cd
--- /dev/null
+++ b/lib/ssl/test/openssl_alpn_SUITE.erl
@@ -0,0 +1,421 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2019-2019. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+
+-module(openssl_alpn_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+-define(OPENSSL_QUIT, "Q\n").
+-define(OPENSSL_RENEGOTIATE, "R\n").
+-define(SLEEP, 1000).
+
+%%--------------------------------------------------------------------
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
+
+all() ->
+ %% Note: ALPN not supported in sslv3
+ case ssl_test_lib:openssl_sane_dtls_alpn() of
+ true ->
+ [
+ {group, 'tlsv1.3'},
+ {group, 'tlsv1.2'},
+ {group, 'tlsv1.1'},
+ {group, 'tlsv1'},
+ {group, 'dtlsv1.2'},
+ {group, 'dtlsv1'}];
+ false ->
+ [{group, 'tlsv1.2'},
+ {group, 'tlsv1.1'},
+ {group, 'tlsv1'}]
+ end.
+
+groups() ->
+ case ssl_test_lib:openssl_sane_dtls_alpn() of
+ true ->
+ [
+ {'tlsv1.3', [], alpn_tests()},
+ {'tlsv1.2', [], alpn_tests() ++ alpn_npn_coexist() ++ rengotiation_tests()},
+ {'tlsv1.1', [], alpn_tests() ++ alpn_npn_coexist() ++ rengotiation_tests()},
+ {'tlsv1', [], alpn_tests() ++ alpn_npn_coexist() ++ rengotiation_tests()},
+ {'dtlsv1.2', [], alpn_tests()},
+ {'dtlsv1', [], alpn_tests()}
+ ];
+ false ->
+ [
+ {'tlsv1.3', [], alpn_tests()},
+ {'tlsv1.2', [], alpn_tests() ++ alpn_npn_coexist() ++ rengotiation_tests()},
+ {'tlsv1.1', [], alpn_tests() ++ alpn_npn_coexist() ++ rengotiation_tests()},
+ {'tlsv1', [], alpn_tests() ++ alpn_npn_coexist() ++ rengotiation_tests()}
+ ]
+ end.
+
+alpn_tests() ->
+ [erlang_client_alpn_openssl_server_alpn,
+ erlang_server_alpn_openssl_client_alpn,
+ erlang_client_alpn_openssl_server,
+ erlang_client_openssl_server_alpn,
+ erlang_server_alpn_openssl_client,
+ erlang_server_openssl_client_alpn].
+
+alpn_npn_coexist() ->
+ [
+ erlang_client_alpn_npn_openssl_server_alpn_npn,
+ erlang_server_alpn_npn_openssl_client_alpn_npn
+ ].
+rengotiation_tests() ->
+ [
+ erlang_client_alpn_openssl_server_alpn_renegotiate,
+ erlang_server_alpn_openssl_client_alpn_renegotiate
+ ].
+
+init_per_suite(Config0) ->
+ case os:find_executable("openssl") of
+ false ->
+ {skip, "Openssl not found"};
+ _ ->
+ case check_openssl_alpn_support(Config0) of
+ {skip, _} = Skip ->
+ Skip;
+ _ ->
+ ct:pal("Version: ~p", [os:cmd("openssl version")]),
+ catch crypto:stop(),
+ try crypto:start() of
+ ok ->
+ ssl_test_lib:clean_start(),
+ ssl_test_lib:make_rsa_cert(Config0)
+ catch _:_ ->
+ {skip, "Crypto did not start"}
+ end
+ end
+ end.
+
+end_per_suite(_Config) ->
+ ssl:stop(),
+ application:stop(crypto),
+ ssl_test_lib:kill_openssl().
+
+init_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ case ssl_test_lib:supports_ssl_tls_version(GroupName) of
+ true ->
+ case ssl_test_lib:check_sane_openssl_version(GroupName) of
+ true ->
+ ssl_test_lib:init_tls_version(GroupName, Config);
+ false ->
+ {skip, openssl_does_not_support_version}
+ end;
+ false ->
+ {skip, openssl_does_not_support_version}
+ end;
+ _ ->
+ Config
+ end.
+
+end_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ ssl_test_lib:clean_tls_version(Config);
+ false ->
+ Config
+ end.
+
+init_per_testcase(TestCase, Config) ->
+ ct:timetrap({seconds, 10}),
+ special_init(TestCase, Config).
+
+special_init(erlang_client_alpn_openssl_server_alpn_renegotiate, Config) ->
+ {ok, Version} = application:get_env(ssl, protocol_version),
+ ssl_test_lib:check_sane_openssl_renegotaite(Config, Version);
+special_init(erlang_server_alpn_openssl_client_alpn_renegotiate, Config) ->
+ {ok, Version} = application:get_env(ssl, protocol_version),
+ case ssl_test_lib:check_sane_openssl_renegotaite(Config, Version) of
+ Config ->
+ ssl_test_lib:openssl_allows_client_renegotaite(Config);
+ Skip ->
+ Skip
+ end;
+special_init(_, Config) ->
+ Config.
+
+end_per_testcase(_, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
+
+erlang_client_alpn_openssl_server_alpn(Config) when is_list(Config) ->
+ Data = "From openssl to erlang",
+ start_erlang_client_and_openssl_server_for_alpn_negotiation(Config, Data, fun(Client, OpensslPort) ->
+ true = port_command(OpensslPort, Data),
+ ssl_test_lib:check_result(Client, Data)
+ end).
+
+%%--------------------------------------------------------------------
+
+erlang_server_alpn_openssl_client_alpn(Config) when is_list(Config) ->
+ Data = "From openssl to erlang",
+ start_erlang_server_and_openssl_client_for_alpn_negotiation(Config, Data, fun(Client, OpensslPort) ->
+ true = port_command(OpensslPort, Data),
+ ssl_test_lib:check_result(Client, Data)
+ end).
+
+%%--------------------------------------------------------------------------
+
+erlang_client_alpn_openssl_server(Config) when is_list(Config) ->
+ Data = "From openssl to erlang",
+ ssl_test_lib:start_erlang_client_and_openssl_server_with_opts(Config,
+ [{alpn_advertised_protocols, [<<"spdy/2">>]}],
+ [],
+ Data, fun(Client, OpensslPort) ->
+ true = port_command(OpensslPort, Data),
+ ssl_test_lib:check_result(Client, Data)
+ end).
+
+%%--------------------------------------------------------------------------
+
+erlang_client_openssl_server_alpn(Config) when is_list(Config) ->
+ Data = "From openssl to erlang",
+ ssl_test_lib:start_erlang_client_and_openssl_server_with_opts(Config,
+ [],
+ ["-alpn", "spdy/2"],
+ Data, fun(Client, OpensslPort) ->
+ true = port_command(OpensslPort, Data),
+ ssl_test_lib:check_result(Client, Data)
+ end).
+
+%%--------------------------------------------------------------------------
+
+erlang_server_alpn_openssl_client(Config) when is_list(Config) ->
+ Data = "From openssl to erlang",
+ ssl_test_lib:start_erlang_server_and_openssl_client_with_opts(Config,
+ [{alpn_preferred_protocols, [<<"spdy/2">>]}],
+ [],
+ Data, fun(Server, OpensslPort) ->
+ true = port_command(OpensslPort, Data),
+ ssl_test_lib:check_result(Server, Data)
+ end).
+
+%%--------------------------------------------------------------------------
+
+erlang_server_openssl_client_alpn(Config) when is_list(Config) ->
+ Data = "From openssl to erlang",
+ ssl_test_lib:start_erlang_server_and_openssl_client_with_opts(Config,
+ [],
+ ["-alpn", "spdy/2"],
+ Data, fun(Server, OpensslPort) ->
+ true = port_command(OpensslPort, Data),
+ ssl_test_lib:check_result(Server, Data)
+ end).
+
+%%--------------------------------------------------------------------
+
+erlang_client_alpn_openssl_server_alpn_renegotiate(Config) when is_list(Config) ->
+ Data = "From openssl to erlang",
+ start_erlang_client_and_openssl_server_for_alpn_negotiation(Config, Data, fun(Client, OpensslPort) ->
+ true = port_command(OpensslPort, ?OPENSSL_RENEGOTIATE),
+ ct:sleep(?SLEEP),
+ true = port_command(OpensslPort, Data),
+ ssl_test_lib:check_result(Client, Data)
+ end).
+
+%%--------------------------------------------------------------------
+
+erlang_server_alpn_openssl_client_alpn_renegotiate(Config) when is_list(Config) ->
+ Data = "From openssl to erlang",
+ start_erlang_server_and_openssl_client_for_alpn_negotiation(Config, Data, fun(Server, OpensslPort) ->
+ true = port_command(OpensslPort, ?OPENSSL_RENEGOTIATE),
+ ct:sleep(?SLEEP),
+ true = port_command(OpensslPort, Data),
+ ssl_test_lib:check_result(Server, Data)
+ end).
+
+%%--------------------------------------------------------------------
+
+erlang_client_alpn_npn_openssl_server_alpn_npn(Config) when is_list(Config) ->
+ Data = "From openssl to erlang",
+ start_erlang_client_and_openssl_server_for_alpn_npn_negotiation(Config, Data, fun(Client, OpensslPort) ->
+ true = port_command(OpensslPort, Data),
+ ssl_test_lib:check_result(Client, Data)
+ end).
+
+%%--------------------------------------------------------------------
+
+erlang_server_alpn_npn_openssl_client_alpn_npn(Config) when is_list(Config) ->
+ Data = "From openssl to erlang",
+ start_erlang_server_and_openssl_client_for_alpn_npn_negotiation(Config, Data, fun(Server, OpensslPort) ->
+ true = port_command(OpensslPort, Data),
+ ssl_test_lib:check_result(Server, Data)
+ end).
+
+
+%%--------------------------------------------------------------------
+%% Internal functions -----------------------------------------------
+%%--------------------------------------------------------------------
+check_openssl_alpn_support(Config) ->
+ HelpText = os:cmd("openssl s_client --help"),
+ case string:str(HelpText, "alpn") of
+ 0 ->
+ {skip, "Openssl not compiled with alpn support"};
+ _ ->
+ Config
+ end.
+
+start_erlang_client_and_openssl_server_for_alpn_negotiation(Config, Data, Callback) ->
+ process_flag(trap_exit, true),
+ ServerOpts = proplists:get_value(server_rsa_verify_opts, Config),
+ ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
+ ClientOpts = [{alpn_advertised_protocols, [<<"spdy/2">>]} | ClientOpts0],
+
+ {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "From openssl to erlang",
+
+ Port = ssl_test_lib:inet_port(node()),
+ CaCertFile = proplists:get_value(cacertfile, ServerOpts),
+ CertFile = proplists:get_value(certfile, ServerOpts),
+ KeyFile = proplists:get_value(keyfile, ServerOpts),
+ Version = ssl_test_lib:protocol_version(Config),
+
+ Exe = "openssl",
+ Args = ["s_server", "-msg", "-alpn", "http/1.1,spdy/2", "-accept",
+ integer_to_list(Port), ssl_test_lib:version_flag(Version),
+ "-CAfile", CaCertFile,
+ "-cert", CertFile, "-key", KeyFile],
+ OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
+ ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib,
+ erlang_ssl_receive_and_assert_negotiated_protocol, [<<"spdy/2">>, Data]}},
+ {options, [{reuse_sessions, false} | ClientOpts]}]),
+
+ Callback(Client, OpensslPort),
+
+ %% Clean close down! Server needs to be closed first !!
+ ssl_test_lib:close_port(OpensslPort),
+
+ ssl_test_lib:close(Client),
+ process_flag(trap_exit, false).
+
+start_erlang_server_and_openssl_client_for_alpn_negotiation(Config, Data, Callback) ->
+ process_flag(trap_exit, true),
+ ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ ServerOpts = [{alpn_preferred_protocols, [<<"spdy/2">>]} | ServerOpts0],
+
+ {_, ServerNode, _} = ssl_test_lib:run_where(Config),
+
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, erlang_ssl_receive_and_assert_negotiated_protocol, [<<"spdy/2">>, Data]}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Version = ssl_test_lib:protocol_version(Config),
+
+ Exe = "openssl",
+ Args = ["s_client", "-alpn", "http/1.0,spdy/2", "-msg", "-port",
+ integer_to_list(Port), ssl_test_lib:version_flag(Version),
+ "-host", "localhost"],
+
+ OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
+
+ Callback(Server, OpenSslPort),
+
+ ssl_test_lib:close(Server),
+
+ ssl_test_lib:close_port(OpenSslPort),
+ process_flag(trap_exit, false).
+
+start_erlang_client_and_openssl_server_for_alpn_npn_negotiation(Config, Data, Callback) ->
+ process_flag(trap_exit, true),
+ ServerOpts = proplists:get_value(server_rsa_verify_opts, Config),
+ ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ClientOpts = [{alpn_advertised_protocols, [<<"spdy/2">>]},
+ {client_preferred_next_protocols, {client, [<<"spdy/3">>, <<"http/1.1">>]}} | ClientOpts0],
+
+ {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "From openssl to erlang",
+
+ Port = ssl_test_lib:inet_port(node()),
+ CertFile = proplists:get_value(certfile, ServerOpts),
+ KeyFile = proplists:get_value(keyfile, ServerOpts),
+ Version = ssl_test_lib:protocol_version(Config),
+
+ Exe = "openssl",
+ Args = ["s_server", "-msg", "-alpn", "http/1.1,spdy/2", "-nextprotoneg",
+ "spdy/3", "-accept", integer_to_list(Port), ssl_test_lib:version_flag(Version),
+ "-cert", CertFile, "-key", KeyFile],
+
+ OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
+
+ ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib,
+ erlang_ssl_receive_and_assert_negotiated_protocol, [<<"spdy/2">>, Data]}},
+ {options, [{reuse_sessions, false} | ClientOpts]}]),
+
+ Callback(Client, OpensslPort),
+
+ %% Clean close down! Server needs to be closed first !!
+ ssl_test_lib:close_port(OpensslPort),
+
+ ssl_test_lib:close(Client),
+ process_flag(trap_exit, false).
+
+start_erlang_server_and_openssl_client_for_alpn_npn_negotiation(Config, Data, Callback) ->
+ process_flag(trap_exit, true),
+ ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
+ ServerOpts = [{alpn_preferred_protocols, [<<"spdy/2">>]},
+ {next_protocols_advertised, [<<"spdy/3">>, <<"http/1.1">>]} | ServerOpts0],
+
+ {_, ServerNode, _} = ssl_test_lib:run_where(Config),
+
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, erlang_ssl_receive_and_assert_negotiated_protocol, [<<"spdy/2">>, Data]}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Version = ssl_test_lib:protocol_version(Config),
+ Exe = "openssl",
+ Args = ["s_client", "-alpn", "http/1.1,spdy/2", "-nextprotoneg", "spdy/3",
+ "-msg", "-port", integer_to_list(Port), ssl_test_lib:version_flag(Version),
+ "-host", "localhost"],
+ OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
+
+ Callback(Server, OpenSslPort),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close_port(OpenSslPort),
+ process_flag(trap_exit, false).
+
diff --git a/lib/ssl/test/openssl_server_cipher_suite_SUITE.erl b/lib/ssl/test/openssl_cipher_suite_SUITE.erl
index 0d68d84d61..955eb914c0 100644
--- a/lib/ssl/test/openssl_server_cipher_suite_SUITE.erl
+++ b/lib/ssl/test/openssl_cipher_suite_SUITE.erl
@@ -20,7 +20,7 @@
%%
--module(openssl_server_cipher_suite_SUITE).
+-module(openssl_cipher_suite_SUITE).
%% Note: This directive should only be used in test suites.
-compile(export_all).
@@ -31,19 +31,26 @@
%% Common Test interface functions -----------------------------------
%%--------------------------------------------------------------------
all() ->
- [
- {group, 'tlsv1.2'},
+ [
+ {group, openssl_server},
+ {group, openssl_client}
+ ].
+
+all_protocol_groups() ->
+ [{group, 'tlsv1.2'},
{group, 'tlsv1.1'},
{group, 'tlsv1'},
{group, 'sslv3'},
{group, 'dtlsv1.2'},
{group, 'dtlsv1'}
- ].
+ ].
groups() ->
%% TODO: Enable SRP, PSK suites (needs OpenSSL s_server conf)
%% TODO: Enable all "kex" on DTLS
[
+ {openssl_server, all_protocol_groups()},
+ {openssl_client, all_protocol_groups()},
{'tlsv1.2', [], kex()},
{'tlsv1.1', [], kex()},
{'tlsv1', [], kex()},
@@ -135,6 +142,9 @@ kex() ->
dtls_kex() -> %% Should be all kex in the future
dtls_rsa() ++ dss() ++ anonymous().
+ssl3_kex() ->
+ ssl3_rsa() ++ ssl3_dss() ++ ssl3_anonymous().
+
rsa() ->
[{group, dhe_rsa},
{group, ecdhe_rsa},
@@ -148,6 +158,11 @@ dtls_rsa() ->
%%,{group, rsa_psk}
].
+ssl3_rsa() ->
+ [{group, dhe_rsa},
+ {group, rsa}
+ ].
+
ecdsa() ->
[{group, ecdhe_ecdsa}].
@@ -156,6 +171,10 @@ dss() ->
%%{group, srp_dss}
].
+ssl3_dss() ->
+ [{group, dhe_dss}
+ ].
+
anonymous() ->
[{group, dh_anon},
{group, ecdh_anon}
@@ -165,6 +184,9 @@ anonymous() ->
%%{group, srp_anon}
].
+ssl3_anonymous() ->
+ [{group, dh_anon}].
+
init_per_suite(Config) ->
catch crypto:stop(),
try crypto:start() of
@@ -199,7 +221,12 @@ init_per_group(GroupName, Config) ->
false ->
do_init_per_group(GroupName, Config)
end.
-
+do_init_per_group(openssl_client, Config0) ->
+ Config = proplists:delete(server_type, proplists:delete(client_type, Config0)),
+ [{client_type, openssl}, {server_type, erlang} | Config];
+do_init_per_group(openssl_server, Config0) ->
+ Config = proplists:delete(server_type, proplists:delete(client_type, Config0)),
+ [{client_type, erlang}, {server_type, openssl} | Config];
do_init_per_group(GroupName, Config) when GroupName == ecdh_anon;
GroupName == ecdhe_rsa;
GroupName == ecdhe_psk ->
@@ -747,8 +774,7 @@ cipher_suite_test(CipherSuite, _Version, Config) ->
ct:log("Testing CipherSuite ~p~n", [CipherSuite]),
ct:log("Server Opts ~p~n", [ServerOpts]),
ct:log("Client Opts ~p~n", [ClientOpts]),
- ssl_test_lib:basic_test([{ciphers, [CipherSuite]} | COpts], SOpts, [{client_type, erlang},
- {server_type, openssl} | Config]).
+ ssl_test_lib:basic_test([{ciphers, [CipherSuite]} | COpts], SOpts, Config).
test_ciphers(Kex, Cipher, Version) ->
diff --git a/lib/ssl/test/openssl_client_cert_SUITE.erl b/lib/ssl/test/openssl_client_cert_SUITE.erl
new file mode 100644
index 0000000000..b327988744
--- /dev/null
+++ b/lib/ssl/test/openssl_client_cert_SUITE.erl
@@ -0,0 +1,350 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2019-2019. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(openssl_client_cert_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+-include_lib("common_test/include/ct.hrl").
+-include_lib("public_key/include/public_key.hrl").
+
+%%--------------------------------------------------------------------
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
+all() ->
+ [
+ {group, openssl_client}
+ ].
+
+groups() ->
+ [
+ {openssl_client, [], protocol_groups()},
+ {'tlsv1.3', [], tls_1_3_protocol_groups()},
+ {'tlsv1.2', [], pre_tls_1_3_protocol_groups()},
+ {'tlsv1.1', [], pre_tls_1_3_protocol_groups()},
+ {'tlsv1', [], pre_tls_1_3_protocol_groups()},
+ {'sslv3', [], ssl_protocol_groups()},
+ {'dtlsv1.2', [], pre_tls_1_3_protocol_groups()},
+ {'dtlsv1', [], pre_tls_1_3_protocol_groups()},
+ {rsa, [], all_version_tests()},
+ {ecdsa, [], all_version_tests()},
+ {dsa, [], all_version_tests()},
+ {rsa_1_3, [], all_version_tests() ++ tls_1_3_tests() ++ [unsupported_sign_algo_client_auth,
+ unsupported_sign_algo_cert_client_auth]},
+ {ecdsa_1_3, [], all_version_tests() ++ tls_1_3_tests()}
+ ].
+
+protocol_groups() ->
+ [{group, 'tlsv1.3'},
+ {group, 'tlsv1.2'},
+ {group, 'tlsv1.1'},
+ {group, 'tlsv1'},
+ {group, 'sslv3'},
+ {group, 'dtlsv1.2'},
+ {group, 'dtlsv1'}
+ ].
+
+ssl_protocol_groups() ->
+ [{group, rsa},
+ {group, dsa}].
+
+pre_tls_1_3_protocol_groups() ->
+ [{group, rsa},
+ {group, ecdsa},
+ {group, dsa}].
+
+tls_1_3_protocol_groups() ->
+ [{group, rsa_1_3},
+ {group, ecdsa_1_3}].
+
+tls_1_3_tests() ->
+ [
+ hello_retry_request,
+ custom_groups,
+ hello_retry_client_auth,
+ hello_retry_client_auth_empty_cert_accepted,
+ hello_retry_client_auth_empty_cert_rejected
+ ].
+
+all_version_tests() ->
+ [
+ no_auth,
+ auth,
+ client_auth_empty_cert_accepted,
+ client_auth_empty_cert_rejected,
+ client_auth_partial_chain,
+ client_auth_allow_partial_chain,
+ client_auth_do_not_allow_partial_chain,
+ client_auth_partial_chain_fun_fail,
+ missing_root_cert_no_auth
+ %%invalid_signature_client
+ ].
+
+init_per_suite(Config) ->
+ catch crypto:stop(),
+ try crypto:start() of
+ ok ->
+ ssl_test_lib:clean_start(),
+ Config
+ catch _:_ ->
+ {skip, "Crypto did not start"}
+ end.
+
+end_per_suite(_Config) ->
+ ssl:stop(),
+ application:unload(ssl),
+ application:stop(crypto).
+
+init_per_group(openssl_client, Config0) ->
+ Config = proplists:delete(server_type, proplists:delete(client_type, Config0)),
+ [{client_type, openssl}, {server_type, erlang} | Config];
+init_per_group(Group, Config0) when Group == rsa;
+ Group == rsa_1_3 ->
+ Config = ssl_test_lib:make_rsa_cert(Config0),
+ COpts = proplists:get_value(client_rsa_opts, Config),
+ SOpts = proplists:get_value(server_rsa_opts, Config),
+ %% Make sure _rsa* suite is choosen by ssl_test_lib:start_server
+ Version = proplists:get_value(version,Config),
+ Ciphers = ssl_cert_tests:test_ciphers(fun(dhe_rsa) ->
+ true;
+ (ecdhe_rsa) ->
+ true;
+ (_) ->
+ false
+ end, Version),
+ case Ciphers of
+ [_|_] ->
+ [{cert_key_alg, rsa} |
+ lists:delete(cert_key_alg,
+ [{client_cert_opts, [{ciphers, Ciphers} | COpts]},
+ {server_cert_opts, SOpts} |
+ lists:delete(server_cert_opts,
+ lists:delete(client_cert_opts, Config))])];
+ [] ->
+ {skip, {no_sup, Group, Version}}
+ end;
+init_per_group(Group, Config0) when Group == ecdsa;
+ Group == ecdsa_1_3 ->
+ PKAlg = crypto:supports(public_keys),
+ case lists:member(ecdsa, PKAlg) andalso (lists:member(ecdh, PKAlg) orelse
+ lists:member(dh, PKAlg)) of
+ true ->
+ Config = ssl_test_lib:make_ecdsa_cert(Config0),
+ COpts = proplists:get_value(client_ecdsa_opts, Config),
+ SOpts = proplists:get_value(server_ecdsa_opts, Config),
+ %% Make sure ecdh* suite is choosen by ssl_test_lib:start_server
+ Version = proplists:get_value(version,Config),
+ Ciphers = ssl_cert_tests:test_ciphers(fun(ecdh_ecdsa) ->
+ true;
+ (ecdhe_ecdsa) ->
+ true;
+ (_) ->
+ false
+ end, Version),
+ case Ciphers of
+ [_|_] ->
+ [{cert_key_alg, ecdsa} |
+ lists:delete(cert_key_alg,
+ [{client_cert_opts, [{ciphers, Ciphers} | COpts]},
+ {server_cert_opts, SOpts} |
+ lists:delete(server_cert_opts,
+ lists:delete(client_cert_opts, Config))]
+ )];
+ [] ->
+ {skip, {no_sup, Group, Version}}
+ end;
+ false ->
+ {skip, "Missing EC crypto support"}
+ end;
+init_per_group(Group, Config0) when Group == dsa ->
+ PKAlg = crypto:supports(public_keys),
+ case lists:member(dss, PKAlg) andalso lists:member(dh, PKAlg) of
+ true ->
+ Config = ssl_test_lib:make_dsa_cert(Config0),
+ COpts = proplists:get_value(client_dsa_opts, Config),
+ SOpts = proplists:get_value(server_dsa_opts, Config),
+ %% Make sure dhe_dss* suite is choosen by ssl_test_lib:start_server
+ Version = proplists:get_value(version,Config),
+ Ciphers = ssl_cert_tests:test_ciphers(fun(dh_dss) ->
+ true;
+ (dhe_dss) ->
+ true;
+ (_) ->
+ false
+ end, Version),
+ case Ciphers of
+ [_|_] ->
+ [{cert_key_alg, dsa} |
+ lists:delete(cert_key_alg,
+ [{client_cert_opts, [{ciphers, Ciphers} | COpts]},
+ {server_cert_opts, SOpts} |
+ lists:delete(server_cert_opts,
+ lists:delete(client_cert_opts, Config))])];
+ [] ->
+ {skip, {no_sup, Group, Version}}
+ end;
+ false ->
+ {skip, "Missing DSS crypto support"}
+ end;
+init_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ case ssl_test_lib:check_sane_openssl_version(GroupName) of
+ true ->
+ [{version, GroupName}
+ | ssl_test_lib:init_tls_version(GroupName, Config)];
+ false ->
+ {skip, "Missing openssl support"}
+ end;
+ _ ->
+ ssl:start(),
+ Config
+ end.
+
+end_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ ssl_test_lib:clean_tls_version(Config);
+ false ->
+ Config
+ end.
+init_per_testcase(TestCase, Config) when
+ TestCase == client_auth_empty_cert_accepted;
+ TestCase == client_auth_empty_cert_rejected ->
+ Version = proplists:get_value(version,Config),
+ case Version of
+ sslv3 ->
+ %% Openssl client sends "No Certificate Reserved" warning ALERT
+ %% instead of sending EMPTY cert message in SSL-3.0 so empty cert test are not
+ %% relevant
+ {skip, openssl_behaves_differently};
+ _ ->
+ ssl_test_lib:ct_log_supported_protocol_versions(Config),
+ ct:timetrap({seconds, 10}),
+ Config
+ end;
+init_per_testcase(_TestCase, Config) ->
+ ssl_test_lib:ct_log_supported_protocol_versions(Config),
+ ct:timetrap({seconds, 10}),
+ Config.
+
+end_per_testcase(_TestCase, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
+
+no_auth() ->
+ ssl_cert_tests:no_auth().
+
+no_auth(Config) ->
+ ssl_cert_tests:no_auth(Config).
+%%--------------------------------------------------------------------
+auth() ->
+ ssl_cert_tests:auth().
+auth(Config) ->
+ ssl_cert_tests:auth(Config).
+%%--------------------------------------------------------------------
+client_auth_empty_cert_accepted() ->
+ ssl_cert_tests:client_auth_empty_cert_accepted().
+client_auth_empty_cert_accepted(Config) ->
+ ssl_cert_tests:client_auth_empty_cert_accepted(Config).
+%%--------------------------------------------------------------------
+client_auth_empty_cert_rejected() ->
+ ssl_cert_tests:client_auth_empty_cert_rejected().
+client_auth_empty_cert_rejected(Config) ->
+ ssl_cert_tests:client_auth_empty_cert_rejected(Config).
+%%--------------------------------------------------------------------
+client_auth_partial_chain() ->
+ ssl_cert_tests:client_auth_partial_chain().
+client_auth_partial_chain(Config) when is_list(Config) ->
+ ssl_cert_tests:client_auth_partial_chain(Config).
+
+%%--------------------------------------------------------------------
+client_auth_allow_partial_chain() ->
+ ssl_cert_tests:client_auth_allow_partial_chain().
+client_auth_allow_partial_chain(Config) when is_list(Config) ->
+ ssl_cert_tests:client_auth_allow_partial_chain(Config).
+%%--------------------------------------------------------------------
+client_auth_do_not_allow_partial_chain() ->
+ ssl_cert_tests:client_auth_do_not_allow_partial_chain().
+client_auth_do_not_allow_partial_chain(Config) when is_list(Config) ->
+ ssl_cert_tests:client_auth_do_not_allow_partial_chain(Config).
+
+%%--------------------------------------------------------------------
+client_auth_partial_chain_fun_fail() ->
+ ssl_cert_tests:client_auth_partial_chain_fun_fail().
+client_auth_partial_chain_fun_fail(Config) when is_list(Config) ->
+ ssl_cert_tests:client_auth_partial_chain_fun_fail(Config).
+
+%%--------------------------------------------------------------------
+missing_root_cert_no_auth() ->
+ ssl_cert_tests:missing_root_cert_no_auth().
+missing_root_cert_no_auth(Config) when is_list(Config) ->
+ ssl_cert_tests:missing_root_cert_no_auth(Config).
+
+%%--------------------------------------------------------------------
+invalid_signature_client() ->
+ ssl_cert_tests:invalid_signature_client().
+invalid_signature_client(Config) when is_list(Config) ->
+ ssl_cert_tests:invalid_signature_client(Config).
+%%--------------------------------------------------------------------
+invalid_signature_server() ->
+ ssl_cert_tests:invalid_signature_client().
+invalid_signature_server(Config) when is_list(Config) ->
+ ssl_cert_tests:invalid_signature_client(Config).
+
+%%--------------------------------------------------------------------
+%% TLS 1.3 Test Cases ------------------------------------------------
+%%--------------------------------------------------------------------
+hello_retry_request() ->
+ ssl_cert_tests:hello_retry_request().
+hello_retry_request(Config) ->
+ ssl_cert_tests:hello_retry_request(Config).
+%%--------------------------------------------------------------------
+custom_groups() ->
+ ssl_cert_tests:custom_groups().
+custom_groups(Config) ->
+ ssl_cert_tests:custom_groups(Config).
+unsupported_sign_algo_cert_client_auth() ->
+ ssl_cert_tests:unsupported_sign_algo_cert_client_auth().
+unsupported_sign_algo_cert_client_auth(Config) ->
+ ssl_cert_tests:unsupported_sign_algo_cert_client_auth(Config).
+unsupported_sign_algo_client_auth() ->
+ ssl_cert_tests:unsupported_sign_algo_client_auth().
+unsupported_sign_algo_client_auth(Config) ->
+ ssl_cert_tests:unsupported_sign_algo_client_auth(Config).
+%%--------------------------------------------------------------------
+hello_retry_client_auth() ->
+ ssl_cert_tests:hello_retry_client_auth().
+hello_retry_client_auth(Config) ->
+ ssl_cert_tests:hello_retry_client_auth(Config).
+%%--------------------------------------------------------------------
+hello_retry_client_auth_empty_cert_accepted() ->
+ ssl_cert_tests:hello_retry_client_auth_empty_cert_accepted().
+hello_retry_client_auth_empty_cert_accepted(Config) ->
+ ssl_cert_tests:hello_retry_client_auth_empty_cert_accepted(Config).
+%%--------------------------------------------------------------------
+hello_retry_client_auth_empty_cert_rejected() ->
+ ssl_cert_tests:hello_retry_client_auth_empty_cert_rejected().
+hello_retry_client_auth_empty_cert_rejected(Config) ->
+ ssl_cert_tests:hello_retry_client_auth_empty_cert_rejected(Config).
diff --git a/lib/ssl/test/openssl_npn_SUITE.erl b/lib/ssl/test/openssl_npn_SUITE.erl
new file mode 100644
index 0000000000..ed3d81eba7
--- /dev/null
+++ b/lib/ssl/test/openssl_npn_SUITE.erl
@@ -0,0 +1,314 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2019-2019. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+
+-module(openssl_npn_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+-define(OPENSSL_QUIT, "Q\n").
+-define(OPENSSL_RENEGOTIATE, "R\n").
+-define(SLEEP, 1000).
+
+%%--------------------------------------------------------------------
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
+all() ->
+ %% NPN is not supported in TLS-1.3 (replaced by ALPN and deprecated in TLS 1.2)
+ %% OpenSSL DTLS support for NPN is either not there or broken.
+ [{group, 'tlsv1.2'},
+ {group, 'tlsv1.1'},
+ {group, 'tlsv1'}].
+
+groups() ->
+ [{'tlsv1.2', [], npn_tests() ++ npn_renegotiate_tests()},
+ {'tlsv1.1', [], npn_tests() ++ npn_renegotiate_tests()},
+ {'tlsv1', [], npn_tests() ++ npn_renegotiate_tests()}
+ ].
+
+npn_tests() ->
+ [erlang_client_openssl_server_npn,
+ erlang_server_openssl_client_npn,
+ erlang_server_openssl_client_npn_only_client,
+ erlang_server_openssl_client_npn_only_server,
+ erlang_client_openssl_server_npn_only_client,
+ erlang_client_openssl_server_npn_only_server].
+
+npn_renegotiate_tests() ->
+ [
+ erlang_server_openssl_client_npn_renegotiate,
+ erlang_client_openssl_server_npn_renegotiate
+ ].
+
+init_per_suite(Config0) ->
+ case os:find_executable("openssl") of
+ false ->
+ {skip, "Openssl not found"};
+ _ ->
+ case check_openssl_npn_support(Config0) of
+ {skip, _} = Skip ->
+ Skip;
+ _ ->
+ ct:pal("Version: ~p", [os:cmd("openssl version")]),
+ catch crypto:stop(),
+ try crypto:start() of
+ ok ->
+ ssl_test_lib:clean_start(),
+ ssl_test_lib:make_rsa_cert(Config0)
+ catch _:_ ->
+ {skip, "Crypto did not start"}
+ end
+ end
+ end.
+
+end_per_suite(_Config) ->
+ ssl:stop(),
+ application:stop(crypto),
+ ssl_test_lib:kill_openssl().
+
+init_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ case ssl_test_lib:supports_ssl_tls_version(GroupName) of
+ true ->
+ case ssl_test_lib:check_sane_openssl_version(GroupName) of
+ true ->
+ ssl_test_lib:init_tls_version(GroupName, Config);
+ false ->
+ {skip, openssl_does_not_support_version}
+ end;
+ false ->
+ {skip, openssl_does_not_support_version}
+ end;
+ _ ->
+ Config
+ end.
+
+end_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ ssl_test_lib:clean_tls_version(Config);
+ false ->
+ Config
+ end.
+
+init_per_testcase(TestCase, Config) ->
+ ct:timetrap({seconds, 10}),
+ special_init(TestCase, Config).
+
+special_init(erlang_client_openssl_server_npn_renegotiate, Config) ->
+ {ok, Version} = application:get_env(ssl, protocol_version),
+ ssl_test_lib:check_sane_openssl_renegotaite(Config, Version);
+special_init(erlang_server_openssl_client_npn_renegotiate, Config) ->
+ {ok, Version} = application:get_env(ssl, protocol_version),
+ case ssl_test_lib:check_sane_openssl_renegotaite(Config, Version) of
+ Config ->
+ ssl_test_lib:openssl_allows_client_renegotaite(Config);
+ Skip ->
+ Skip
+ end;
+special_init(_, Config) ->
+ Config.
+
+end_per_testcase(_, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
+
+erlang_client_openssl_server_npn() ->
+ [{doc,"Test erlang client with openssl server doing npn negotiation"}].
+
+erlang_client_openssl_server_npn(Config) when is_list(Config) ->
+ Data = "From openssl to erlang",
+ start_erlang_client_and_openssl_server_for_npn_negotiation(Config, Data,
+ fun(Client, OpensslPort) ->
+ true = port_command(OpensslPort, Data),
+ ssl_test_lib:check_result(Client, Data)
+ end).
+
+%%--------------------------------------------------------------------
+erlang_client_openssl_server_npn_renegotiate() ->
+ [{doc,"Test erlang client with openssl server doing npn negotiation and renegotiate"}].
+
+erlang_client_openssl_server_npn_renegotiate(Config) when is_list(Config) ->
+ Data = "From openssl to erlang",
+ start_erlang_client_and_openssl_server_for_npn_negotiation(Config, Data,
+ fun(Client, OpensslPort) ->
+ true = port_command(OpensslPort,
+ ?OPENSSL_RENEGOTIATE),
+ ct:sleep(?SLEEP),
+ true = port_command(OpensslPort, Data),
+ ssl_test_lib:check_result(Client, Data)
+ end).
+%%--------------------------------------------------------------------------
+erlang_server_openssl_client_npn() ->
+ [{doc,"Test erlang server with openssl client and npn negotiation"}].
+
+erlang_server_openssl_client_npn(Config) when is_list(Config) ->
+
+ Data = "From openssl to erlang",
+ start_erlang_server_and_openssl_client_for_npn_negotiation(Config, Data,
+ fun(Server, OpensslPort) ->
+ true = port_command(OpensslPort, Data),
+ ssl_test_lib:check_result(Server, Data)
+ end).
+
+%%--------------------------------------------------------------------------
+erlang_server_openssl_client_npn_renegotiate() ->
+ [{doc,"Test erlang server with openssl client and npn negotiation with renegotiation"}].
+
+erlang_server_openssl_client_npn_renegotiate(Config) when is_list(Config) ->
+ Data = "From openssl to erlang",
+ start_erlang_server_and_openssl_client_for_npn_negotiation(Config, Data,
+ fun(Server, OpensslPort) ->
+ true = port_command(OpensslPort,
+ ?OPENSSL_RENEGOTIATE),
+ ct:sleep(?SLEEP),
+ true = port_command(OpensslPort, Data),
+ ssl_test_lib:check_result(Server, Data)
+ end).
+%%--------------------------------------------------------------------------
+erlang_client_openssl_server_npn_only_server(Config) when is_list(Config) ->
+ Data = "From openssl to erlang",
+ ssl_test_lib:start_erlang_client_and_openssl_server_with_opts(Config, [],
+ ["-nextprotoneg", "spdy/2"], Data,
+ fun(Server, OpensslPort) ->
+ true = port_command(OpensslPort, Data),
+ ssl_test_lib:check_result(Server, Data)
+ end).
+
+%%--------------------------------------------------------------------------
+
+erlang_client_openssl_server_npn_only_client(Config) when is_list(Config) ->
+ Data = "From openssl to erlang",
+ ssl_test_lib:start_erlang_client_and_openssl_server_with_opts(Config,
+ [{client_preferred_next_protocols,
+ {client, [<<"spdy/2">>], <<"http/1.1">>}}], [],
+ Data,
+ fun(Server, OpensslPort) ->
+ true = port_command(OpensslPort, Data),
+ ssl_test_lib:check_result(Server, Data)
+ end).
+
+%%--------------------------------------------------------------------------
+erlang_server_openssl_client_npn_only_server(Config) when is_list(Config) ->
+ Data = "From openssl to erlang",
+ ssl_test_lib:start_erlang_server_and_openssl_client_with_opts(Config,
+ [{next_protocols_advertised, [<<"spdy/2">>]}], [],
+ Data,
+ fun(Server, OpensslPort) ->
+ true = port_command(OpensslPort, Data),
+ ssl_test_lib:check_result(Server, Data)
+ end).
+
+erlang_server_openssl_client_npn_only_client(Config) when is_list(Config) ->
+ Data = "From openssl to erlang",
+ ssl_test_lib:start_erlang_server_and_openssl_client_with_opts(Config, [], ["-nextprotoneg", "spdy/2"],
+ Data,
+ fun(Server, OpensslPort) ->
+ true = port_command(OpensslPort, Data),
+ ssl_test_lib:check_result(Server, Data)
+ end).
+
+%%--------------------------------------------------------------------
+%% Internal functions -----------------------------------------------
+%%--------------------------------------------------------------------
+
+start_erlang_client_and_openssl_server_for_npn_negotiation(Config, Data, Callback) ->
+ process_flag(trap_exit, true),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
+ ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
+ ClientOpts = [{client_preferred_next_protocols, {client, [<<"spdy/2">>], <<"http/1.1">>}} | ClientOpts0],
+
+ {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "From openssl to erlang",
+
+ Port = ssl_test_lib:inet_port(node()),
+ CaCertFile = proplists:get_value(cacertfile, ServerOpts),
+ CertFile = proplists:get_value(certfile, ServerOpts),
+ KeyFile = proplists:get_value(keyfile, ServerOpts),
+ Version = ssl_test_lib:protocol_version(Config),
+
+ Exe = "openssl",
+ Args = ["s_server", "-msg", "-nextprotoneg", "http/1.1,spdy/2", "-accept", integer_to_list(Port),
+ ssl_test_lib:version_flag(Version),
+ "-CAfile", CaCertFile,
+ "-cert", CertFile, "-key", KeyFile],
+ OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
+
+ ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib,
+ erlang_ssl_receive_and_assert_negotiated_protocol, [<<"spdy/2">>, Data]}},
+ {options, ClientOpts}]),
+
+ Callback(Client, OpensslPort),
+
+ %% Clean close down! Server needs to be closed first !!
+ ssl_test_lib:close_port(OpensslPort),
+
+ ssl_test_lib:close(Client),
+ process_flag(trap_exit, false).
+
+start_erlang_server_and_openssl_client_for_npn_negotiation(Config, Data, Callback) ->
+ process_flag(trap_exit, true),
+ ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ ServerOpts = [{next_protocols_advertised, [<<"spdy/2">>]} | ServerOpts0],
+
+ {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, erlang_ssl_receive_and_assert_negotiated_protocol, [<<"spdy/2">>, Data]}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Version = ssl_test_lib:protocol_version(Config),
+
+ Exe = "openssl",
+ Args = ["s_client", "-nextprotoneg", "http/1.0,spdy/2", "-msg", "-connect",
+ ssl_test_lib:hostname_format(Hostname) ++ ":"
+ ++ integer_to_list(Port), ssl_test_lib:version_flag(Version)],
+
+ OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
+
+ Callback(Server, OpenSslPort),
+
+ ssl_test_lib:close(Server),
+
+ ssl_test_lib:close_port(OpenSslPort),
+ process_flag(trap_exit, false).
+
+check_openssl_npn_support(Config) ->
+ HelpText = os:cmd("openssl s_client --help"),
+ case string:str(HelpText, "nextprotoneg") of
+ 0 ->
+ {skip, "Openssl not compiled with nextprotoneg support"};
+ _ ->
+ Config
+ end.
diff --git a/lib/ssl/test/openssl_reject_SUITE.erl b/lib/ssl/test/openssl_reject_SUITE.erl
new file mode 100644
index 0000000000..deefd11823
--- /dev/null
+++ b/lib/ssl/test/openssl_reject_SUITE.erl
@@ -0,0 +1,209 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2019-2019. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+
+-module(openssl_reject_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+-define(SLEEP, 1000).
+-define(OPENSSL_GARBAGE, "P\n").
+
+%%--------------------------------------------------------------------
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
+
+all() ->
+ [{group, 'tlsv1.2'},
+ {group, 'tlsv1.1'},
+ {group, 'tlsv1'},
+ {group, 'sslv3'}].
+
+groups() ->
+ [{'tlsv1.2', [], all_versions_tests()},
+ {'tlsv1.1', [], all_versions_tests()},
+ {'tlsv1', [], all_versions_tests()},
+ {'sslv3', [], all_versions_tests()}
+ ].
+
+all_versions_tests() ->
+ [
+ erlang_client_bad_openssl_server,
+ ssl2_erlang_server_openssl_client
+ ].
+
+init_per_suite(Config0) ->
+ case os:find_executable("openssl") of
+ false ->
+ {skip, "Openssl not found"};
+ _ ->
+ ct:pal("Version: ~p", [os:cmd("openssl version")]),
+ catch crypto:stop(),
+ try crypto:start() of
+ ok ->
+ ssl_test_lib:clean_start(),
+ ssl_test_lib:make_rsa_cert(Config0)
+ catch _:_ ->
+ {skip, "Crypto did not start"}
+ end
+ end.
+
+end_per_suite(_Config) ->
+ ssl:stop(),
+ application:stop(crypto),
+ ssl_test_lib:kill_openssl().
+
+init_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ case ssl_test_lib:supports_ssl_tls_version(GroupName) of
+ true ->
+ case ssl_test_lib:check_sane_openssl_version(GroupName) of
+ true ->
+ ssl_test_lib:init_tls_version(GroupName, Config);
+ false ->
+ {skip, openssl_does_not_support_version}
+ end;
+ false ->
+ {skip, openssl_does_not_support_version}
+ end;
+ _ ->
+ Config
+ end.
+
+end_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ ssl_test_lib:clean_tls_version(Config);
+ false ->
+ Config
+ end.
+
+init_per_testcase(TestCase, Config) ->
+ ct:timetrap({seconds, 10}),
+ special_init(TestCase, Config).
+
+special_init(ssl2_erlang_server_openssl_client, Config) ->
+ case ssl_test_lib:supports_ssl_tls_version(sslv2) of
+ true ->
+ Config;
+ false ->
+ {skip, "sslv2 not supported by openssl"}
+ end;
+special_init(_, Config) ->
+ Config.
+
+end_per_testcase(_, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
+erlang_client_bad_openssl_server() ->
+ [{doc,"Test what happens if openssl server sends garbage to erlang ssl client"}].
+erlang_client_bad_openssl_server(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+
+ {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
+
+ Port = ssl_test_lib:inet_port(node()),
+ CertFile = proplists:get_value(certfile, ServerOpts),
+ KeyFile = proplists:get_value(keyfile, ServerOpts),
+ Version = ssl_test_lib:protocol_version(Config),
+ Exe = "openssl",
+ Args = ["s_server", "-accept", integer_to_list(Port), ssl_test_lib:version_flag(Version),
+ "-cert", CertFile, "-key", KeyFile],
+ OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
+
+ ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)),
+
+ Client0 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, server_sent_garbage, []}},
+ {options,
+ [{versions, [Version]} | ClientOpts]}]),
+
+ %% Send garbage
+ true = port_command(OpensslPort, ?OPENSSL_GARBAGE),
+
+ ct:sleep(?SLEEP),
+
+ Client0 ! server_sent_garbage,
+
+ ssl_test_lib:check_result(Client0, true),
+
+ ssl_test_lib:close(Client0),
+
+ %% Make sure openssl does not hang and leave zombie process
+ Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {options,
+ [{versions, [Version]} | ClientOpts]}]),
+
+ %% Clean close down! Server needs to be closed first !!
+ ssl_test_lib:close_port(OpensslPort),
+ ssl_test_lib:close(Client1),
+ process_flag(trap_exit, false).
+
+%%--------------------------------------------------------------------
+ssl2_erlang_server_openssl_client() ->
+ [{doc,"Test that ssl v2 clients are rejected"}].
+
+ssl2_erlang_server_openssl_client(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+
+ {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ Exe = "openssl",
+ Args = ["s_client", "-connect", ssl_test_lib:hostname_format(Hostname) ++ ":" ++ integer_to_list(Port),
+ "-ssl2", "-msg"],
+
+ OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
+
+ ct:log("Ports ~p~n", [[erlang:port_info(P) || P <- erlang:ports()]]),
+ ssl_test_lib:consume_port_exit(OpenSslPort),
+ ssl_test_lib:check_server_alert(Server, unexpected_message),
+ process_flag(trap_exit, false).
+
+
+%%--------------------------------------------------------------------
+%% Internal functions ------------------------------------------------
+%%--------------------------------------------------------------------
+
+server_sent_garbage(Socket) ->
+ receive
+ server_sent_garbage ->
+ {error, closed} == ssl:send(Socket, "data")
+
+ end.
diff --git a/lib/ssl/test/openssl_renegotiate_SUITE.erl b/lib/ssl/test/openssl_renegotiate_SUITE.erl
new file mode 100644
index 0000000000..a5c6056d63
--- /dev/null
+++ b/lib/ssl/test/openssl_renegotiate_SUITE.erl
@@ -0,0 +1,334 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2019-2019. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+
+-module(openssl_renegotiate_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+-define(SLEEP, 1000).
+-define(OPENSSL_RENEGOTIATE, "R\n").
+
+%%--------------------------------------------------------------------
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
+
+all() ->
+ case ssl_test_lib:openssl_sane_dtls() of
+ true ->
+ [{group, 'tlsv1.2'},
+ {group, 'tlsv1.1'},
+ {group, 'tlsv1'},
+ {group, 'sslv3'},
+ {group, 'dtlsv1.2'},
+ {group, 'dtlsv1'}];
+ false ->
+ [{group, 'tlsv1.2'},
+ {group, 'tlsv1.1'},
+ {group, 'tlsv1'},
+ {group, 'sslv3'}]
+ end.
+
+groups() ->
+ case ssl_test_lib:openssl_sane_dtls() of
+ true ->
+ [{'tlsv1.2', [], all_versions_tests()},
+ {'tlsv1.1', [], all_versions_tests()},
+ {'tlsv1', [], all_versions_tests()},
+ {'sslv3', [], all_versions_tests()},
+ {'dtlsv1.2', [], all_versions_tests()},
+ {'dtlsv1', [], all_versions_tests()}
+ ];
+ false ->
+ [{'tlsv1.2', [], all_versions_tests()},
+ {'tlsv1.1', [], all_versions_tests()},
+ {'tlsv1', [], all_versions_tests()},
+ {'sslv3', [], all_versions_tests()}
+ ]
+ end.
+
+all_versions_tests() ->
+ [
+ erlang_client_openssl_server_renegotiate,
+ erlang_client_openssl_server_renegotiate_after_client_data,
+ erlang_client_openssl_server_nowrap_seqnum,
+ erlang_server_openssl_client_nowrap_seqnum
+ ].
+
+
+init_per_suite(Config0) ->
+ case os:find_executable("openssl") of
+ false ->
+ {skip, "Openssl not found"};
+ _ ->
+ ct:pal("Version: ~p", [os:cmd("openssl version")]),
+ catch crypto:stop(),
+ try crypto:start() of
+ ok ->
+ ssl_test_lib:clean_start(),
+ ssl_test_lib:make_rsa_cert(Config0)
+ catch _:_ ->
+ {skip, "Crypto did not start"}
+ end
+ end.
+
+end_per_suite(_Config) ->
+ ssl:stop(),
+ application:stop(crypto),
+ ssl_test_lib:kill_openssl().
+
+init_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ case ssl_test_lib:supports_ssl_tls_version(GroupName) of
+ true ->
+ case ssl_test_lib:check_sane_openssl_version(GroupName) of
+ true ->
+ ssl_test_lib:check_sane_openssl_renegotaite(ssl_test_lib:init_tls_version(GroupName,
+ Config),
+ GroupName);
+ false ->
+ {skip, openssl_does_not_support_version}
+ end;
+ false ->
+ {skip, openssl_does_not_support_version}
+ end;
+ _ ->
+ Config
+ end.
+
+end_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ ssl_test_lib:clean_tls_version(Config);
+ false ->
+ Config
+ end.
+init_per_testcase(erlang_client_openssl_server_nowrap_seqnum, Config) ->
+ ct:timetrap({seconds, 10}),
+ ssl_test_lib:openssl_allows_client_renegotaite(Config);
+init_per_testcase(TestCase, Config) ->
+ ct:timetrap({seconds, 10}),
+ Config.
+
+end_per_testcase(_, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
+
+erlang_client_openssl_server_renegotiate() ->
+ [{doc,"Test erlang client when openssl server issuses a renegotiate"}].
+erlang_client_openssl_server_renegotiate(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
+
+ {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
+
+ ErlData = "From erlang to openssl",
+ OpenSslData = "From openssl to erlang",
+
+ Port = ssl_test_lib:inet_port(node()),
+ CertFile = proplists:get_value(certfile, ServerOpts),
+ CaCertFile = proplists:get_value(cacertfile, ServerOpts),
+ KeyFile = proplists:get_value(keyfile, ServerOpts),
+ Version = ssl_test_lib:protocol_version(Config),
+
+ Exe = "openssl",
+ Args = ["s_server", "-accept", integer_to_list(Port),
+ ssl_test_lib:version_flag(Version),
+ "-CAfile", CaCertFile,
+ "-cert", CertFile, "-key", KeyFile, "-msg"],
+
+ OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
+
+ ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ delayed_send, [[ErlData, OpenSslData]]}},
+ {options, [{reuse_sessions, false} | ClientOpts]}]),
+
+ true = port_command(OpensslPort, ?OPENSSL_RENEGOTIATE),
+ ct:sleep(?SLEEP),
+ true = port_command(OpensslPort, OpenSslData),
+
+ ssl_test_lib:check_result(Client, OpenSslData),
+
+ %% Clean close down! Server needs to be closed first !!
+ ssl_test_lib:close_port(OpensslPort),
+ ssl_test_lib:close(Client),
+ process_flag(trap_exit, false),
+ ok.
+%%--------------------------------------------------------------------
+erlang_client_openssl_server_renegotiate_after_client_data() ->
+ [{doc,"Test erlang client when openssl server issuses a renegotiate after reading client data"}].
+erlang_client_openssl_server_renegotiate_after_client_data(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
+
+ {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
+
+ ErlData = "From erlang to openssl",
+ OpenSslData = "From openssl to erlang",
+
+ Port = ssl_test_lib:inet_port(node()),
+ CaCertFile = proplists:get_value(cacertfile, ServerOpts),
+ CertFile = proplists:get_value(certfile, ServerOpts),
+ KeyFile = proplists:get_value(keyfile, ServerOpts),
+ Version = ssl_test_lib:protocol_version(Config),
+
+ Exe = "openssl",
+ Args = ["s_server", "-accept", integer_to_list(Port),
+ ssl_test_lib:version_flag(Version),
+ "-CAfile", CaCertFile,
+ "-cert", CertFile, "-key", KeyFile, "-msg"],
+
+ OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
+
+ ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ send_wait_send, [[ErlData, OpenSslData]]}},
+ {options, [{reuse_sessions, false} |ClientOpts]}]),
+
+ true = port_command(OpensslPort, ?OPENSSL_RENEGOTIATE),
+ ct:sleep(?SLEEP),
+ true = port_command(OpensslPort, OpenSslData),
+
+ ssl_test_lib:check_result(Client, OpenSslData),
+
+ %% Clean close down! Server needs to be closed first !!
+ ssl_test_lib:close_port(OpensslPort),
+ ssl_test_lib:close(Client),
+ process_flag(trap_exit, false),
+ ok.
+
+%%--------------------------------------------------------------------
+erlang_client_openssl_server_nowrap_seqnum() ->
+ [{doc, "Test that erlang client will renegotiate session when",
+ "max sequence number celing is about to be reached. Although"
+ "in the testcase we use the test option renegotiate_at"
+ " to lower treashold substantially."}].
+erlang_client_openssl_server_nowrap_seqnum(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+
+ {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
+
+ ErlData = "From erlang to openssl\n",
+ N = 10,
+
+ Port = ssl_test_lib:inet_port(node()),
+ CaCertFile = proplists:get_value(cacertfile, ServerOpts),
+ CertFile = proplists:get_value(certfile, ServerOpts),
+ KeyFile = proplists:get_value(keyfile, ServerOpts),
+ Version = ssl_test_lib:protocol_version(Config),
+ Exe = "openssl",
+ Args = ["s_server", "-accept", integer_to_list(Port),
+ ssl_test_lib:version_flag(Version),
+ "-CAfile", CaCertFile,
+ "-cert", CertFile, "-key", KeyFile, "-msg"],
+
+ OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
+
+ ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib,
+ trigger_renegotiate, [[ErlData, N+2]]}},
+ {options, [{reuse_sessions, false},
+ {renegotiate_at, N} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client, ok),
+
+ %% Clean close down! Server needs to be closed first !!
+ ssl_test_lib:close_port(OpensslPort),
+ ssl_test_lib:close(Client),
+ process_flag(trap_exit, false).
+%%--------------------------------------------------------------------
+erlang_server_openssl_client_nowrap_seqnum() ->
+ [{doc, "Test that erlang server will renegotiate session when",
+ "max sequence number celing is about to be reached. Although"
+ "in the testcase we use the test option renegotiate_at"
+ " to lower treashold substantially."}].
+erlang_server_openssl_client_nowrap_seqnum(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
+
+ {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "From openssl to erlang",
+
+ N = 10,
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib,
+ trigger_renegotiate, [[Data, N+2]]}},
+ {options, [{renegotiate_at, N}, {reuse_sessions, false} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Version = ssl_test_lib:protocol_version(Config),
+ Exe = "openssl",
+ Args = ["s_client","-connect", ssl_test_lib:hostname_format(Hostname) ++ ":" ++ integer_to_list(Port),
+ ssl_test_lib:version_flag(Version),
+ "-msg"],
+
+ OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
+
+ true = port_command(OpenSslPort, Data),
+
+ ssl_test_lib:check_result(Server, ok),
+
+ %% Clean close down! Server needs to be closed first !!
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close_port(OpenSslPort),
+ process_flag(trap_exit, false).
+
+
+%%--------------------------------------------------------------------
+%% Internal functions ------------------------------------------------
+%%--------------------------------------------------------------------
+delayed_send(Socket, [ErlData, OpenSslData]) ->
+ ct:sleep(?SLEEP),
+ ssl:send(Socket, ErlData),
+ ssl_test_lib:active_recv(Socket, length(OpenSslData)).
+
+
+send_wait_send(Socket, [ErlData, OpenSslData]) ->
+ ssl:send(Socket, ErlData),
+ ct:sleep(?SLEEP),
+ ssl:send(Socket, ErlData),
+ ssl_test_lib:active_recv(Socket, length(OpenSslData)).
+
diff --git a/lib/ssl/test/openssl_server_cert_SUITE.erl b/lib/ssl/test/openssl_server_cert_SUITE.erl
new file mode 100644
index 0000000000..c2af864a92
--- /dev/null
+++ b/lib/ssl/test/openssl_server_cert_SUITE.erl
@@ -0,0 +1,373 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2019-2019. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(openssl_server_cert_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+-include_lib("common_test/include/ct.hrl").
+-include_lib("public_key/include/public_key.hrl").
+
+%%--------------------------------------------------------------------
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
+all() ->
+ [
+ {group, openssl_server}].
+
+groups() ->
+ [
+ {openssl_server, [], protocol_groups()},
+ {'tlsv1.3', [], tls_1_3_protocol_groups()},
+ {'tlsv1.2', [], pre_tls_1_3_protocol_groups()},
+ {'tlsv1.1', [], pre_tls_1_3_protocol_groups()},
+ {'tlsv1', [], pre_tls_1_3_protocol_groups()},
+ {'sslv3', [], ssl_protocol_groups()},
+ {'dtlsv1.2', [], pre_tls_1_3_protocol_groups()},
+ {'dtlsv1', [], pre_tls_1_3_protocol_groups()},
+ {rsa, [], all_version_tests()},
+ {ecdsa, [], all_version_tests()},
+ {dsa, [], all_version_tests()},
+ {rsa_1_3, [], all_version_tests() ++ tls_1_3_tests()},
+ %% TODO: Create proper conf of openssl server
+ %%++ [unsupported_sign_algo_client_auth,
+ %% unsupported_sign_algo_cert_client_auth]},
+ {ecdsa_1_3, [], all_version_tests() ++ tls_1_3_tests()}
+ ].
+
+protocol_groups() ->
+ [{group, 'tlsv1.3'},
+ {group, 'tlsv1.2'},
+ {group, 'tlsv1.1'},
+ {group, 'tlsv1'},
+ {group, 'sslv3'},
+ {group, 'dtlsv1.2'},
+ {group, 'dtlsv1'}
+ ].
+
+ssl_protocol_groups() ->
+ [{group, rsa},
+ {group, dsa}].
+
+pre_tls_1_3_protocol_groups() ->
+ [{group, rsa},
+ {group, ecdsa},
+ {group, dsa}].
+
+tls_1_3_protocol_groups() ->
+ [{group, rsa_1_3},
+ {group, ecdsa_1_3}].
+
+tls_1_3_tests() ->
+ [
+ hello_retry_request,
+ custom_groups,
+ hello_retry_client_auth,
+ hello_retry_client_auth_empty_cert_accepted,
+ hello_retry_client_auth_empty_cert_rejected
+ ].
+
+all_version_tests() ->
+ [
+ no_auth,
+ auth,
+ missing_root_cert_no_auth
+ %%invalid_signature_client
+ ].
+
+init_per_suite(Config) ->
+ catch crypto:stop(),
+ try crypto:start() of
+ ok ->
+ ssl_test_lib:clean_start(),
+ Config
+ catch _:_ ->
+ {skip, "Crypto did not start"}
+ end.
+
+end_per_suite(_Config) ->
+ ssl:stop(),
+ application:unload(ssl),
+ application:stop(crypto).
+
+init_per_group(openssl_server, Config0) ->
+ Config = proplists:delete(server_type, proplists:delete(client_type, Config0)),
+ [{client_type, erlang}, {server_type, openssl} | Config];
+init_per_group(rsa = Group, Config0) ->
+ Config = ssl_test_lib:make_rsa_cert(Config0),
+ COpts = proplists:get_value(client_rsa_opts, Config),
+ SOpts = proplists:get_value(server_rsa_opts, Config),
+ %% Make sure _rsa* suite is choosen by ssl_test_lib:start_server
+ Version = proplists:get_value(version,Config),
+ Ciphers = ssl_cert_tests:test_ciphers(fun(dhe_rsa) ->
+ true;
+ (ecdhe_rsa) ->
+ true;
+ (_) ->
+ false
+ end, Version),
+ case Ciphers of
+ [_|_] ->
+ [{cert_key_alg, rsa} |
+ lists:delete(cert_key_alg,
+ [{client_cert_opts, [{ciphers, Ciphers} | COpts]},
+ {server_cert_opts, SOpts} |
+ lists:delete(server_cert_opts,
+ lists:delete(client_cert_opts, Config))])];
+ [] ->
+ {skip, {no_sup, Group, Version}}
+ end;
+init_per_group(rsa_1_3 = Group, Config0) ->
+ Config = ssl_test_lib:make_rsa_cert(Config0),
+ COpts = proplists:get_value(client_rsa_opts, Config),
+ SOpts = proplists:get_value(server_rsa_opts, Config),
+ %% Make sure _rsa* suite is choosen by ssl_test_lib:start_server
+ Version = proplists:get_value(version,Config),
+ Ciphers = ssl_cert_tests:test_ciphers(undefined, Version),
+ case Ciphers of
+ [_|_] ->
+ [{cert_key_alg, rsa} |
+ lists:delete(cert_key_alg,
+ [{client_cert_opts, [{ciphers, Ciphers} | COpts]},
+ {server_cert_opts, SOpts} |
+ lists:delete(server_cert_opts,
+ lists:delete(client_cert_opts, Config))])];
+ [] ->
+ {skip, {no_sup, Group, Version}}
+ end;
+init_per_group(ecdsa = Group, Config0) ->
+ PKAlg = crypto:supports(public_keys),
+ case lists:member(ecdsa, PKAlg) andalso (lists:member(ecdh, PKAlg) orelse
+ lists:member(dh, PKAlg)) of
+ true ->
+ Config = ssl_test_lib:make_ecdsa_cert(Config0),
+ COpts = proplists:get_value(client_ecdsa_opts, Config),
+ SOpts = proplists:get_value(server_ecdsa_opts, Config),
+ %% Make sure ecdh* suite is choosen by ssl_test_lib:start_server
+ Version = proplists:get_value(version,Config),
+ Ciphers = ssl_cert_tests:test_ciphers(fun(ecdh_ecdsa) ->
+ true;
+ (ecdhe_ecdsa) ->
+ true;
+ (_) ->
+ false
+ end, Version),
+ case Ciphers of
+ [_|_] ->
+ [{cert_key_alg, ecdsa} |
+ lists:delete(cert_key_alg,
+ [{client_cert_opts, [{ciphers, Ciphers} | COpts]},
+ {server_cert_opts, SOpts} |
+ lists:delete(server_cert_opts,
+ lists:delete(client_cert_opts, Config))]
+ )];
+ [] ->
+ {skip, {no_sup, Group, Version}}
+ end;
+ false ->
+ {skip, "Missing EC crypto support"}
+ end;
+init_per_group(ecdsa_1_3 = Group, Config0) ->
+ PKAlg = crypto:supports(public_keys),
+ case lists:member(ecdsa, PKAlg) andalso (lists:member(ecdh, PKAlg) orelse
+ lists:member(dh, PKAlg)) of
+ true ->
+ Config = ssl_test_lib:make_ecdsa_cert(Config0),
+ COpts = proplists:get_value(client_ecdsa_opts, Config),
+ SOpts = proplists:get_value(server_ecdsa_opts, Config),
+ %% Make sure ecdh* suite is choosen by ssl_test_lib:start_server
+ Version = proplists:get_value(version,Config),
+ Ciphers = ssl_cert_tests:test_ciphers(undefined, Version),
+ case Ciphers of
+ [_|_] ->
+ [{cert_key_alg, ecdsa} |
+ lists:delete(cert_key_alg,
+ [{client_cert_opts, [{ciphers, Ciphers} | COpts]},
+ {server_cert_opts, SOpts} |
+ lists:delete(server_cert_opts,
+ lists:delete(client_cert_opts, Config))]
+ )];
+ [] ->
+ {skip, {no_sup, Group, Version}}
+ end;
+ false ->
+ {skip, "Missing EC crypto support"}
+ end;
+init_per_group(Group, Config0) when Group == dsa ->
+ PKAlg = crypto:supports(public_keys),
+ case lists:member(dss, PKAlg) andalso lists:member(dh, PKAlg) of
+ true ->
+ Config = ssl_test_lib:make_dsa_cert(Config0),
+ COpts = proplists:get_value(client_dsa_opts, Config),
+ SOpts = proplists:get_value(server_dsa_opts, Config),
+ %% Make sure dhe_dss* suite is choosen by ssl_test_lib:start_server
+ Version = proplists:get_value(version,Config),
+ Ciphers = ssl_cert_tests:test_ciphers(fun(dh_dss) ->
+ true;
+ (dhe_dss) ->
+ true;
+ (_) ->
+ false
+ end, Version),
+ case Ciphers of
+ [_|_] ->
+ [{cert_key_alg, dsa} |
+ lists:delete(cert_key_alg,
+ [{client_cert_opts, [{ciphers, Ciphers} | COpts]},
+ {server_cert_opts, SOpts} |
+ lists:delete(server_cert_opts,
+ lists:delete(client_cert_opts, Config))])];
+ [] ->
+ {skip, {no_sup, Group, Version}}
+ end;
+ false ->
+ {skip, "Missing DSS crypto support"}
+ end;
+init_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ case ssl_test_lib:check_sane_openssl_version(GroupName) of
+ true ->
+ [{version, GroupName}
+ | ssl_test_lib:init_tls_version(GroupName, Config)];
+ false ->
+ {skip, "Missing openssl support"}
+ end;
+ _ ->
+ ssl:start(),
+ Config
+ end.
+
+end_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ ssl_test_lib:clean_tls_version(Config);
+ false ->
+ Config
+ end.
+
+init_per_testcase(_TestCase, Config) ->
+ ssl_test_lib:ct_log_supported_protocol_versions(Config),
+ ct:timetrap({seconds, 10}),
+ Config.
+
+end_per_testcase(_TestCase, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
+
+no_auth() ->
+ ssl_cert_tests:no_auth().
+
+no_auth(Config) ->
+ ssl_cert_tests:no_auth(Config).
+%%--------------------------------------------------------------------
+auth() ->
+ ssl_cert_tests:auth().
+auth(Config) ->
+ ssl_cert_tests:auth(Config).
+%%--------------------------------------------------------------------
+client_auth_empty_cert_accepted() ->
+ ssl_cert_tests:client_auth_empty_cert_accepted().
+client_auth_empty_cert_accepted(Config) ->
+ ssl_cert_tests:client_auth_empty_cert_accepted(Config).
+%%--------------------------------------------------------------------
+client_auth_empty_cert_rejected() ->
+ ssl_cert_tests:client_auth_empty_cert_rejected().
+client_auth_empty_cert_rejected(Config) ->
+ ssl_cert_tests:client_auth_empty_cert_rejected(Config).
+%%--------------------------------------------------------------------
+client_auth_partial_chain() ->
+ ssl_cert_tests:client_auth_partial_chain().
+client_auth_partial_chain(Config) when is_list(Config) ->
+ ssl_cert_tests:client_auth_partial_chain(Config).
+
+%%--------------------------------------------------------------------
+client_auth_allow_partial_chain() ->
+ ssl_cert_tests:client_auth_allow_partial_chain().
+client_auth_allow_partial_chain(Config) when is_list(Config) ->
+ ssl_cert_tests:client_auth_allow_partial_chain(Config).
+%%--------------------------------------------------------------------
+client_auth_do_not_allow_partial_chain() ->
+ ssl_cert_tests:client_auth_do_not_allow_partial_chain().
+client_auth_do_not_allow_partial_chain(Config) when is_list(Config) ->
+ ssl_cert_tests:client_auth_do_not_allow_partial_chain(Config).
+
+%%--------------------------------------------------------------------
+client_auth_partial_chain_fun_fail() ->
+ ssl_cert_tests:client_auth_partial_chain_fun_fail().
+client_auth_partial_chain_fun_fail(Config) when is_list(Config) ->
+ ssl_cert_tests:client_auth_partial_chain_fun_fail(Config).
+
+%%--------------------------------------------------------------------
+missing_root_cert_no_auth() ->
+ ssl_cert_tests:missing_root_cert_no_auth().
+missing_root_cert_no_auth(Config) when is_list(Config) ->
+ ssl_cert_tests:missing_root_cert_no_auth(Config).
+
+%%--------------------------------------------------------------------
+invalid_signature_client() ->
+ ssl_cert_tests:invalid_signature_client().
+invalid_signature_client(Config) when is_list(Config) ->
+ ssl_cert_tests:invalid_signature_client(Config).
+%%--------------------------------------------------------------------
+invalid_signature_server() ->
+ ssl_cert_tests:invalid_signature_client().
+invalid_signature_server(Config) when is_list(Config) ->
+ ssl_cert_tests:invalid_signature_client(Config).
+
+%%--------------------------------------------------------------------
+%% TLS 1.3 Test Cases ------------------------------------------------
+%%--------------------------------------------------------------------
+hello_retry_request() ->
+ ssl_cert_tests:hello_retry_request().
+hello_retry_request(Config) ->
+ ssl_cert_tests:hello_retry_request(Config).
+%%--------------------------------------------------------------------
+custom_groups() ->
+ ssl_cert_tests:custom_groups().
+custom_groups(Config) ->
+ ssl_cert_tests:custom_groups(Config).
+unsupported_sign_algo_cert_client_auth() ->
+ ssl_cert_tests:unsupported_sign_algo_cert_client_auth().
+unsupported_sign_algo_cert_client_auth(Config) ->
+ ssl_cert_tests:unsupported_sign_algo_cert_client_auth(Config).
+unsupported_sign_algo_client_auth() ->
+ ssl_cert_tests:unsupported_sign_algo_client_auth().
+unsupported_sign_algo_client_auth(Config) ->
+ ssl_cert_tests:unsupported_sign_algo_client_auth(Config).
+%%--------------------------------------------------------------------
+hello_retry_client_auth() ->
+ ssl_cert_tests:hello_retry_client_auth().
+hello_retry_client_auth(Config) ->
+ ssl_cert_tests:hello_retry_client_auth(Config).
+%%--------------------------------------------------------------------
+hello_retry_client_auth_empty_cert_accepted() ->
+ ssl_cert_tests:hello_retry_client_auth_empty_cert_accepted().
+hello_retry_client_auth_empty_cert_accepted(Config) ->
+ ssl_cert_tests:hello_retry_client_auth_empty_cert_accepted(Config).
+%%--------------------------------------------------------------------
+hello_retry_client_auth_empty_cert_rejected() ->
+ ssl_cert_tests:hello_retry_client_auth_empty_cert_rejected().
+hello_retry_client_auth_empty_cert_rejected(Config) ->
+ ssl_cert_tests:hello_retry_client_auth_empty_cert_rejected(Config).
diff --git a/lib/ssl/test/openssl_session_SUITE.erl b/lib/ssl/test/openssl_session_SUITE.erl
new file mode 100644
index 0000000000..7c129633da
--- /dev/null
+++ b/lib/ssl/test/openssl_session_SUITE.erl
@@ -0,0 +1,259 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2008-2018. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+
+-module(openssl_session_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+-define(SLEEP, 1000).
+-define(EXPIRE, 10).
+
+%%--------------------------------------------------------------------
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
+
+all() ->
+ case ssl_test_lib:openssl_sane_dtls() of
+ true ->
+ [{group, 'tlsv1.2'},
+ {group, 'tlsv1.1'},
+ {group, 'tlsv1'},
+ {group, 'sslv3'},
+ {group, 'dtlsv1.2'},
+ {group, 'dtlsv1'}];
+ false ->
+ [{group, 'tlsv1.2'},
+ {group, 'tlsv1.1'},
+ {group, 'tlsv1'},
+ {group, 'sslv3'}]
+ end.
+
+groups() ->
+ case ssl_test_lib:openssl_sane_dtls() of
+ true ->
+ [{'tlsv1.2', [], tests()},
+ {'tlsv1.1', [], tests()},
+ {'tlsv1', [], tests()},
+ {'sslv3', [], tests()},
+ {'dtlsv1.2', [], tests()},
+ {'dtlsv1', [], tests()}
+ ];
+ false ->
+ [{'tlsv1.2', [], tests()},
+ {'tlsv1.1', [], tests()},
+ {'tlsv1', [], tests()},
+ {'sslv3', [], tests()}
+ ]
+ end.
+
+tests() ->
+ [
+ reuse_session_erlang_server,
+ reuse_session_erlang_client
+ ].
+
+
+init_per_suite(Config0) ->
+ case os:find_executable("openssl") of
+ false ->
+ {skip, "Openssl not found"};
+ _ ->
+ ct:pal("Version: ~p", [os:cmd("openssl version")]),
+ catch crypto:stop(),
+ try crypto:start() of
+ ok ->
+ ssl_test_lib:clean_start(),
+ ssl_test_lib:make_rsa_cert(Config0)
+ catch _:_ ->
+ {skip, "Crypto did not start"}
+ end
+ end.
+
+end_per_suite(_Config) ->
+ ssl:stop(),
+ application:stop(crypto),
+ ssl_test_lib:kill_openssl().
+
+init_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ case ssl_test_lib:supports_ssl_tls_version(GroupName) of
+ true ->
+ case ssl_test_lib:check_sane_openssl_version(GroupName) of
+ true ->
+ ssl_test_lib:init_tls_version(GroupName, Config);
+ false ->
+ {skip, openssl_does_not_support_version}
+ end;
+ false ->
+ {skip, openssl_does_not_support_version}
+ end;
+ _ ->
+ ssl:start(),
+ Config
+ end.
+
+end_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ ssl_test_lib:clean_tls_version(Config);
+ false ->
+ Config
+ end.
+
+init_per_testcase(reuse_session_erlang_client, Config) ->
+ ct:timetrap(?EXPIRE * 1000 * 5),
+ ssl:stop(),
+ application:load(ssl),
+ application:set_env(ssl, session_lifetime, ?EXPIRE),
+ ssl:start(),
+ Config;
+
+init_per_testcase(TestCase, Config) ->
+ ct:timetrap({seconds, 10}),
+ Config.
+
+end_per_testcase(reuse_session_erlang_client, Config) ->
+ application:unset_env(ssl, session_lifetime),
+ Config;
+end_per_testcase(_, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
+
+%%--------------------------------------------------------------------
+reuse_session_erlang_server() ->
+ [{doc, "Test erlang server with openssl client that reconnects with the"
+ "same session id, to test reusing of sessions."}].
+reuse_session_erlang_server(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+
+ {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "From openssl to erlang",
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, active_recv, [length(Data)]}},
+ {reconnect_times, 5},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Version = ssl_test_lib:protocol_version(Config),
+
+ Exe = "openssl",
+ Args = ["s_client", "-connect", ssl_test_lib:hostname_format(Hostname)
+ ++ ":" ++ integer_to_list(Port),
+ ssl_test_lib:version_flag(Version),
+ "-reconnect"],
+
+ OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
+
+ true = port_command(OpenSslPort, Data),
+
+ ssl_test_lib:check_result(Server, Data),
+
+ %% Clean close down! Server needs to be closed first !!
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close_port(OpenSslPort).
+
+%%--------------------------------------------------------------------
+
+reuse_session_erlang_client() ->
+ [{doc, "Test erlang ssl client that wants to reuse sessions"}].
+reuse_session_erlang_client(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
+
+ Version = ssl_test_lib:protocol_version(Config),
+ Port = ssl_test_lib:inet_port(node()),
+ CertFile = proplists:get_value(certfile, ServerOpts),
+ CACertFile = proplists:get_value(cacertfile, ServerOpts),
+ KeyFile = proplists:get_value(keyfile, ServerOpts),
+
+ Exe = "openssl",
+ Args = ["s_server", "-accept", integer_to_list(Port), ssl_test_lib:version_flag(Version),
+ "-cert", CertFile,"-key", KeyFile, "-CAfile", CACertFile],
+
+ OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
+
+ ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)),
+
+ Client0 =
+ ssl_test_lib:start_client([{node, ClientNode},
+ {port, Port}, {host, Hostname},
+ {mfa, {ssl_test_lib, session_id, []}},
+ {from, self()}, {options, [{reuse_sessions, save}, {verify, verify_peer}| ClientOpts]}]),
+
+ SID = receive
+ {Client0, Id0} ->
+ Id0
+ end,
+
+ ssl_test_lib:close(Client0),
+
+ Client1 =
+ ssl_test_lib:start_client([{node, ClientNode},
+ {port, Port}, {host, Hostname},
+ {mfa, {ssl_test_lib, session_id, []}},
+ {from, self()}, {options, [{reuse_session, SID} | ClientOpts]}]),
+ receive
+ {Client1, SID} ->
+ ok
+ after ?SLEEP ->
+ ct:fail(session_not_reused)
+ end,
+
+
+ ssl_test_lib:close(Client1),
+ %% Make sure session is unregistered due to expiration
+ ct:sleep(20000),
+
+ Client2 =
+ ssl_test_lib:start_client([{node, ClientNode},
+ {port, Port}, {host, Hostname},
+ {mfa, {ssl_test_lib, session_id, []}},
+ {from, self()}, {options, ClientOpts}]),
+ receive
+ {Client2, ID} ->
+ case ID of
+ SID ->
+ ct:fail(expired_session_reused);
+ _ ->
+ ok
+ end
+ end,
+
+ %% Clean close down! Server needs to be closed first !!
+ ssl_test_lib:close_port(OpensslPort),
+ ssl_test_lib:close(Client2).
+
+
+%%--------------------------------------------------------------------
+%% Internal functions ------------------------------------------------
+%%--------------------------------------------------------------------
diff --git a/lib/ssl/test/openssl_sni_SUITE.erl b/lib/ssl/test/openssl_sni_SUITE.erl
new file mode 100644
index 0000000000..26f08e36c0
--- /dev/null
+++ b/lib/ssl/test/openssl_sni_SUITE.erl
@@ -0,0 +1,251 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2019-2019. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+
+-module(openssl_sni_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+
+-define(OPENSSL_QUIT, "Q\n").
+-define(OPENSSL_RENEGOTIATE, "R\n").
+-define(SLEEP, 1000).
+
+%%--------------------------------------------------------------------
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
+
+all() ->
+ %% Note: SNI not supported in sslv3
+ case ssl_test_lib:openssl_sane_dtls() of
+ true ->
+ [{group, 'tlsv1.2'},
+ {group, 'tlsv1.1'},
+ {group, 'tlsv1'}
+ %% Seems broken in openssl
+ %%{group, 'dtlsv1.2'},
+ %%{group, 'dtlsv1'}
+ ];
+ false ->
+ [{group, 'tlsv1.2'},
+ {group, 'tlsv1.1'},
+ {group, 'tlsv1'}]
+ end.
+
+groups() ->
+ case ssl_test_lib:openssl_sane_dtls() of
+ true ->
+ [{'tlsv1.2', [], sni_tests()},
+ {'tlsv1.1', [], sni_tests()},
+ {'tlsv1', [], sni_tests()}
+ %% Seems broken in openssl
+ %%{'dtlsv1.2', [], sni_tests()},
+ %%{'dtlsv1', [], sni_tests()}
+ ];
+ false ->
+ [{'tlsv1.2', [], sni_tests()},
+ {'tlsv1.1', [], sni_tests()},
+ {'tlsv1', [], sni_tests()}
+ ]
+ end.
+
+sni_tests() ->
+ [erlang_server_openssl_client_sni_match,
+ erlang_server_openssl_client_sni_match_fun,
+ erlang_server_openssl_client_sni_no_match,
+ erlang_server_openssl_client_sni_no_match_fun,
+ erlang_server_openssl_client_sni_no_header,
+ erlang_server_openssl_client_sni_no_header_fun].
+
+init_per_suite(Config0) ->
+ case os:find_executable("openssl") of
+ false ->
+ {skip, "Openssl not found"};
+ _ ->
+ case check_openssl_sni_support(Config0) of
+ {skip, _} = Skip ->
+ Skip;
+ _ ->
+ ct:pal("Version: ~p", [os:cmd("openssl version")]),
+ catch crypto:stop(),
+ try crypto:start() of
+ ok ->
+ ssl_test_lib:clean_start(),
+ Config = ssl_test_lib:make_rsa_cert(Config0),
+ RsaOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ [{sni_server_opts, [{sni_hosts,
+ [{"a.server", [
+ {certfile, proplists:get_value(certfile, RsaOpts)},
+ {keyfile, proplists:get_value(keyfile, RsaOpts)}
+ ]},
+ {"b.server", [
+ {certfile, proplists:get_value(certfile, RsaOpts)},
+ {keyfile, proplists:get_value(keyfile, RsaOpts)}
+ ]}
+ ]}]} | Config]
+ catch _:_ ->
+ {skip, "Crypto did not start"}
+ end
+ end
+ end.
+
+end_per_suite(_Config) ->
+ ssl:stop(),
+ application:stop(crypto),
+ ssl_test_lib:kill_openssl().
+
+init_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ case ssl_test_lib:supports_ssl_tls_version(GroupName) of
+ true ->
+ case ssl_test_lib:check_sane_openssl_version(GroupName) of
+ true ->
+ ssl_test_lib:init_tls_version(GroupName, Config);
+ false ->
+ {skip, openssl_does_not_support_version}
+ end;
+ false ->
+ {skip, openssl_does_not_support_version}
+ end;
+ _ ->
+ Config
+ end.
+
+end_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ ssl_test_lib:clean_tls_version(Config);
+ false ->
+ Config
+ end.
+
+init_per_testcase(_TestCase, Config) ->
+ ct:timetrap({seconds, 10}),
+ Config.
+
+
+end_per_testcase(_, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
+erlang_server_openssl_client_sni_no_header(Config) when is_list(Config) ->
+ erlang_server_openssl_client_sni_test(Config, undefined, undefined, "server Peer cert").
+
+erlang_server_openssl_client_sni_no_header_fun(Config) when is_list(Config) ->
+ erlang_server_openssl_client_sni_test_sni_fun(Config, undefined, undefined, "server Peer cert").
+
+erlang_server_openssl_client_sni_match(Config) when is_list(Config) ->
+ erlang_server_openssl_client_sni_test(Config, "a.server", "a.server", "server Peer cert").
+
+erlang_server_openssl_client_sni_match_fun(Config) when is_list(Config) ->
+ erlang_server_openssl_client_sni_test_sni_fun(Config, "a.server", "a.server", "server Peer cert").
+
+erlang_server_openssl_client_sni_no_match(Config) when is_list(Config) ->
+ erlang_server_openssl_client_sni_test(Config, "c.server", undefined, "server Peer cert").
+
+erlang_server_openssl_client_sni_no_match_fun(Config) when is_list(Config) ->
+ erlang_server_openssl_client_sni_test_sni_fun(Config, "c.server", undefined, "server Peer cert").
+
+
+erlang_server_openssl_client_sni_test(Config, SNIHostname, ExpectedSNIHostname, ExpectedCN) ->
+ Version = ssl_test_lib:protocol_version(Config),
+ ct:log("Start running handshake, Config: ~p, SNIHostname: ~p, ExpectedSNIHostname: ~p, ExpectedCN: ~p",
+ [Config, SNIHostname, ExpectedSNIHostname, ExpectedCN]),
+ ServerOptions = proplists:get_value(sni_server_opts, Config) ++ proplists:get_value(server_rsa_opts, Config),
+ {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()}, {mfa, {?MODULE, send_and_hostname, []}},
+ {options, ServerOptions}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Exe = "openssl",
+ ClientArgs = case SNIHostname of
+ undefined ->
+ openssl_client_args(Version, Hostname,Port);
+ _ ->
+ openssl_client_args(Version, Hostname, Port, SNIHostname)
+ end,
+ ClientPort = ssl_test_lib:portable_open_port(Exe, ClientArgs),
+
+ ssl_test_lib:check_result(Server, ExpectedSNIHostname),
+ ssl_test_lib:close_port(ClientPort),
+ ssl_test_lib:close(Server),
+ ok.
+
+
+erlang_server_openssl_client_sni_test_sni_fun(Config, SNIHostname, ExpectedSNIHostname, ExpectedCN) ->
+ Version = ssl_test_lib:protocol_version(Config),
+ ct:log("Start running handshake for sni_fun, Config: ~p, SNIHostname: ~p, ExpectedSNIHostname: ~p, ExpectedCN: ~p",
+ [Config, SNIHostname, ExpectedSNIHostname, ExpectedCN]),
+ [{sni_hosts, ServerSNIConf}] = proplists:get_value(sni_server_opts, Config),
+ SNIFun = fun(Domain) -> proplists:get_value(Domain, ServerSNIConf, undefined) end,
+ ServerOptions = proplists:get_value(server_rsa_opts, Config) ++ [{sni_fun, SNIFun}],
+ {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()}, {mfa, {?MODULE, send_and_hostname, []}},
+ {options, ServerOptions}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Exe = "openssl",
+ ClientArgs = case SNIHostname of
+ undefined ->
+ openssl_client_args(Version, Hostname,Port);
+ _ ->
+ openssl_client_args(Version, Hostname, Port, SNIHostname)
+ end,
+
+ ClientPort = ssl_test_lib:portable_open_port(Exe, ClientArgs),
+
+ ssl_test_lib:check_result(Server, ExpectedSNIHostname),
+ ssl_test_lib:close_port(ClientPort),
+ ssl_test_lib:close(Server).
+
+send_and_hostname(SSLSocket) ->
+ ssl:send(SSLSocket, "OK"),
+ case ssl:connection_information(SSLSocket, [sni_hostname]) of
+ {ok, []} ->
+ undefined;
+ {ok, [{sni_hostname, Hostname}]} ->
+ Hostname
+ end.
+
+openssl_client_args(Version, Hostname, Port) ->
+ ["s_client", "-connect", Hostname ++ ":" ++ integer_to_list(Port), ssl_test_lib:version_flag(Version)].
+
+openssl_client_args(Version, Hostname, Port, ServerName) ->
+ ["s_client", "-connect", Hostname ++ ":" ++
+ integer_to_list(Port), ssl_test_lib:version_flag(Version), "-servername", ServerName].
+
+check_openssl_sni_support(Config) ->
+ HelpText = os:cmd("openssl s_client --help"),
+ case ssl_test_lib:is_sane_oppenssl_client() of
+ true ->
+ case string:str(HelpText, "-servername") of
+ 0 ->
+ {skip, "Current openssl doesn't support SNI"};
+ _ ->
+ Config
+ end;
+ false ->
+ {skip, "Current openssl doesn't support SNI or extension handling is flawed"}
+ end.
diff --git a/lib/ssl/test/openssl_tls_1_3_version_SUITE.erl b/lib/ssl/test/openssl_tls_1_3_version_SUITE.erl
new file mode 100644
index 0000000000..8a2692ec1d
--- /dev/null
+++ b/lib/ssl/test/openssl_tls_1_3_version_SUITE.erl
@@ -0,0 +1,172 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2019-2019. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+
+-module(openssl_tls_1_3_version_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("public_key/include/public_key.hrl").
+
+%%--------------------------------------------------------------------
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
+all() ->
+ [
+ %%{group, openssl_server},
+ {group, openssl_client}
+ ].
+
+groups() ->
+ [
+ %%{openssl_server, [{group, 'tlsv1.3'}]},
+ {openssl_client, [{group, 'tlsv1.3'}]},
+ {'tlsv1.3', [], cert_groups()},
+ {rsa, [], tests()},
+ {ecdsa, [], tests()}
+ ].
+
+cert_groups() ->
+ [{group, rsa},
+ {group, ecdsa}].
+
+tests() ->
+ [tls13_client_tls12_server,
+ %%tls13_client_with_ext_tls12_server,
+ tls12_client_tls13_server].
+
+init_per_suite(Config) ->
+ catch crypto:stop(),
+ try crypto:start() of
+ ok ->
+ case ssl_test_lib:check_sane_openssl_version('tlsv1.3') of
+ true ->
+ ssl_test_lib:clean_start(),
+ Config;
+ false ->
+ {skip, openssl_does_not_support_version}
+ end
+ catch _:_ ->
+ {skip, "Crypto did not start"}
+ end.
+
+end_per_suite(_Config) ->
+ ssl:stop(),
+ application:stop(crypto).
+
+init_per_group(openssl_client, Config0) ->
+ Config = proplists:delete(server_type, proplists:delete(client_type, Config0)),
+ [{client_type, openssl}, {server_type, erlang} | Config];
+init_per_group(openssl_server, Config0) ->
+ Config = proplists:delete(server_type, proplists:delete(client_type, Config0)),
+ [{client_type, erlang}, {server_type, openssl} | Config];
+init_per_group(rsa, Config0) ->
+ Config = ssl_test_lib:make_rsa_cert(Config0),
+ COpts = proplists:get_value(client_rsa_opts, Config),
+ SOpts = proplists:get_value(server_rsa_opts, Config),
+ [{client_cert_opts, COpts}, {server_cert_opts, SOpts} |
+ lists:delete(server_cert_opts, lists:delete(client_cert_opts, Config))];
+init_per_group(ecdsa, Config0) ->
+ PKAlg = crypto:supports(public_keys),
+ case lists:member(ecdsa, PKAlg) andalso
+ (lists:member(ecdh, PKAlg) orelse lists:member(dh, PKAlg)) of
+ true ->
+ Config = ssl_test_lib:make_ecdsa_cert(Config0),
+ COpts = proplists:get_value(client_ecdsa_opts, Config),
+ SOpts = proplists:get_value(server_ecdsa_opts, Config),
+ [{client_cert_opts, COpts}, {server_cert_opts, SOpts} |
+ lists:delete(server_cert_opts, lists:delete(client_cert_opts, Config))];
+ false ->
+ {skip, "Missing EC crypto support"}
+ end;
+init_per_group(GroupName, Config) ->
+ ssl_test_lib:clean_tls_version(Config),
+ case ssl_test_lib:is_tls_version(GroupName) andalso
+ ssl_test_lib:sufficient_crypto_support(GroupName) of
+ true ->
+ ssl_test_lib:init_tls_version(GroupName, Config);
+ _ ->
+ case ssl_test_lib:sufficient_crypto_support(GroupName) of
+ true ->
+ ssl:start(),
+ Config;
+ false ->
+ {skip, "Missing crypto support"}
+ end
+ end.
+
+end_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ ssl_test_lib:clean_tls_version(Config);
+ false ->
+ Config
+ end.
+
+%%--------------------------------------------------------------------
+%% Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
+
+tls13_client_tls12_server() ->
+ [{doc,"Test that a TLS 1.3 client can connect to a TLS 1.2 server."}].
+
+tls13_client_tls12_server(Config) when is_list(Config) ->
+ ClientOpts = [{versions,
+ ['tlsv1.3', 'tlsv1.2']} | ssl_test_lib:ssl_options(client_cert_opts, Config)],
+ ServerOpts = [{versions,
+ ['tlsv1.1', 'tlsv1.2']} | ssl_test_lib:ssl_options(server_cert_opts, Config)],
+ ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config).
+
+%% tls13_client_with_ext_tls12_server() ->
+%% [{doc,"Test basic connection between TLS 1.2 server and TLS 1.3 client when "
+%% "client has TLS 1.3 specsific extensions"}].
+
+%% tls13_client_with_ext_tls12_server(Config) ->
+%% ClientOpts0 = ssl_test_lib:ssl_options(client_cert_opts, Config),
+%% ServerOpts0 = ssl_test_lib:ssl_options(server_cert_opts, Config),
+
+%% {ServerOpts, ClientOpts} =
+%% case proplists:get_value(client_type) of
+%% erlang ->
+%% {[{versions, ['tlsv1.2']}|ServerOpts0],
+%% [{versions, ['tlsv1.2','tlsv1.3']},
+%% {signature_algs_cert, [ecdsa_secp384r1_sha384,
+%% ecdsa_secp256r1_sha256,
+%% rsa_pss_rsae_sha256,
+%% rsa_pkcs1_sha256,
+%% {sha256,rsa},{sha256,dsa}]}|ClientOpts0]};
+%% openssl ->
+
+
+%% ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config).
+
+tls12_client_tls13_server() ->
+ [{doc,"Test that a TLS 1.2 client can connect to a TLS 1.3 server."}].
+
+tls12_client_tls13_server(Config) when is_list(Config) ->
+ ClientOpts = [{versions,
+ ['tlsv1.1', 'tlsv1.2']} | ssl_test_lib:ssl_options(client_cert_opts, Config)],
+ ServerOpts = [{versions,
+ ['tlsv1.3', 'tlsv1.2']} | ssl_test_lib:ssl_options(server_cert_opts, Config)],
+ ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config).
+
diff --git a/lib/ssl/test/property_test/ssl_eqc_handshake.erl b/lib/ssl/test/property_test/ssl_eqc_handshake.erl
index 38a4b7fb11..2ceb540e15 100644
--- a/lib/ssl/test/property_test/ssl_eqc_handshake.erl
+++ b/lib/ssl/test/property_test/ssl_eqc_handshake.erl
@@ -119,19 +119,27 @@ tls_msg(Version) ->
%%
client_hello(?'TLS_v1.3' = Version) ->
#client_hello{session_id = session_id(),
- client_version = ?'TLS_v1.2',
- cipher_suites = cipher_suites(Version),
+ client_version = ?'TLS_v1.2',
+ cipher_suites = cipher_suites(Version),
+ compression_methods = compressions(Version),
+ random = client_random(Version),
+ extensions = client_hello_extensions(Version)
+ };
+client_hello(Version) ->
+ #client_hello{session_id = session_id(),
+ client_version = Version,
+ cipher_suites = cipher_suites(Version),
compression_methods = compressions(Version),
random = client_random(Version),
extensions = client_hello_extensions(Version)
};
-client_hello(Version) ->
+client_hello(?'SSL_v3' = Version) ->
#client_hello{session_id = session_id(),
client_version = Version,
cipher_suites = cipher_suites(Version),
compression_methods = compressions(Version),
random = client_random(Version),
- extensions = client_hello_extensions(Version)
+ extensions = ssl_handshake:empty_extensions(Version, client_hello)
}.
server_hello(?'TLS_v1.3' = Version) ->
@@ -142,6 +150,14 @@ server_hello(?'TLS_v1.3' = Version) ->
compression_method = compression(Version),
extensions = server_hello_extensions(Version)
};
+server_hello(?'SSL_v3' = Version) ->
+ #server_hello{server_version = Version,
+ session_id = session_id(),
+ random = server_random(Version),
+ cipher_suite = cipher_suite(Version),
+ compression_method = compression(Version),
+ extensions = ssl_handshake:empty_extensions(Version, server_hello)
+ };
server_hello(Version) ->
#server_hello{server_version = Version,
session_id = session_id(),
@@ -291,7 +307,7 @@ pre_shared_keyextension() ->
%% | | |
%% | signature_algorithms_cert (RFC 8446) | CH, CR |
%% +--------------------------------------------------+-------------+
-extensions(?'TLS_v1.3' = Version, client_hello) ->
+extensions(?'TLS_v1.3' = Version, MsgType = client_hello) ->
?LET({
ServerName,
%% MaxFragmentLength,
@@ -306,8 +322,8 @@ extensions(?'TLS_v1.3' = Version, client_hello) ->
%% ServerCertificateType,
%% Padding,
KeyShare,
- %% PreSharedKey,
- %% PSKKeyExchangeModes,
+ PreSharedKey,
+ PSKKeyExchangeModes,
%% EarlyData,
%% Cookie,
SupportedVersions,
@@ -328,9 +344,9 @@ extensions(?'TLS_v1.3' = Version, client_hello) ->
%% oneof([client_cert_type(), undefined]),
%% oneof([server_cert_type(), undefined]),
%% oneof([padding(), undefined]),
- oneof([key_share(client_hello), undefined]),
- %% oneof([pre_shared_key(), undefined]),
- %% oneof([psk_key_exchange_modes(), undefined]),
+ oneof([key_share(MsgType), undefined]),
+ oneof([pre_shared_key(MsgType), undefined]),
+ oneof([psk_key_exchange_modes(), undefined]),
%% oneof([early_data(), undefined]),
%% oneof([cookie(), undefined]),
oneof([client_hello_versions(Version)]),
@@ -357,8 +373,8 @@ extensions(?'TLS_v1.3' = Version, client_hello) ->
%% server_cert_type => ServerCertificateType,
%% padding => Padding,
key_share => KeyShare,
- %% pre_shared_key => PreSharedKey,
- %% psk_key_exhange_modes => PSKKeyExchangeModes,
+ pre_shared_key => PreSharedKey,
+ psk_key_exchange_modes => PSKKeyExchangeModes,
%% early_data => EarlyData,
%% cookie => Cookie,
client_hello_versions => SupportedVersions,
@@ -401,15 +417,15 @@ extensions(Version, client_hello) ->
srp => SRP
%% renegotiation_info => RenegotiationInfo
}));
-extensions(?'TLS_v1.3' = Version, server_hello) ->
+extensions(?'TLS_v1.3' = Version, MsgType = server_hello) ->
?LET({
KeyShare,
- %% PreSharedKeys,
+ PreSharedKey,
SupportedVersions
},
{
- oneof([key_share(server_hello), undefined]),
- %% oneof([pre_shared_keys(), undefined]),
+ oneof([key_share(MsgType), undefined]),
+ oneof([pre_shared_key(MsgType), undefined]),
oneof([server_hello_selected_version()])
},
maps:filter(fun(_, undefined) ->
@@ -419,7 +435,7 @@ extensions(?'TLS_v1.3' = Version, server_hello) ->
end,
#{
key_share => KeyShare,
- %% pre_shared_keys => PreSharedKeys,
+ pre_shared_key => PreSharedKey,
server_hello_selected_version => SupportedVersions
}));
extensions(Version, server_hello) ->
@@ -810,3 +826,58 @@ group_list(N, Pool, Acc) ->
R = rand:uniform(length(Pool)),
G = lists:nth(R, Pool),
group_list(N - 1, Pool -- [G], [G|Acc]).
+
+
+ke_modes() ->
+ oneof([[psk_ke],[psk_dhe_ke],[psk_ke,psk_dhe_ke]]).
+
+psk_key_exchange_modes() ->
+ ?LET(KEModes, ke_modes(),
+ #psk_key_exchange_modes{
+ ke_modes = KEModes}).
+
+pre_shared_key(client_hello) ->
+ ?LET(OfferedPsks, offered_psks(),
+ #pre_shared_key_client_hello{
+ offered_psks = OfferedPsks});
+pre_shared_key(server_hello) ->
+ ?LET(SelectedIdentity, selected_identity(),
+ #pre_shared_key_server_hello{
+ selected_identity = SelectedIdentity}).
+
+selected_identity() ->
+ rand:uniform(32).
+
+offered_psks() ->
+ ?LET(Size, choose(1,5),
+ #offered_psks{
+ identities = psk_identities(Size),
+ binders = psk_binders(Size)}).
+
+psk_identities(Size) ->
+ psk_identities(Size, []).
+%%
+psk_identities(0, Acc) ->
+ Acc;
+psk_identities(N, Acc) ->
+ psk_identities(N - 1, [psk_identity()|Acc]).
+
+psk_identity() ->
+ Len = rand:uniform(32),
+ Identity = crypto:strong_rand_bytes(Len),
+ Age = crypto:strong_rand_bytes(4),
+ #psk_identity{
+ identity = Identity,
+ obfuscated_ticket_age = Age}.
+
+psk_binders(Size) ->
+ psk_binders(Size, []).
+%%
+psk_binders(0, Acc) ->
+ Acc;
+psk_binders(N, Acc) ->
+ psk_binders(N - 1, [psk_binder()|Acc]).
+
+psk_binder() ->
+ Len = rand:uniform(224) + 31,
+ crypto:strong_rand_bytes(Len).
diff --git a/lib/ssl/test/ssl_alert_SUITE.erl b/lib/ssl/test/ssl_alert_SUITE.erl
new file mode 100644
index 0000000000..cc0b636580
--- /dev/null
+++ b/lib/ssl/test/ssl_alert_SUITE.erl
@@ -0,0 +1,100 @@
+%%
+%% Copyright Ericsson AB 2019-2019. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+
+-module(ssl_alert_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("public_key/include/public_key.hrl").
+
+-include_lib("ssl/src/ssl_alert.hrl").
+
+%%--------------------------------------------------------------------
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
+all() ->
+ [
+ alerts,
+ alert_details,
+ alert_details_not_too_big
+ ].
+
+init_per_testcase(_TestCase, Config) ->
+ ct:timetrap({seconds, 5}),
+ Config.
+
+end_per_testcase(_TestCase, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
+alerts() ->
+ [{doc, "Test ssl_alert:alert_txt/1"}].
+alerts(Config) when is_list(Config) ->
+ Descriptions = [?CLOSE_NOTIFY, ?UNEXPECTED_MESSAGE, ?BAD_RECORD_MAC,
+ ?DECRYPTION_FAILED_RESERVED, ?RECORD_OVERFLOW, ?DECOMPRESSION_FAILURE,
+ ?HANDSHAKE_FAILURE, ?BAD_CERTIFICATE, ?UNSUPPORTED_CERTIFICATE,
+ ?CERTIFICATE_REVOKED,?CERTIFICATE_EXPIRED, ?CERTIFICATE_UNKNOWN,
+ ?ILLEGAL_PARAMETER, ?UNKNOWN_CA, ?ACCESS_DENIED, ?DECODE_ERROR,
+ ?DECRYPT_ERROR, ?EXPORT_RESTRICTION, ?PROTOCOL_VERSION,
+ ?INSUFFICIENT_SECURITY, ?INTERNAL_ERROR, ?USER_CANCELED,
+ ?NO_RENEGOTIATION, ?UNSUPPORTED_EXTENSION, ?CERTIFICATE_UNOBTAINABLE,
+ ?UNRECOGNISED_NAME, ?BAD_CERTIFICATE_STATUS_RESPONSE,
+ ?BAD_CERTIFICATE_HASH_VALUE, ?UNKNOWN_PSK_IDENTITY,
+ 255 %% Unsupported/unknow alert will result in a description too
+ ],
+ Alerts = [?ALERT_REC(?WARNING, ?CLOSE_NOTIFY) |
+ [?ALERT_REC(?FATAL, Desc) || Desc <- Descriptions]],
+ lists:foreach(fun(Alert) ->
+ try ssl_alert:alert_txt(Alert)
+ catch
+ C:E:T ->
+ ct:fail({unexpected, {C, E, T}})
+ end
+ end, Alerts).
+%%--------------------------------------------------------------------
+alert_details() ->
+ [{doc, "Test that ssl_alert:alert_txt/1 result contains extendend error description"}].
+alert_details(Config) when is_list(Config) ->
+ Unique = make_ref(),
+ UniqueStr = lists:flatten(io_lib:format("~w", [Unique])),
+ Alert = ?ALERT_REC(?WARNING, ?CLOSE_NOTIFY, Unique),
+ case string:str(ssl_alert:alert_txt(Alert), UniqueStr) of
+ 0 ->
+ ct:fail(error_details_missing);
+ _ ->
+ ok
+ end.
+
+%%--------------------------------------------------------------------
+alert_details_not_too_big() ->
+ [{doc, "Test that ssl_alert:alert_txt/1 limits printed depth of extended error description"}].
+alert_details_not_too_big(Config) when is_list(Config) ->
+ Reason = lists:duplicate(10, lists:duplicate(10, lists:duplicate(10, {some, data}))),
+ Alert = ?ALERT_REC(?WARNING, ?CLOSE_NOTIFY, Reason),
+ case length(ssl_alert:alert_txt(Alert)) < 1000 of
+ true ->
+ ok;
+ false ->
+ ct:fail(ssl_alert_text_too_big)
+ end.
diff --git a/lib/ssl/test/ssl_alpn_handshake_SUITE.erl b/lib/ssl/test/ssl_alpn_SUITE.erl
index dfc780479e..82a49e1469 100644
--- a/lib/ssl/test/ssl_alpn_handshake_SUITE.erl
+++ b/lib/ssl/test/ssl_alpn_SUITE.erl
@@ -19,7 +19,7 @@
%%
%%
--module(ssl_alpn_handshake_SUITE).
+-module(ssl_alpn_SUITE).
%% Note: This directive should only be used in test suites.
-compile(export_all).
@@ -32,7 +32,9 @@
%%--------------------------------------------------------------------
all() ->
- [{group, 'tlsv1.2'},
+ [
+ {group, 'tlsv1.3'},
+ {group, 'tlsv1.2'},
{group, 'tlsv1.1'},
{group, 'tlsv1'},
{group, 'sslv3'},
@@ -42,12 +44,13 @@ all() ->
groups() ->
[
- {'tlsv1.2', [], alpn_tests()},
- {'tlsv1.1', [], alpn_tests()},
- {'tlsv1', [], alpn_tests()},
+ {'tlsv1.3', [], alpn_tests() -- [client_renegotiate, session_reused]},
+ {'tlsv1.2', [], alpn_tests() ++ alpn_npn_coexist()},
+ {'tlsv1.1', [], alpn_tests() ++ alpn_npn_coexist()},
+ {'tlsv1', [], alpn_tests() ++ alpn_npn_coexist()},
{'sslv3', [], alpn_not_supported()},
- {'dtlsv1.2', [], alpn_tests() -- [client_renegotiate]},
- {'dtlsv1', [], alpn_tests() -- [client_renegotiate]}
+ {'dtlsv1.2', [], alpn_tests() ++ alpn_npn_coexist()},
+ {'dtlsv1', [], alpn_tests() ++ alpn_npn_coexist()}
].
alpn_tests() ->
@@ -61,12 +64,16 @@ alpn_tests() ->
client_alpn_and_server_no_support,
client_no_support_and_server_alpn,
client_alpn_npn_and_server_alpn,
- client_alpn_npn_and_server_alpn_npn,
- client_alpn_and_server_alpn_npn,
client_renegotiate,
session_reused
].
+alpn_npn_coexist() ->
+ [
+ client_alpn_npn_and_server_alpn_npn,
+ client_alpn_and_server_alpn_npn
+ ].
+
alpn_not_supported() ->
[alpn_not_supported_client,
alpn_not_supported_server
@@ -77,8 +84,7 @@ init_per_suite(Config0) ->
try crypto:start() of
ok ->
ssl_test_lib:clean_start(),
- Config = ssl_test_lib:make_rsa_cert(Config0),
- ssl_test_lib:cert_options(Config)
+ ssl_test_lib:make_rsa_cert(Config0)
catch _:_ ->
{skip, "Crypto did not start"}
end.
diff --git a/lib/ssl/test/ssl_api_SUITE.erl b/lib/ssl/test/ssl_api_SUITE.erl
new file mode 100644
index 0000000000..c0f23cce58
--- /dev/null
+++ b/lib/ssl/test/ssl_api_SUITE.erl
@@ -0,0 +1,1978 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2019-2019. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(ssl_api_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+-include_lib("common_test/include/ct.hrl").
+-include_lib("ssl/src/ssl_api.hrl").
+
+-define(SLEEP, 500).
+%%--------------------------------------------------------------------
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
+
+all() ->
+ [
+ {group, 'tlsv1.3'},
+ {group, 'tlsv1.2'},
+ {group, 'tlsv1.1'},
+ {group, 'tlsv1'},
+ {group, 'sslv3'},
+ {group, 'dtlsv1.2'},
+ {group, 'dtlsv1'}
+ ].
+
+groups() ->
+ [
+ {'tlsv1.3', [], ((gen_api_tests() ++ tls13_group() ++ handshake_paus_tests()) -- [dh_params, honor_server_cipher_order, honor_client_cipher_order,
+ new_options_in_handshake])
+ ++ (since_1_2() -- [conf_signature_algs])},
+ {'tlsv1.2', [], gen_api_tests() ++ since_1_2() ++ handshake_paus_tests() ++ pre_1_3()},
+ {'tlsv1.1', [], gen_api_tests() ++ handshake_paus_tests() ++ pre_1_3()},
+ {'tlsv1', [], gen_api_tests() ++ handshake_paus_tests() ++ pre_1_3() ++ beast_mitigation_test()},
+ {'sslv3', [], (gen_api_tests() -- [new_options_in_handshake]) ++ beast_mitigation_test() ++ pre_1_3()},
+ {'dtlsv1.2', [], (gen_api_tests() -- [invalid_keyfile, invalid_certfile, invalid_cacertfile,
+ invalid_options, new_options_in_handshake]) ++ handshake_paus_tests() ++ pre_1_3()},
+ {'dtlsv1', [], (gen_api_tests() -- [invalid_keyfile, invalid_certfile, invalid_cacertfile,
+ invalid_options, new_options_in_handshake]) ++ handshake_paus_tests() ++ pre_1_3()}
+ ].
+
+since_1_2() ->
+ [
+ conf_signature_algs,
+ no_common_signature_algs
+ ].
+
+pre_1_3() ->
+ [
+ default_reject_anonymous
+ ].
+gen_api_tests() ->
+ [
+ peercert,
+ peercert_with_client_cert,
+ connection_information,
+ secret_connection_info,
+ versions,
+ active_n,
+ dh_params,
+ hibernate,
+ hibernate_right_away,
+ listen_socket,
+ recv_active,
+ recv_active_once,
+ recv_active_n,
+ recv_timeout,
+ recv_close,
+ controlling_process,
+ controller_dies,
+ controlling_process_transport_accept_socket,
+ close_with_timeout,
+ close_in_error_state,
+ call_in_error_state,
+ close_transport_accept,
+ abuse_transport_accept_socket,
+ honor_server_cipher_order,
+ honor_client_cipher_order,
+ ipv6,
+ der_input,
+ new_options_in_handshake,
+ max_handshake_size,
+ invalid_certfile,
+ invalid_cacertfile,
+ invalid_keyfile,
+ options_not_proplist,
+ invalid_options
+ ].
+
+handshake_paus_tests() ->
+ [
+ handshake_continue,
+ handshake_continue_timeout,
+ hello_client_cancel,
+ hello_server_cancel
+ ].
+
+%% Only relevant for SSL 3.0 and TLS 1.1
+beast_mitigation_test() ->
+ [%% Original option
+ rizzo_disabled,
+ %% Same effect as disable
+ rizzo_zero_n,
+ %% Same as default
+ rizzo_one_n_minus_one
+ ].
+
+tls13_group() ->
+ [
+ supported_groups,
+ honor_server_cipher_order_tls13,
+ honor_client_cipher_order_tls13
+ ].
+
+
+init_per_suite(Config0) ->
+ catch crypto:stop(),
+ try crypto:start() of
+ ok ->
+ ssl_test_lib:clean_start(),
+ ssl_test_lib:make_rsa_cert(Config0)
+ catch _:_ ->
+ {skip, "Crypto did not start"}
+ end.
+
+end_per_suite(_Config) ->
+ ssl:stop(),
+ application:unload(ssl),
+ application:stop(crypto).
+
+
+init_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ case ssl_test_lib:sufficient_crypto_support(GroupName) of
+ true ->
+ [{client_type, erlang},
+ {server_type, erlang} | ssl_test_lib:init_tls_version(GroupName, Config)];
+ false ->
+ {skip, "Missing crypto support"}
+ end;
+ _ ->
+ ssl:start(),
+ Config
+ end.
+
+end_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ ssl_test_lib:clean_tls_version(Config);
+ false ->
+ Config
+ end.
+
+init_per_testcase(prf, Config) ->
+ ssl_test_lib:ct_log_supported_protocol_versions(Config),
+ ct:timetrap({seconds, 10}),
+ Version = ssl_test_lib:protocol_version(Config),
+ PRFS = [md5, sha, sha256, sha384, sha512],
+ %% All are the result of running tls_v1:prf(PrfAlgo, <<>>, <<>>, <<>>, 16)
+ %% with the specified PRF algorithm
+ ExpectedPrfResults =
+ [{md5, <<96,139,180,171,236,210,13,10,28,32,2,23,88,224,235,199>>},
+ {sha, <<95,3,183,114,33,169,197,187,231,243,19,242,220,228,70,151>>},
+ {sha256, <<166,249,145,171,43,95,158,232,6,60,17,90,183,180,0,155>>},
+ {sha384, <<153,182,217,96,186,130,105,85,65,103,123,247,146,91,47,106>>},
+ {sha512, <<145,8,98,38,243,96,42,94,163,33,53,49,241,4,127,28>>},
+ %% TLS 1.0 and 1.1 PRF:
+ {md5sha, <<63,136,3,217,205,123,200,177,251,211,17,229,132,4,173,80>>}],
+ TestPlan = prf_create_plan([Version], PRFS, ExpectedPrfResults),
+ [{prf_test_plan, TestPlan} | Config];
+init_per_testcase(_TestCase, Config) ->
+ ssl_test_lib:ct_log_supported_protocol_versions(Config),
+ ct:timetrap({seconds, 10}),
+ Config.
+
+end_per_testcase(internal_active_n, _Config) ->
+ application:unset_env(ssl, internal_active_n);
+end_per_testcase(_TestCase, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
+peercert() ->
+ [{doc,"Test API function peercert/1"}].
+peercert(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, peercert_result, []}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, peercert_result, []}},
+ {options, ClientOpts}]),
+
+ CertFile = proplists:get_value(certfile, ServerOpts),
+ [{'Certificate', BinCert, _}]= ssl_test_lib:pem_to_der(CertFile),
+
+ ServerMsg = {error, no_peercert},
+ ClientMsg = {ok, BinCert},
+
+ ct:log("Testcase ~p, Client ~p Server ~p ~n",
+ [self(), Client, Server]),
+
+ ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+
+peercert_with_client_cert() ->
+ [{doc,"Test API function peercert/1"}].
+peercert_with_client_cert(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, peercert_result, []}},
+ {options, [{verify, verify_peer} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, peercert_result, []}},
+ {options, ClientOpts}]),
+
+ ServerCertFile = proplists:get_value(certfile, ServerOpts),
+ [{'Certificate', ServerBinCert, _}]= ssl_test_lib:pem_to_der(ServerCertFile),
+ ClientCertFile = proplists:get_value(certfile, ClientOpts),
+ [{'Certificate', ClientBinCert, _}]= ssl_test_lib:pem_to_der(ClientCertFile),
+
+ ServerMsg = {ok, ClientBinCert},
+ ClientMsg = {ok, ServerBinCert},
+
+ ct:log("Testcase ~p, Client ~p Server ~p ~n",
+ [self(), Client, Server]),
+
+ ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+connection_information() ->
+ [{doc,"Test the API function ssl:connection_information/1"}].
+connection_information(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, connection_information_result, []}},
+ {options, ServerOpts}]),
+
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, connection_information_result, []}},
+ {options, ClientOpts}]),
+
+ ct:log("Testcase ~p, Client ~p Server ~p ~n",
+ [self(), Client, Server]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+%%--------------------------------------------------------------------
+
+secret_connection_info() ->
+ [{doc,"Test the API function ssl:connection_information/2"}].
+secret_connection_info(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, secret_connection_info_result, []}},
+ {options, [{verify, verify_peer} | ServerOpts]}]),
+
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, secret_connection_info_result, []}},
+ {options, [{verify, verify_peer} |ClientOpts]}]),
+
+ ct:log("Testcase ~p, Client ~p Server ~p ~n",
+ [self(), Client, Server]),
+
+ ssl_test_lib:check_result(Server, true, Client, true),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+%%--------------------------------------------------------------------
+prf() ->
+ [{doc,"Test that ssl:prf/5 uses the negotiated PRF."}].
+prf(Config) when is_list(Config) ->
+ TestPlan = proplists:get_value(prf_test_plan, Config),
+ case TestPlan of
+ [] -> ct:fail({error, empty_prf_test_plan});
+ _ -> lists:foreach(fun(Suite) ->
+ lists:foreach(
+ fun(Test) ->
+ V = proplists:get_value(tls_ver, Test),
+ C = proplists:get_value(ciphers, Test),
+ E = proplists:get_value(expected, Test),
+ P = proplists:get_value(prf, Test),
+ prf_run_test(Config, V, C, E, P)
+ end, Suite)
+ end, TestPlan)
+ end.
+
+%%--------------------------------------------------------------------
+dh_params() ->
+ [{doc,"Test to specify DH-params file in server."}].
+
+dh_params(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ DataDir = proplists:get_value(data_dir, Config),
+ DHParamFile = filename:join(DataDir, "dHParam.pem"),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, [{dhfile, DHParamFile} | 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,
+ [{ciphers,[{dhe_rsa,aes_256_cbc,sha}]} |
+ ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+conf_signature_algs() ->
+ [{doc,"Test to set the signature_algs option on both client and server"}].
+conf_signature_algs(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result, []}},
+ {options, [{active, false}, {signature_algs, [{sha256, rsa}]} | 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, []}},
+ {options, [{active, false}, {signature_algs, [{sha256, rsa}]} | ClientOpts]}]),
+
+ ct:log("Testcase ~p, Client ~p Server ~p ~n",
+ [self(), Client, Server]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+
+%%--------------------------------------------------------------------
+no_common_signature_algs() ->
+ [{doc,"Set the signature_algs option so that there client and server does not share any hash sign algorithms"}].
+no_common_signature_algs(Config) when is_list(Config) ->
+
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+
+ Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {options, [{signature_algs, [{sha256, rsa}]}
+ | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {options, [{signature_algs, [{sha384, rsa}]}
+ | ClientOpts]}]),
+
+ ssl_test_lib:check_server_alert(Server, Client, insufficient_security).
+
+%%--------------------------------------------------------------------
+handshake_continue() ->
+ [{doc, "Test API function ssl:handshake_continue/3"}].
+handshake_continue(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, ssl_test_lib:ssl_options([{reuseaddr, true},
+ {verify, verify_peer},
+ {handshake, hello} | ServerOpts
+ ],
+ Config)},
+ {continue_options, proplists:delete(reuseaddr, 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, ssl_test_lib:ssl_options([{handshake, hello},
+ {verify, verify_peer} | ClientOpts
+ ],
+ Config)},
+ {continue_options, proplists:delete(reuseaddr, ClientOpts)}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+
+%%------------------------------------------------------------------
+handshake_continue_timeout() ->
+ [{doc, "Test API function ssl:handshake_continue/3 with short timeout"}].
+handshake_continue_timeout(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {timeout, 1},
+ {options, ssl_test_lib:ssl_options([{reuseaddr, true}, {handshake, hello},
+ {verify, verify_peer} | ServerOpts],
+ Config)},
+ {continue_options, proplists:delete(reuseaddr, ServerOpts)}
+ ]),
+
+ Port = ssl_test_lib:inet_port(Server),
+
+
+ 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,timeout}),
+ ssl_test_lib:close(Server).
+
+
+%%--------------------------------------------------------------------
+hello_client_cancel() ->
+ [{doc, "Test API function ssl:handshake_cancel/1 on the client side"}].
+hello_client_cancel(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {options, ssl_test_lib:ssl_options([{handshake, hello},
+ {verify, verify_peer} | ServerOpts], Config)},
+ {continue_options, proplists:delete(reuseaddr, ServerOpts)}]),
+
+ Port = ssl_test_lib:inet_port(Server),
+
+ %% That is ssl:handshake_cancel returns ok
+ {connect_failed, ok} = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {options, ssl_test_lib:ssl_options([{handshake, hello},
+ {verify, verify_peer} | ClientOpts], Config)},
+ {continue_options, cancel}]),
+ ssl_test_lib:check_server_alert(Server, user_canceled).
+%%--------------------------------------------------------------------
+hello_server_cancel() ->
+ [{doc, "Test API function ssl:handshake_cancel/1 on the server side"}].
+hello_server_cancel(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {options, ssl_test_lib:ssl_options([{handshake, hello},
+ {verify, verify_peer} | ServerOpts
+ ], Config)},
+ {continue_options, cancel}]),
+
+ Port = ssl_test_lib:inet_port(Server),
+
+ ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {options, ssl_test_lib:ssl_options([{handshake, hello},
+ {verify, verify_peer} | ClientOpts
+ ], Config)},
+ {continue_options, proplists:delete(reuseaddr, ClientOpts)}]),
+
+ ssl_test_lib:check_result(Server, ok).
+
+%%--------------------------------------------------------------------
+versions() ->
+ [{doc,"Test API function versions/0"}].
+
+versions(Config) when is_list(Config) ->
+ [_|_] = Versions = ssl:versions(),
+ ct:log("~p~n", [Versions]).
+
+%%--------------------------------------------------------------------
+%% Test case adapted from gen_tcp_misc_SUITE.
+active_n() ->
+ [{doc,"Test {active,N} option"}].
+
+active_n(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ Port = ssl_test_lib:inet_port(node()),
+ N = 3,
+ LS = ok(ssl:listen(Port, [{active,N}|ServerOpts])),
+ [{active,N}] = ok(ssl:getopts(LS, [active])),
+ active_n_common(LS, N),
+ Self = self(),
+ spawn_link(fun() ->
+ S0 = ok(ssl:transport_accept(LS)),
+ {ok, S} = ssl:handshake(S0),
+ ok = ssl:setopts(S, [{active,N}]),
+ [{active,N}] = ok(ssl:getopts(S, [active])),
+ ssl:controlling_process(S, Self),
+ Self ! {server, S}
+ end),
+ C = ok(ssl:connect("localhost", Port, [{active,N}|ClientOpts])),
+ [{active,N}] = ok(ssl:getopts(C, [active])),
+ S = receive
+ {server, S0} -> S0
+ after
+ 1000 ->
+ exit({error, connect})
+ end,
+ active_n_common(C, N),
+ active_n_common(S, N),
+ ok = ssl:setopts(C, [{active,N}]),
+ ok = ssl:setopts(S, [{active,N}]),
+ ReceiveMsg = fun(Socket, Msg) ->
+ receive
+ {ssl,Socket,Msg} ->
+ ok;
+ {ssl,Socket,Begin} ->
+ receive
+ {ssl,Socket,End} ->
+ Msg = Begin ++ End,
+ ok
+ after 1000 ->
+ exit(timeout)
+ end
+ after 1000 ->
+ exit(timeout)
+ end
+ end,
+ repeat(3, fun(I) ->
+ Msg = "message "++integer_to_list(I),
+ ok = ssl:send(C, Msg),
+ ReceiveMsg(S, Msg),
+ ok = ssl:send(S, Msg),
+ ReceiveMsg(C, Msg)
+ end),
+ receive
+ {ssl_passive,S} ->
+ [{active,false}] = ok(ssl:getopts(S, [active]))
+ after
+ 1000 ->
+ exit({error,ssl_passive})
+ end,
+ receive
+ {ssl_passive,C} ->
+ [{active,false}] = ok(ssl:getopts(C, [active]))
+ after
+ 1000 ->
+ exit({error,ssl_passive})
+ end,
+ LS2 = ok(ssl:listen(0, [{active,0}])),
+ receive
+ {ssl_passive,LS2} ->
+ [{active,false}] = ok(ssl:getopts(LS2, [active]))
+ after
+ 1000 ->
+ exit({error,ssl_passive})
+ end,
+ ok = ssl:close(LS2),
+ ok = ssl:close(C),
+ ok = ssl:close(S),
+ ok = ssl:close(LS),
+ ok.
+
+hibernate() ->
+ [{doc,"Check that an SSL connection that is started with option "
+ "{hibernate_after, 1000} indeed hibernates after 1000ms of "
+ "inactivity"}].
+
+hibernate(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ {Client, #sslsocket{pid=[Pid|_]}} = ssl_test_lib:start_client([return_socket,
+ {node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, [{hibernate_after, 1000}|ClientOpts]}]),
+ {current_function, _} =
+ process_info(Pid, current_function),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ct:sleep(1500),
+ {current_function, {erlang, hibernate, 3}} =
+ process_info(Pid, current_function),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+
+hibernate_right_away() ->
+ [{doc,"Check that an SSL connection that is configured to hibernate "
+ "after 0 or 1 milliseconds hibernates as soon as possible and not "
+ "crashes"}].
+
+hibernate_right_away(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ StartServerOpts = [{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, ServerOpts}],
+ StartClientOpts = [return_socket,
+ {node, ClientNode},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}}],
+
+ Server1 = ssl_test_lib:start_server(StartServerOpts),
+ Port1 = ssl_test_lib:inet_port(Server1),
+ {Client1, #sslsocket{pid = [Pid1|_]}} = ssl_test_lib:start_client(StartClientOpts ++
+ [{port, Port1}, {options, [{hibernate_after, 0}|ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server1, ok, Client1, ok),
+
+ ct:sleep(1000), %% Schedule out
+
+ {current_function, {erlang, hibernate, 3}} =
+ process_info(Pid1, current_function),
+ ssl_test_lib:close(Server1),
+ ssl_test_lib:close(Client1),
+
+ Server2 = ssl_test_lib:start_server(StartServerOpts),
+ Port2 = ssl_test_lib:inet_port(Server2),
+ {Client2, #sslsocket{pid = [Pid2|_]}} = ssl_test_lib:start_client(StartClientOpts ++
+ [{port, Port2}, {options, [{hibernate_after, 1}|ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server2, ok, Client2, ok),
+
+ ct:sleep(1000), %% Schedule out
+
+ {current_function, {erlang, hibernate, 3}} =
+ process_info(Pid2, current_function),
+
+ ssl_test_lib:close(Server2),
+ ssl_test_lib:close(Client2).
+
+listen_socket() ->
+ [{doc,"Check error handling and inet compliance when calling API functions with listen sockets."}].
+
+listen_socket(Config) ->
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ {ok, ListenSocket} = ssl:listen(0, ServerOpts),
+
+ %% This can be a valid thing to do as
+ %% options are inherited by the accept socket
+ ok = ssl:controlling_process(ListenSocket, self()),
+
+ {ok, _} = ssl:sockname(ListenSocket),
+
+ {error, enotconn} = ssl:send(ListenSocket, <<"data">>),
+ {error, enotconn} = ssl:recv(ListenSocket, 0),
+ {error, enotconn} = ssl:connection_information(ListenSocket),
+ {error, enotconn} = ssl:peername(ListenSocket),
+ {error, enotconn} = ssl:peercert(ListenSocket),
+ {error, enotconn} = ssl:renegotiate(ListenSocket),
+ {error, enotconn} = ssl:prf(ListenSocket, 'master_secret', <<"Label">>, [client_random], 256),
+ {error, enotconn} = ssl:shutdown(ListenSocket, read_write),
+
+ ok = ssl:close(ListenSocket).
+
+%%--------------------------------------------------------------------
+recv_active() ->
+ [{doc,"Test recv on active socket"}].
+
+recv_active(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, try_recv_active, []}},
+ {options, [{active, true} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client =
+ ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, try_recv_active, []}},
+ {options, [{active, true} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+recv_active_once() ->
+ [{doc,"Test recv on active (once) socket"}].
+
+recv_active_once(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, try_recv_active_once, []}},
+ {options, [{active, once} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client =
+ ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, try_recv_active_once, []}},
+ {options, [{active, once} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+recv_active_n() ->
+ [{doc,"Test recv on active (n) socket"}].
+
+recv_active_n(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, try_recv_active_once, []}},
+ {options, [{active, 1} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client =
+ ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, try_recv_active_once, []}},
+ {options, [{active, 1} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+%%--------------------------------------------------------------------
+recv_timeout() ->
+ [{doc,"Test ssl:ssl_accept timeout"}].
+
+recv_timeout(Config) ->
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result_timeout_server, []}},
+ {options, [{active, false} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ send_recv_result_timeout_client, []}},
+ {options, [{active, false} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client, ok, Server, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+recv_close() ->
+ [{doc,"Special case of call error handling"}].
+recv_close(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, do_recv_close, []}},
+ {options, [{active, false} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ {_Client, #sslsocket{} = SslSocket} = ssl_test_lib:start_client([return_socket,
+ {node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {options, ClientOpts}]),
+ ssl:close(SslSocket),
+ ssl_test_lib:check_result(Server, ok).
+
+
+
+%%--------------------------------------------------------------------
+controlling_process() ->
+ [{doc,"Test API function controlling_process/2"}].
+
+controlling_process(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ ClientMsg = "Server hello",
+ ServerMsg = "Client hello",
+
+ Server = ssl_test_lib:start_server([
+ {node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE,
+ controlling_process_result, [self(),
+ ServerMsg]}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ {Client, CSocket} = ssl_test_lib:start_client([return_socket,
+ {node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ controlling_process_result, [self(),
+ ClientMsg]}},
+ {options, ClientOpts}]),
+
+ ct:log("Testcase ~p, Client ~p Server ~p ~n",
+ [self(), Client, Server]),
+
+ ServerMsg = ssl_test_lib:active_recv(CSocket, length(ServerMsg)),
+ %% We do not have the TLS server socket but all messages form the client
+ %% socket are now read, so ramining are form the server socket
+ ClientMsg = ssl_active_recv(length(ClientMsg)),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+%%--------------------------------------------------------------------
+controller_dies() ->
+ [{doc,"Test that the socket is closed after controlling process dies"}].
+controller_dies(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ ClientMsg = "Hello server",
+ ServerMsg = "Hello client",
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE,
+ controller_dies_result, [self(),
+ ServerMsg]}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ controller_dies_result, [self(),
+ ClientMsg]}},
+ {options, ClientOpts}]),
+
+ ct:log("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]),
+ ct:sleep(?SLEEP), %% so that they are connected
+
+ process_flag(trap_exit, true),
+
+ %% Test that clients die
+ exit(Client, killed),
+ get_close(Client, ?LINE),
+
+ %% Test that clients die when process disappear
+ Server ! listen,
+ Tester = self(),
+ Connect = fun(Pid) ->
+ {ok, Socket} = ssl:connect(Hostname, Port, ClientOpts),
+ %% Make sure server finishes and verification
+ %% and is in coonection state before
+ %% killing client
+ ct:sleep(?SLEEP),
+ Pid ! {self(), connected, Socket},
+ receive die_nice -> normal end
+ end,
+ Client2 = spawn_link(fun() -> Connect(Tester) end),
+ receive {Client2, connected, _Socket} -> Client2 ! die_nice end,
+
+ get_close(Client2, ?LINE),
+
+ %% Test that clients die when the controlling process have changed
+ Server ! listen,
+
+ Client3 = spawn_link(fun() -> Connect(Tester) end),
+ Controller = spawn_link(fun() -> receive die_nice -> normal end end),
+ receive
+ {Client3, connected, Socket} ->
+ ok = ssl:controlling_process(Socket, Controller),
+ Client3 ! die_nice
+ end,
+
+ ct:log("Wating on exit ~p~n",[Client3]),
+ receive {'EXIT', Client3, normal} -> ok end,
+
+ receive %% Client3 is dead but that doesn't matter, socket should not be closed.
+ Unexpected ->
+ ct:log("Unexpected ~p~n",[Unexpected]),
+ ct:fail({line, ?LINE-1})
+ after 1000 ->
+ ok
+ end,
+ Controller ! die_nice,
+ get_close(Controller, ?LINE),
+
+ %% Test that servers die
+ Server ! listen,
+ LastClient = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ controller_dies_result, [self(),
+ ClientMsg]}},
+ {options, ClientOpts}]),
+ ct:sleep(?SLEEP), %% so that they are connected
+
+ exit(Server, killed),
+ get_close(Server, ?LINE),
+ process_flag(trap_exit, false),
+ ssl_test_lib:close(LastClient).
+%%--------------------------------------------------------------------
+controlling_process_transport_accept_socket() ->
+ [{doc,"Only ssl:handshake and ssl:controlling_process is allowed for transport_accept:sockets"}].
+controlling_process_transport_accept_socket(Config) when is_list(Config) ->
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server_transport_control([{node, ServerNode},
+ {port, 0},
+ {from, self()},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ _Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {options, ClientOpts}]),
+ ssl_test_lib:check_result(Server, ok),
+ ssl_test_lib:close(Server).
+
+%%--------------------------------------------------------------------
+close_with_timeout() ->
+ [{doc,"Test normal (not downgrade) ssl:close/2"}].
+close_with_timeout(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, tls_close, []}},
+ {options,[{active, false} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, tls_close, []}},
+ {options, [{active, false} |ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok).
+
+%%--------------------------------------------------------------------
+close_in_error_state() ->
+ [{doc,"Special case of closing socket in error state"}].
+close_in_error_state(Config) when is_list(Config) ->
+ ServerOpts0 = ssl_test_lib:ssl_options(server_opts, Config),
+ ServerOpts = [{cacertfile, "foo.pem"} | proplists:delete(cacertfile, ServerOpts0)],
+ ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
+ _ = spawn_link(?MODULE, run_error_server_close, [[self() | ServerOpts]]),
+ receive
+ {_Pid, Port} ->
+ spawn_link(?MODULE, run_client_error, [[Port, ClientOpts]])
+ end,
+ receive
+ ok ->
+ ok;
+ Other ->
+ ct:fail(Other)
+ end.
+
+%%--------------------------------------------------------------------
+call_in_error_state() ->
+ [{doc,"Special case of call error handling"}].
+call_in_error_state(Config) when is_list(Config) ->
+ ServerOpts0 = ssl_test_lib:ssl_options(server_opts, Config),
+ ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
+ ServerOpts = [{cacertfile, "foo.pem"} | proplists:delete(cacertfile, ServerOpts0)],
+ Pid = spawn_link(?MODULE, run_error_server, [[self() | ServerOpts]]),
+ receive
+ {Pid, Port} ->
+ spawn_link(?MODULE, run_client_error, [[Port, ClientOpts]])
+ end,
+ receive
+ {error, closed} ->
+ ok;
+ Other ->
+ ct:fail(Other)
+ end.
+%%--------------------------------------------------------------------
+close_transport_accept() ->
+ [{doc,"Tests closing ssl socket when waiting on ssl:transport_accept/1"}].
+
+close_transport_accept(Config) when is_list(Config) ->
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ {_ClientNode, ServerNode, _Hostname} = ssl_test_lib:run_where(Config),
+
+ Port = 0,
+ Opts = [{active, false} | ServerOpts],
+ {ok, ListenSocket} = rpc:call(ServerNode, ssl, listen, [Port, Opts]),
+ spawn_link(fun() ->
+ ct:sleep(?SLEEP),
+ rpc:call(ServerNode, ssl, close, [ListenSocket])
+ end),
+ case rpc:call(ServerNode, ssl, transport_accept, [ListenSocket]) of
+ {error, closed} ->
+ ok;
+ Other ->
+ exit({?LINE, Other})
+ end.
+%%--------------------------------------------------------------------
+abuse_transport_accept_socket() ->
+ [{doc,"Only ssl:handshake and ssl:controlling_process is allowed for transport_accept:sockets"}].
+abuse_transport_accept_socket(Config) when is_list(Config) ->
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server_transport_abuse_socket([{node, ServerNode},
+ {port, 0},
+ {from, self()},
+ {options, 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, no_result, []}},
+ {options, ClientOpts}]),
+ ssl_test_lib:check_result(Server, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+
+invalid_keyfile() ->
+ [{doc,"Test what happens with an invalid key file"}].
+invalid_keyfile(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ BadKeyFile = filename:join([proplists:get_value(priv_dir, Config),
+ "badkey.pem"]),
+ BadOpts = [{keyfile, BadKeyFile}| proplists:delete(keyfile, ServerOpts)],
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server =
+ ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {options, BadOpts}]),
+
+ Port = ssl_test_lib:inet_port(Server),
+
+ Client =
+ ssl_test_lib:start_client_error([{node, ClientNode},
+ {port, Port}, {host, Hostname},
+ {from, self()}, {options, ClientOpts}]),
+
+ File = proplists:get_value(keyfile,BadOpts),
+ ssl_test_lib:check_result(Server, {error,{options, {keyfile, File, {error,enoent}}}}, Client,
+ {error, closed}).
+
+%%--------------------------------------------------------------------
+honor_server_cipher_order() ->
+ [{doc,"Test API honor server cipher order."}].
+honor_server_cipher_order(Config) when is_list(Config) ->
+ ClientCiphers = [#{key_exchange => dhe_rsa,
+ cipher => aes_128_cbc,
+ mac => sha,
+ prf => default_prf},
+ #{key_exchange => dhe_rsa,
+ cipher => aes_256_cbc,
+ mac => sha,
+ prf => default_prf}],
+ ServerCiphers = [#{key_exchange => dhe_rsa,
+ cipher => aes_256_cbc,
+ mac =>sha,
+ prf => default_prf},
+ #{key_exchange => dhe_rsa,
+ cipher => aes_128_cbc,
+ mac => sha,
+ prf => default_prf}],
+ honor_cipher_order(Config, true, ServerCiphers, ClientCiphers, #{key_exchange => dhe_rsa,
+ cipher => aes_256_cbc,
+ mac => sha,
+ prf => default_prf}).
+
+%%--------------------------------------------------------------------
+honor_client_cipher_order() ->
+ [{doc,"Test API honor server cipher order."}].
+honor_client_cipher_order(Config) when is_list(Config) ->
+ ClientCiphers = [#{key_exchange => dhe_rsa,
+ cipher => aes_128_cbc,
+ mac => sha,
+ prf => default_prf},
+ #{key_exchange => dhe_rsa,
+ cipher => aes_256_cbc,
+ mac => sha,
+ prf => default_prf}],
+ ServerCiphers = [#{key_exchange => dhe_rsa,
+ cipher => aes_256_cbc,
+ mac =>sha,
+ prf => default_prf},
+ #{key_exchange => dhe_rsa,
+ cipher => aes_128_cbc,
+ mac => sha,
+ prf => default_prf}],
+ honor_cipher_order(Config, false, ServerCiphers, ClientCiphers, #{key_exchange => dhe_rsa,
+ cipher => aes_128_cbc,
+ mac => sha,
+ prf => default_prf}).
+
+%%--------------------------------------------------------------------
+ipv6() ->
+ [{require, ipv6_hosts},
+ {doc,"Test ipv6."}].
+ipv6(Config) when is_list(Config) ->
+ {ok, Hostname0} = inet:gethostname(),
+
+ case lists:member(list_to_atom(Hostname0), ct:get_config(ipv6_hosts)) of
+ true ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ {ClientNode, ServerNode, Hostname} =
+ ssl_test_lib:run_where(Config, ipv6),
+ Server = ssl_test_lib:start_server([{node, ServerNode},
+ {port, 0}, {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result, []}},
+ {options,
+ [inet6, {active, false} | 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, []}},
+ {options,
+ [inet6, {active, false} | ClientOpts]}]),
+
+ ct:log("Testcase ~p, Client ~p Server ~p ~n",
+ [self(), Client, Server]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client);
+ false ->
+ {skip, "Host does not support IPv6"}
+ end.
+
+%%--------------------------------------------------------------------
+der_input() ->
+ [{doc,"Test to input certs and key as der"}].
+
+der_input(Config) when is_list(Config) ->
+ DataDir = proplists:get_value(data_dir, Config),
+ DHParamFile = filename:join(DataDir, "dHParam.pem"),
+
+ {status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)),
+ [_, _,_, _, Prop] = StatusInfo,
+ State = ssl_test_lib:state(Prop),
+ [CADb | _] = element(6, State),
+ ct:sleep(?SLEEP*2), %%Make sure there is no outstanding clean cert db msg in manager
+ Size = ets:info(CADb, size),
+ ct:pal("Size ~p", [Size]),
+
+ SeverVerifyOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ {ServerCert, ServerKey, ServerCaCerts, DHParams} = der_input_opts([{dhfile, DHParamFile} |
+ SeverVerifyOpts]),
+ ClientVerifyOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ {ClientCert, ClientKey, ClientCaCerts, DHParams} = der_input_opts([{dhfile, DHParamFile} |
+ ClientVerifyOpts]),
+ ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true},
+ {dh, DHParams},
+ {cert, ServerCert}, {key, ServerKey}, {cacerts, ServerCaCerts}],
+ ClientOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true},
+ {dh, DHParams},
+ {cert, ClientCert}, {key, ClientKey}, {cacerts, ClientCaCerts}],
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result, []}},
+ {options, [{active, false} | 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, []}},
+ {options, [{active, false} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client),
+ %% Using only DER input should not increase file indexed DB
+ Size = ets:info(CADb, size).
+
+%%--------------------------------------------------------------------
+invalid_certfile() ->
+ [{doc,"Test what happens with an invalid cert file"}].
+
+invalid_certfile(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ BadCertFile = filename:join([proplists:get_value(priv_dir, Config),
+ "badcert.pem"]),
+ ServerBadOpts = [{certfile, BadCertFile}| proplists:delete(certfile, ServerOpts)],
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server =
+ ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {options, ServerBadOpts}]),
+
+ Port = ssl_test_lib:inet_port(Server),
+
+ Client =
+ ssl_test_lib:start_client_error([{node, ClientNode},
+ {port, Port}, {host, Hostname},
+ {from, self()},
+ {options, ClientOpts}]),
+ File = proplists:get_value(certfile, ServerBadOpts),
+ ssl_test_lib:check_result(Server, {error,{options, {certfile, File, {error,enoent}}}},
+ Client, {error, closed}).
+
+
+%%--------------------------------------------------------------------
+invalid_cacertfile() ->
+ [{doc,"Test what happens with an invalid cacert file"}].
+
+invalid_cacertfile(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ BadCACertFile = filename:join([proplists:get_value(priv_dir, Config),
+ "badcacert.pem"]),
+ ServerBadOpts = [{cacertfile, BadCACertFile}| proplists:delete(cacertfile, ServerOpts)],
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server0 =
+ ssl_test_lib:start_server_error([{node, ServerNode},
+ {port, 0}, {from, self()},
+ {options, ServerBadOpts}]),
+
+ Port0 = ssl_test_lib:inet_port(Server0),
+
+
+ Client0 =
+ ssl_test_lib:start_client_error([{node, ClientNode},
+ {port, Port0}, {host, Hostname},
+ {from, self()},
+ {options, ClientOpts}]),
+
+ File0 = proplists:get_value(cacertfile, ServerBadOpts),
+
+ ssl_test_lib:check_result(Server0, {error, {options, {cacertfile, File0,{error,enoent}}}},
+ Client0, {error, closed}),
+
+ File = File0 ++ "do_not_exit.pem",
+ ServerBadOpts1 = [{cacertfile, File}|proplists:delete(cacertfile, ServerBadOpts)],
+
+ Server1 =
+ ssl_test_lib:start_server_error([{node, ServerNode},
+ {port, 0}, {from, self()},
+ {options, ServerBadOpts1}]),
+
+ Port1 = ssl_test_lib:inet_port(Server1),
+
+ Client1 =
+ ssl_test_lib:start_client_error([{node, ClientNode},
+ {port, Port1}, {host, Hostname},
+ {from, self()},
+ {options, ClientOpts}]),
+
+
+ ssl_test_lib:check_result(Server1, {error, {options, {cacertfile, File,{error,enoent}}}},
+ Client1, {error, closed}),
+ ok.
+
+%%--------------------------------------------------------------------
+new_options_in_handshake() ->
+ [{doc,"Test that you can set ssl options in handshake/3 and not only in tcp upgrade"}].
+new_options_in_handshake(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ Version = ssl_test_lib:protocol_version(Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ [_, Cipher | _] = ssl:filter_cipher_suites(ssl:cipher_suites(all, Version),
+ [{key_exchange,
+ fun(dhe_rsa) ->
+ true;
+ (ecdhe_rsa) ->
+ true;
+ (ecdh_rsa) ->
+ true;
+ (rsa) ->
+ true;
+ (_) ->
+ false
+ end
+ }]),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {ssl_extra_opts, [{versions, [Version]},
+ {ciphers,[Cipher]}]}, %% To be set in ssl_accept/3
+ {mfa, {?MODULE, connection_info_result, []}},
+ {options, ServerOpts}]),
+
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, connection_info_result, []}},
+ {options, [{ciphers, [Cipher]} | ClientOpts]}]),
+
+ ct:log("Testcase ~p, Client ~p Server ~p ~n",
+ [self(), Client, Server]),
+
+ ServerMsg = ClientMsg = {ok, {Version, Cipher}},
+
+ ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%-------------------------------------------------------------------
+max_handshake_size() ->
+ [{doc,"Test that we can set max_handshake_size to max value."}].
+
+max_handshake_size(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, [{max_handshake_size, 8388607} |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, [{max_handshake_size, 8388607} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok).
+
+
+%%-------------------------------------------------------------------
+options_not_proplist() ->
+ [{doc,"Test what happens if an option is not a key value tuple"}].
+
+options_not_proplist(Config) when is_list(Config) ->
+ BadOption = {client_preferred_next_protocols,
+ client, [<<"spdy/3">>,<<"http/1.1">>], <<"http/1.1">>},
+ {option_not_a_key_value_tuple, BadOption} =
+ ssl:connect("twitter.com", 443, [binary, {active, false},
+ BadOption]).
+
+%%-------------------------------------------------------------------
+invalid_options() ->
+ [{doc,"Test what happens when we give invalid options"}].
+
+invalid_options(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Check = fun(Client, Server, {versions, [sslv2, sslv3]} = Option) ->
+ ssl_test_lib:check_result(Server,
+ {error, {options, {sslv2, Option}}},
+ Client,
+ {error, {options, {sslv2, Option}}});
+ (Client, Server, Option) ->
+ ssl_test_lib:check_result(Server,
+ {error, {options, Option}},
+ Client,
+ {error, {options, Option}})
+ end,
+
+ TestOpts =
+ [{versions, [sslv2, sslv3]},
+ {verify, 4},
+ {verify_fun, function},
+ {fail_if_no_peer_cert, 0},
+ {verify_client_once, 1},
+ {depth, four},
+ {certfile, 'cert.pem'},
+ {keyfile,'key.pem' },
+ {password, foo},
+ {cacertfile, ""},
+ {dhfile,'dh.pem' },
+ {ciphers, [{foo, bar, sha, ignore}]},
+ {reuse_session, foo},
+ {reuse_sessions, 0},
+ {renegotiate_at, "10"},
+ {mode, depech},
+ {packet, 8.0},
+ {packet_size, "2"},
+ {header, a},
+ {active, trice},
+ {key, 'key.pem' }],
+
+ [begin
+ Server =
+ ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {options, [TestOpt | ServerOpts]}]),
+ %% Will never reach a point where port is used.
+ Client =
+ ssl_test_lib:start_client_error([{node, ClientNode}, {port, 0},
+ {host, Hostname}, {from, self()},
+ {options, [TestOpt | ClientOpts]}]),
+ Check(Client, Server, TestOpt),
+ ok
+ end || TestOpt <- TestOpts],
+ ok.
+%%-------------------------------------------------------------------
+
+default_reject_anonymous()->
+ [{doc,"Test that by default anonymous cipher suites are rejected "}].
+default_reject_anonymous(Config) when is_list(Config) ->
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ Version = ssl_test_lib:protocol_version(Config),
+ TLSVersion = ssl_test_lib:tls_version(Version),
+
+ [CipherSuite | _] = ssl_test_lib:ecdh_dh_anonymous_suites(TLSVersion),
+
+ Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {options,
+ [{ciphers,[CipherSuite]} |
+ ClientOpts]}]),
+
+ ssl_test_lib:check_server_alert(Server, Client, insufficient_security).
+
+%%-------------------------------------------------------------------
+%% Note that these test only test that the options are valid to set. As application data
+%% is a stream you can not test that the send acctually splits it up as when it arrives
+%% again at the user layer it may be concatenated. But COVER can show that the split up
+%% code has been run.
+
+rizzo_disabled() ->
+ [{doc, "Test original beast mitigation disable option for SSL 3.0 and TLS 1.0"}].
+
+rizzo_disabled(Config) ->
+ ClientOpts = [{beast_mitigation, disabled} | ssl_test_lib:ssl_options(client_rsa_opts, Config)],
+ ServerOpts = [{beast_mitigation, disabled} | ssl_test_lib:ssl_options(server_rsa_opts, Config)],
+
+ ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config).
+%%-------------------------------------------------------------------
+rizzo_zero_n() ->
+ [{doc, "Test zero_n beast mitigation option (same affect as original disable option) for SSL 3.0 and TLS 1.0"}].
+
+rizzo_zero_n(Config) ->
+ ClientOpts = [{beast_mitigation, zero_n} | ssl_test_lib:ssl_options(client_rsa_opts, Config)],
+ ServerOpts = [{beast_mitigation, zero_n} | ssl_test_lib:ssl_options(server_rsa_opts, Config)],
+
+ ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config).
+%%-------------------------------------------------------------------
+rizzo_one_n_minus_one () ->
+ [{doc, "Test beast_mitigation option one_n_minus_one (same affect as default) for SSL 3.0 and TLS 1.0"}].
+
+rizzo_one_n_minus_one (Config) ->
+ ClientOpts = [{beast_mitigation, one_n_minus_one } | ssl_test_lib:ssl_options(client_rsa_opts, Config)],
+ ServerOpts = [{beast_mitigation, one_n_minus_one} | ssl_test_lib:ssl_options(server_rsa_opts, Config)],
+
+ ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config).
+
+supported_groups() ->
+ [{doc,"Test the supported_groups option in TLS 1.3."}].
+
+supported_groups(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, [{supported_groups, [x448, x25519]} | 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, [{supported_groups,[x448]} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+honor_client_cipher_order_tls13() ->
+ [{doc,"Test API honor server cipher order in TLS 1.3."}].
+honor_client_cipher_order_tls13(Config) when is_list(Config) ->
+ ClientCiphers = [#{key_exchange => any,
+ cipher => aes_256_gcm,
+ mac => aead,
+ prf => sha384},
+ #{key_exchange => any,
+ cipher => aes_128_gcm,
+ mac => aead,
+ prf => sha256}],
+ ServerCiphers = [#{key_exchange => any,
+ cipher => aes_128_gcm,
+ mac => aead,
+ prf => sha256},
+ #{key_exchange => any,
+ cipher => aes_256_gcm,
+ mac => aead,
+ prf => sha384}],
+ honor_cipher_order(Config, false, ServerCiphers, ClientCiphers, #{key_exchange => any,
+ cipher => aes_256_gcm,
+ mac => aead,
+ prf => sha384}).
+
+%%--------------------------------------------------------------------
+honor_server_cipher_order_tls13() ->
+ [{doc,"Test API honor server cipher order in TLS 1.3."}].
+honor_server_cipher_order_tls13(Config) when is_list(Config) ->
+ ClientCiphers = [#{key_exchange => any,
+ cipher => aes_256_gcm,
+ mac => aead,
+ prf => sha384},
+ #{key_exchange => any,
+ cipher => aes_128_gcm,
+ mac => aead,
+ prf => sha256}],
+ ServerCiphers = [#{key_exchange => any,
+ cipher => aes_128_gcm,
+ mac => aead,
+ prf => sha256},
+ #{key_exchange => any,
+ cipher => aes_256_gcm,
+ mac => aead,
+ prf => sha384}],
+ honor_cipher_order(Config, true, ServerCiphers, ClientCiphers, #{key_exchange => any,
+ cipher => aes_128_gcm,
+ mac => aead,
+ prf => sha256}).
+
+
+%%--------------------------------------------------------------------
+%% Internal functions ------------------------------------------------
+%%--------------------------------------------------------------------
+
+peercert_result(Socket) ->
+ ssl:peercert(Socket).
+
+connection_information_result(Socket) ->
+ {ok, Info = [_ | _]} = ssl:connection_information(Socket),
+ case length(Info) > 3 of
+ true ->
+ %% Atleast one ssl_option() is set
+ ct:log("Info ~p", [Info]),
+ ok;
+ false ->
+ ct:fail(no_ssl_options_returned)
+ end.
+secret_connection_info_result(Socket) ->
+ {ok, [{protocol, Protocol}]} = ssl:connection_information(Socket, [protocol]),
+ {ok, ConnInfo} = ssl:connection_information(Socket, [client_random, server_random, master_secret]),
+ check_connection_info(Protocol, ConnInfo).
+
+
+%% In TLS 1.3 the master_secret field is used to store multiple secrets from the key schedule and it is a tuple.
+%% client_random and server_random are not used in the TLS 1.3 key schedule.
+check_connection_info('tlsv1.3', [{client_random, ClientRand}, {master_secret, {master_secret, MasterSecret}}]) ->
+ is_binary(ClientRand) andalso is_binary(MasterSecret);
+check_connection_info('tlsv1.3', [{server_random, ServerRand}, {master_secret, {master_secret, MasterSecret}}]) ->
+ is_binary(ServerRand) andalso is_binary(MasterSecret);
+check_connection_info(_, [{client_random, ClientRand}, {server_random, ServerRand}, {master_secret, MasterSecret}]) ->
+ is_binary(ClientRand) andalso is_binary(ServerRand) andalso is_binary(MasterSecret);
+check_connection_info(_, _) ->
+ false.
+
+
+prf_create_plan(TlsVersions, PRFs, Results) ->
+ lists:foldl(fun(Ver, Acc) ->
+ A = prf_ciphers_and_expected(Ver, PRFs, Results),
+ [A|Acc]
+ end, [], TlsVersions).
+
+prf_ciphers_and_expected(TlsVer, PRFs, Results) ->
+ case TlsVer of
+ TlsVer when TlsVer == sslv3 orelse TlsVer == tlsv1
+ orelse TlsVer == 'tlsv1.1' orelse TlsVer == 'dtlsv1' ->
+ Ciphers = ssl:cipher_suites(),
+ {_, Expected} = lists:keyfind(md5sha, 1, Results),
+ [[{tls_ver, TlsVer}, {ciphers, Ciphers}, {expected, Expected}, {prf, md5sha}]];
+ TlsVer when TlsVer == 'tlsv1.2' orelse TlsVer == 'dtlsv1.2'->
+ lists:foldl(
+ fun(PRF, Acc) ->
+ Ciphers = prf_get_ciphers(TlsVer, PRF),
+ case Ciphers of
+ [] ->
+ ct:log("No ciphers for PRF algorithm ~p. Skipping.", [PRF]),
+ Acc;
+ Ciphers ->
+ {_, Expected} = lists:keyfind(PRF, 1, Results),
+ [[{tls_ver, TlsVer}, {ciphers, Ciphers}, {expected, Expected},
+ {prf, PRF}] | Acc]
+ end
+ end, [], PRFs)
+ end.
+
+prf_get_ciphers(_, PRF) ->
+ lists:filter(
+ fun(C) when tuple_size(C) == 4 andalso
+ element(4, C) == PRF ->
+ true;
+ (_) ->
+ false
+ end,
+ ssl:cipher_suites()).
+
+prf_run_test(_, TlsVer, [], _, Prf) ->
+ ct:fail({error, cipher_list_empty, TlsVer, Prf});
+prf_run_test(Config, TlsVer, Ciphers, Expected, Prf) ->
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ BaseOpts = [{active, true}, {versions, [TlsVer]}, {ciphers, Ciphers}, {protocol, tls_or_dtls(TlsVer)}],
+ ServerOpts = BaseOpts ++ proplists:get_value(server_opts, Config),
+ ClientOpts = BaseOpts ++ proplists:get_value(client_opts, Config),
+ Server = ssl_test_lib:start_server(
+ [{node, ServerNode}, {port, 0}, {from, self()},
+ {mfa, {?MODULE, prf_verify_value, [TlsVer, Expected, Prf]}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client(
+ [{node, ClientNode}, {port, Port},
+ {host, Hostname}, {from, self()},
+ {mfa, {?MODULE, prf_verify_value, [TlsVer, Expected, Prf]}},
+ {options, ClientOpts}]),
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+prf_verify_value(Socket, TlsVer, Expected, Algo) ->
+ Ret = ssl:prf(Socket, <<>>, <<>>, [<<>>], 16),
+ case TlsVer of
+ sslv3 ->
+ case Ret of
+ {error, undefined} -> ok;
+ _ ->
+ {error, {expected, {error, undefined},
+ got, Ret, tls_ver, TlsVer, prf_algorithm, Algo}}
+ end;
+ _ ->
+ case Ret of
+ {ok, Expected} -> ok;
+ {ok, Val} -> {error, {expected, Expected, got, Val, tls_ver, TlsVer,
+ prf_algorithm, Algo}}
+ end
+ end.
+
+tls_or_dtls('dtlsv1') ->
+ dtls;
+tls_or_dtls('dtlsv1.2') ->
+ dtls;
+tls_or_dtls(_) ->
+ tls.
+
+active_n_common(S, N) ->
+ ok = ssl:setopts(S, [{active,-N}]),
+ receive
+ {ssl_passive, S} -> ok
+ after
+ 1000 ->
+ error({error,ssl_passive_failure})
+ end,
+ [{active,false}] = ok(ssl:getopts(S, [active])),
+ ok = ssl:setopts(S, [{active,0}]),
+ receive
+ {ssl_passive, S} -> ok
+ after
+ 1000 ->
+ error({error,ssl_passive_failure})
+ end,
+ ok = ssl:setopts(S, [{active,32767}]),
+ {error,{options,_}} = ssl:setopts(S, [{active,1}]),
+ {error,{options,_}} = ssl:setopts(S, [{active,-32769}]),
+ ok = ssl:setopts(S, [{active,-32768}]),
+ receive
+ {ssl_passive, S} -> ok
+ after
+ 1000 ->
+ error({error,ssl_passive_failure})
+ end,
+ [{active,false}] = ok(ssl:getopts(S, [active])),
+ ok = ssl:setopts(S, [{active,N}]),
+ ok = ssl:setopts(S, [{active,true}]),
+ [{active,true}] = ok(ssl:getopts(S, [active])),
+ receive
+ _ -> error({error,active_n})
+ after
+ 0 ->
+ ok
+ end,
+ ok = ssl:setopts(S, [{active,N}]),
+ ok = ssl:setopts(S, [{active,once}]),
+ [{active,once}] = ok(ssl:getopts(S, [active])),
+ receive
+ _ -> error({error,active_n})
+ after
+ 0 ->
+ ok
+ end,
+ {error,{options,_}} = ssl:setopts(S, [{active,32768}]),
+ ok = ssl:setopts(S, [{active,false}]),
+ [{active,false}] = ok(ssl:getopts(S, [active])),
+ ok.
+
+ok({ok,V}) -> V.
+
+repeat(N, Fun) ->
+ repeat(N, N, Fun).
+
+repeat(N, T, Fun) when is_integer(N), N > 0 ->
+ Fun(T-N),
+ repeat(N-1, T, Fun);
+repeat(_, _, _) ->
+ ok.
+
+try_recv_active(Socket) ->
+ ssl:send(Socket, "Hello world"),
+ {error, einval} = ssl:recv(Socket, 11),
+ ok.
+try_recv_active_once(Socket) ->
+ {error, einval} = ssl:recv(Socket, 11),
+ ok.
+
+controlling_process_result(Socket, Pid, Msg) ->
+ ok = ssl:controlling_process(Socket, Pid),
+ %% Make sure other side has evaluated controlling_process
+ %% before message is sent
+ ct:sleep(?SLEEP),
+ ssl:send(Socket, Msg),
+ no_result_msg.
+
+controller_dies_result(_Socket, _Pid, _Msg) ->
+ receive Result -> Result end.
+get_close(Pid, Where) ->
+ receive
+ {'EXIT', Pid, _Reason} ->
+ receive
+ {_, {ssl_closed, Socket}} ->
+ ct:log("Socket closed ~p~n",[Socket]);
+ Unexpected ->
+ ct:log("Unexpected ~p~n",[Unexpected]),
+ ct:fail({line, ?LINE-1})
+ after 5000 ->
+ ct:fail({timeout, {line, ?LINE, Where}})
+ end;
+ Unexpected ->
+ ct:log("Unexpected ~p~n",[Unexpected]),
+ ct:fail({line, ?LINE-1})
+ after 5000 ->
+ ct:fail({timeout, {line, ?LINE, Where}})
+ end.
+
+ssl_active_recv(N) ->
+ ssl_active_recv(N, []).
+
+ssl_active_recv(0, Acc) ->
+ Acc;
+ssl_active_recv(N, Acc) ->
+ receive
+ {ssl, _, Bytes} ->
+ ssl_active_recv(N-length(Bytes), Acc ++ Bytes)
+ end.
+
+send_recv_result_timeout_client(Socket) ->
+ {error, timeout} = ssl:recv(Socket, 11, 500),
+ {error, timeout} = ssl:recv(Socket, 11, 0),
+ ssl:send(Socket, "Hello world"),
+ receive
+ Msg ->
+ io:format("Msg ~p~n",[Msg])
+ after 500 ->
+ ok
+ end,
+ {ok, "Hello world"} = ssl:recv(Socket, 11, 500),
+ ok.
+send_recv_result_timeout_server(Socket) ->
+ ssl:send(Socket, "Hello"),
+ {ok, "Hello world"} = ssl:recv(Socket, 11),
+ ssl:send(Socket, " world"),
+ ok.
+
+do_recv_close(Socket) ->
+ {error, closed} = ssl:recv(Socket, 11),
+ receive
+ {_,{error,closed}} ->
+ error_extra_close_sent_to_user_process
+ after 500 ->
+ ok
+ end.
+
+tls_close(Socket) ->
+ ok = ssl_test_lib:send_recv_result(Socket),
+ case ssl:close(Socket, 10000) of
+ ok ->
+ ok;
+ {error, closed} ->
+ ok;
+ Other ->
+ ct:fail(Other)
+ end.
+
+run_error_server_close([Pid | Opts]) ->
+ {ok, Listen} = ssl:listen(0, Opts),
+ {ok,{_, Port}} = ssl:sockname(Listen),
+ Pid ! {self(), Port},
+ {ok, Socket} = ssl:transport_accept(Listen),
+ Pid ! ssl:close(Socket).
+
+run_error_server([ Pid | Opts]) ->
+ {ok, Listen} = ssl:listen(0, Opts),
+ {ok,{_, Port}} = ssl:sockname(Listen),
+ Pid ! {self(), Port},
+ {ok, Socket} = ssl:transport_accept(Listen),
+ Pid ! ssl:controlling_process(Socket, self()).
+
+run_client_error([Port, Opts]) ->
+ ssl:connect("localhost", Port, Opts).
+
+honor_cipher_order(Config, Honor, ServerCiphers, ClientCiphers, Expected) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, connection_info_result, []}},
+ {options, [{ciphers, ServerCiphers}, {honor_cipher_order, Honor}
+ | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, connection_info_result, []}},
+ {options, [{ciphers, ClientCiphers}
+ | ClientOpts]}]),
+
+ Version = ssl_test_lib:protocol_version(Config),
+
+ ServerMsg = ClientMsg = {ok, {Version, Expected}},
+
+ ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+connection_info_result(Socket) ->
+ {ok, Info} = ssl:connection_information(Socket, [protocol, selected_cipher_suite]),
+ {ok, {proplists:get_value(protocol, Info), proplists:get_value(selected_cipher_suite, Info)}}.
+
+der_input_opts(Opts) ->
+ Certfile = proplists:get_value(certfile, Opts),
+ CaCertsfile = proplists:get_value(cacertfile, Opts),
+ Keyfile = proplists:get_value(keyfile, Opts),
+ Dhfile = proplists:get_value(dhfile, Opts),
+ [{_, Cert, _}] = ssl_test_lib:pem_to_der(Certfile),
+ [{Asn1Type, Key, _}] = ssl_test_lib:pem_to_der(Keyfile),
+ [{_, DHParams, _}] = ssl_test_lib:pem_to_der(Dhfile),
+ CaCerts =
+ lists:map(fun(Entry) ->
+ {_, CaCert, _} = Entry,
+ CaCert
+ end, ssl_test_lib:pem_to_der(CaCertsfile)),
+ {Cert, {Asn1Type, Key}, CaCerts, DHParams}.
diff --git a/lib/ssl/test/ssl_basic_SUITE_data/dHParam.pem b/lib/ssl/test/ssl_api_SUITE_data/dHParam.pem
index feb581da30..feb581da30 100644
--- a/lib/ssl/test/ssl_basic_SUITE_data/dHParam.pem
+++ b/lib/ssl/test/ssl_api_SUITE_data/dHParam.pem
diff --git a/lib/ssl/test/ssl_app_env_SUITE.erl b/lib/ssl/test/ssl_app_env_SUITE.erl
new file mode 100644
index 0000000000..27fbcb8e47
--- /dev/null
+++ b/lib/ssl/test/ssl_app_env_SUITE.erl
@@ -0,0 +1,171 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2019-2019. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(ssl_app_env_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+-include_lib("common_test/include/ct.hrl").
+-include_lib("ssl/src/ssl_api.hrl").
+
+-define(SLEEP, 500).
+%%--------------------------------------------------------------------
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
+
+all() ->
+ [
+ {group, 'tlsv1.3'},
+ {group, 'tlsv1.2'},
+ {group, 'tlsv1.1'},
+ {group, 'tlsv1'},
+ {group, 'sslv3'},
+ {group, 'dtlsv1.2'},
+ {group, 'dtlsv1'}
+ ].
+
+groups() ->
+ [
+ {'tlsv1.3', [], tests()},
+ {'tlsv1.2', [], tests()},
+ {'tlsv1.1', [], tests()},
+ {'tlsv1', [], tests()},
+ {'sslv3', [], tests()},
+ {'dtlsv1.2', [], tests()},
+ {'dtlsv1', [], tests()}
+ ].
+
+tests() ->
+ [
+ internal_active_1,
+ protocol_versions,
+ empty_protocol_versions
+ ].
+
+
+init_per_suite(Config0) ->
+ catch crypto:stop(),
+ try crypto:start() of
+ ok ->
+ ssl_test_lib:clean_start(),
+ ssl_test_lib:make_rsa_cert(Config0)
+ catch _:_ ->
+ {skip, "Crypto did not start"}
+ end.
+
+end_per_suite(_Config) ->
+ ssl:stop(),
+ application:unload(ssl),
+ application:stop(crypto).
+
+
+init_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ case ssl_test_lib:sufficient_crypto_support(GroupName) of
+ true ->
+ [{client_type, erlang},
+ {server_type, erlang} | ssl_test_lib:init_tls_version(GroupName, Config)];
+ false ->
+ {skip, "Missing crypto support"}
+ end;
+ _ ->
+ ssl:start(),
+ Config
+ end.
+
+end_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ ssl_test_lib:clean_tls_version(Config);
+ false ->
+ Config
+ end.
+
+init_per_testcase(internal_active_1, Config) ->
+ ssl:stop(),
+ application:load(ssl),
+ application:set_env(ssl, internal_active_n, 1),
+ ssl:start(),
+ ct:timetrap({seconds, 5}),
+ Config;
+init_per_testcase(protocol_versions, Config) ->
+ Version = ssl_test_lib:protocol_version(Config),
+ case atom_to_list(Version) of
+ "d" ++ _ ->
+ ssl:stop(),
+ application:load(ssl),
+ application:set_env(ssl, dtls_protocol_version, [Version]),
+ ssl:start();
+ _ ->
+ ssl:stop(),
+ application:load(ssl),
+ application:set_env(ssl, protocol_version, [Version]),
+ ssl:start()
+ end,
+ ct:timetrap({seconds, 5}),
+ Config;
+init_per_testcase(empty_protocol_versions, Config) ->
+ ssl:stop(),
+ application:load(ssl),
+ ssl_test_lib:clean_env(),
+ application:set_env(ssl, protocol_version, []),
+ application:set_env(ssl, dtls_protocol_version, []),
+ ssl:start(),
+ ct:timetrap({seconds, 5}),
+ Config;
+init_per_testcase(_TestCase, Config) ->
+ ct:timetrap({seconds, 5}),
+ Config.
+
+end_per_testcase(_, _Config) ->
+ ssl_test_lib:clean_start().
+
+%%--------------------------------------------------------------------
+%% Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
+%%--------------------------------------------------------------------
+internal_active_1() ->
+ [{doc,"Test internal active 1 (behave as internal active once)"}].
+
+internal_active_1(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config).
+
+%%--------------------------------------------------------------------
+protocol_versions() ->
+ [{doc,"Test to set a list of protocol versions in app environment."}].
+
+protocol_versions(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config).
+
+%%--------------------------------------------------------------------
+empty_protocol_versions() ->
+ [{doc,"Test to set an empty list of protocol versions in app environment."}].
+
+empty_protocol_versions(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config).
+
diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl
index 86a0aaf67b..355cd31070 100644
--- a/lib/ssl/test/ssl_basic_SUITE.erl
+++ b/lib/ssl/test/ssl_basic_SUITE.erl
@@ -26,15 +26,8 @@
-compile(export_all).
-include_lib("common_test/include/ct.hrl").
--include_lib("public_key/include/public_key.hrl").
+-include_lib("ssl/src/ssl_api.hrl").
--include("ssl_api.hrl").
--include("ssl_cipher.hrl").
--include("ssl_internal.hrl").
--include("ssl_alert.hrl").
--include("ssl_internal.hrl").
--include("tls_record.hrl").
--include("tls_handshake.hrl").
-define(TIMEOUT, 20000).
-define(EXPIRE, 10).
@@ -49,243 +42,35 @@
all() ->
[
{group, basic},
- {group, basic_tls},
- {group, options},
- {group, options_tls},
- {group, session},
- {group, 'dtlsv1.2'},
- {group, 'dtlsv1'},
- {group, 'tlsv1.3'},
- {group, 'tlsv1.2'},
- {group, 'tlsv1.1'},
- {group, 'tlsv1'},
- {group, 'sslv3'}
+ {group, options}
].
groups() ->
[{basic, [], basic_tests()},
- {basic_tls, [], basic_tests_tls()},
- {options, [], options_tests()},
- {options_tls, [], options_tests_tls()},
- {'dtlsv1.2', [], all_versions_groups()},
- {'dtlsv1', [], all_versions_groups()},
- {'tlsv1.3', [], tls13_test_group()},
- {'tlsv1.2', [], all_versions_groups() ++ tls_versions_groups() ++ [conf_signature_algs, no_common_signature_algs]},
- {'tlsv1.1', [], all_versions_groups() ++ tls_versions_groups()},
- {'tlsv1', [], all_versions_groups() ++ tls_versions_groups() ++ rizzo_tests()},
- {'sslv3', [], all_versions_groups() ++ tls_versions_groups() ++ rizzo_tests() ++ [tls_ciphersuite_vs_version]},
- {api,[], api_tests()},
- {api_tls,[], api_tests_tls()},
- {session, [], session_tests()},
- {renegotiate, [], renegotiate_tests()},
- {ciphers, [], cipher_tests()},
- {error_handling_tests, [], error_handling_tests()},
- {error_handling_tests_tls, [], error_handling_tests_tls()}
+ {options, [], options_tests()}
].
-tls_versions_groups ()->
- [
- {group, api_tls},
- {group, error_handling_tests_tls}].
-
-all_versions_groups ()->
- [{group, api},
- {group, renegotiate},
- {group, ciphers},
- {group, error_handling_tests}].
-
-
basic_tests() ->
[app,
- appup,
- alerts,
- alert_details,
- alert_details_not_too_big,
+ appup,
version_option,
connect_twice,
connect_dist,
- clear_pem_cache,
defaults,
fallback,
cipher_format,
- suite_to_str
- ].
-
-basic_tests_tls() ->
- [tls_send_close
- ].
-
-options_tests() ->
- [der_input,
- ssl_options_not_proplist,
- raw_ssl_option,
- invalid_inet_get_option,
- invalid_inet_get_option_not_list,
- invalid_inet_get_option_improper_list,
- invalid_inet_set_option,
- invalid_inet_set_option_not_list,
- invalid_inet_set_option_improper_list,
- dh_params,
- invalid_certfile,
- invalid_cacertfile,
- invalid_keyfile,
- invalid_options,
- protocol_versions,
- empty_protocol_versions,
- ipv6,
- reuseaddr,
- honor_server_cipher_order,
- honor_client_cipher_order,
- unordered_protocol_versions_server,
- unordered_protocol_versions_client,
- max_handshake_size
-].
-
-options_tests_tls() ->
- [tls_misc_ssl_options,
- tls_tcp_reuseaddr].
-
-api_tests() ->
- [secret_connection_info,
- connection_information,
- peercert,
- peercert_with_client_cert,
- versions,
+ tls_versions_option,
eccs,
- controlling_process,
- getstat,
- close_with_timeout,
- hibernate,
- hibernate_right_away,
- listen_socket,
- ssl_recv_timeout,
- server_name_indication_option,
- accept_pool,
- prf,
- socket_options,
- active_n,
- internal_active_1,
cipher_suites,
- handshake_continue,
- handshake_continue_timeout,
- hello_client_cancel,
- hello_server_cancel
- ].
-
-api_tests_tls() ->
- [tls_versions_option,
- tls_upgrade,
- tls_upgrade_with_timeout,
- tls_ssl_accept_timeout,
- tls_downgrade,
- tls_shutdown,
- tls_shutdown_write,
- tls_shutdown_both,
- tls_shutdown_error,
- peername,
- sockname,
- tls_socket_options,
- new_options_in_accept
- ].
-
-session_tests() ->
- [reuse_session,
- reuse_session_expired,
- server_does_not_want_to_reuse_session,
- no_reuses_session_server_restart_new_cert,
- no_reuses_session_server_restart_new_cert_file].
-
-renegotiate_tests() ->
- [client_renegotiate,
- server_renegotiate,
- client_secure_renegotiate,
- client_secure_renegotiate_fallback,
- client_renegotiate_reused_session,
- server_renegotiate_reused_session,
- client_no_wrap_sequence_number,
- server_no_wrap_sequence_number,
- renegotiate_dos_mitigate_active,
- renegotiate_dos_mitigate_passive,
- renegotiate_dos_mitigate_absolute].
-
-cipher_tests() ->
- [old_cipher_suites,
- cipher_suites_mix,
- default_reject_anonymous].
-
-error_handling_tests()->
- [close_transport_accept,
- recv_active,
- recv_active_once,
- recv_active_n,
- recv_error_handling,
- call_in_error_state,
- close_in_error_state,
- abuse_transport_accept_socket,
- controlling_process_transport_accept_socket
- ].
-
-error_handling_tests_tls()->
- [controller_dies,
- tls_client_closes_socket,
- tls_closed_in_active_once,
- tls_tcp_error_propagation_in_active_mode,
- tls_tcp_connect,
- tls_tcp_connect_big,
- tls_dont_crash_on_handshake_garbage
+ old_cipher_suites,
+ cipher_suites_mix
].
-rizzo_tests() ->
- [rizzo,
- no_rizzo_rc4,
- rizzo_one_n_minus_one,
- rizzo_zero_n,
- rizzo_disabled].
-
-%% For testing TLS 1.3 features and possible regressions
-tls13_test_group() ->
- [handshake_continue_tls13_client,
- tls13_enable_client_side,
- tls13_enable_server_side,
- tls_record_1_3_encode_decode,
- tls13_finished_verify_data,
- tls13_1_RTT_handshake,
- tls12_ssl_server_tls13_ssl_client,
- tls13_basic_ssl_server_openssl_client,
- tls13_basic_ssl_server_ssl_client,
- tls13_basic_openssl_server_ssl_client,
- tls13_custom_groups_ssl_server_openssl_client,
- tls13_custom_groups_ssl_server_ssl_client,
- tls13_hello_retry_request_ssl_server_openssl_client,
- tls13_hello_retry_request_ssl_server_ssl_client,
- tls13_client_auth_empty_cert_alert_ssl_server_openssl_client,
- tls13_client_auth_empty_cert_alert_ssl_server_ssl_client,
- tls13_client_auth_empty_cert_ssl_server_openssl_client,
- tls13_client_auth_empty_cert_ssl_server_ssl_client,
- tls13_client_auth_ssl_server_openssl_client,
- tls13_client_auth_ssl_server_ssl_client,
- tls13_hrr_client_auth_empty_cert_alert_ssl_server_openssl_client,
- tls13_hrr_client_auth_empty_cert_alert_ssl_server_ssl_client,
- tls13_hrr_client_auth_empty_cert_ssl_server_openssl_client,
- tls13_hrr_client_auth_empty_cert_ssl_server_ssl_client,
- tls13_hrr_client_auth_ssl_server_openssl_client,
- tls13_hrr_client_auth_ssl_server_ssl_client,
- tls13_unsupported_sign_algo_client_auth_ssl_server_openssl_client,
- tls13_unsupported_sign_algo_client_auth_ssl_server_ssl_client,
- tls13_unsupported_sign_algo_cert_client_auth_ssl_server_openssl_client,
- tls13_unsupported_sign_algo_cert_client_auth_ssl_server_ssl_client,
- tls13_connection_information,
- tls13_ssl_server_with_alpn_ssl_client,
- tls13_ssl_server_with_alpn_ssl_client_empty_alpn,
- tls13_ssl_server_with_alpn_ssl_client_bad_alpn,
- tls13_ssl_server_with_alpn_ssl_client_alpn,
- tls13_ecdsa_ssl_server_openssl_client,
- tls13_ecdsa_ssl_server_ssl_client,
- tls13_ecdsa_openssl_server_ssl_client,
- tls13_ecdsa_client_auth_ssl_server_ssl_client
- ].
+options_tests() ->
+ [
+ unordered_protocol_versions_server,
+ unordered_protocol_versions_client].
-%%--------------------------------------------------------------------
init_per_suite(Config0) ->
catch crypto:stop(),
try crypto:start() of
@@ -308,229 +93,6 @@ end_per_suite(_Config) ->
application:stop(crypto).
%%--------------------------------------------------------------------
-
-init_per_group(GroupName, Config) when GroupName == basic_tls;
- GroupName == options_tls;
- GroupName == options;
- GroupName == basic;
- GroupName == session;
- GroupName == error_handling_tests_tls ->
- ssl_test_lib:clean_tls_version(Config);
-%% Do not automatically configure TLS version for the 'tlsv1.3' group
-init_per_group('tlsv1.3' = GroupName, Config) ->
- case ssl_test_lib:sufficient_crypto_support(GroupName) of
- true ->
- ssl:start(),
- Config;
- false ->
- {skip, "Missing crypto support"}
- end;
-init_per_group(GroupName, Config) ->
- ssl_test_lib:clean_tls_version(Config),
- case ssl_test_lib:is_tls_version(GroupName) andalso ssl_test_lib:sufficient_crypto_support(GroupName) of
- true ->
- ssl_test_lib:init_tls_version(GroupName, Config);
- _ ->
- case ssl_test_lib:sufficient_crypto_support(GroupName) of
- true ->
- ssl:start(),
- Config;
- false ->
- {skip, "Missing crypto support"}
- end
- end.
-
-end_per_group(GroupName, Config) ->
- case ssl_test_lib:is_tls_version(GroupName) of
- true ->
- ssl_test_lib:clean_tls_version(Config);
- false ->
- Config
- end.
-
-%%--------------------------------------------------------------------
-init_per_testcase(Case, Config) when Case == unordered_protocol_versions_client;
- Case == unordered_protocol_versions_server->
- case proplists:get_value(supported, ssl:versions()) of
- ['tlsv1.2' | _] ->
- ct:timetrap({seconds, 5}),
- Config;
- _ ->
- {skip, "TLS 1.2 need but not supported on this platform"}
- end;
-
-init_per_testcase(protocol_versions, Config) ->
- ssl:stop(),
- application:load(ssl),
- %% For backwards compatibility sslv2 should be filtered out.
- application:set_env(ssl, protocol_version, [sslv2, sslv3, tlsv1]),
- ssl:start(),
- ct:timetrap({seconds, 5}),
- Config;
-
-init_per_testcase(reuse_session_expired, Config) ->
- ssl:stop(),
- application:load(ssl),
- ssl_test_lib:clean_env(),
- application:set_env(ssl, session_lifetime, ?EXPIRE),
- application:set_env(ssl, session_delay_cleanup_time, 500),
- ssl:start(),
- ct:timetrap({seconds, 30}),
- Config;
-
-init_per_testcase(empty_protocol_versions, Config) ->
- ssl:stop(),
- application:load(ssl),
- ssl_test_lib:clean_env(),
- application:set_env(ssl, protocol_version, []),
- ssl:start(),
- ct:timetrap({seconds, 5}),
- Config;
-
-init_per_testcase(fallback, Config) ->
- case tls_record:highest_protocol_version([]) of
- {3, N} when N > 1 ->
- ct:timetrap({seconds, 5}),
- Config;
- _ ->
- {skip, "Not relevant if highest supported version is less than 3.2"}
- end;
-
-init_per_testcase(TestCase, Config) when TestCase == client_renegotiate;
- TestCase == server_renegotiate;
- TestCase == client_secure_renegotiate;
- TestCase == client_renegotiate_reused_session;
- TestCase == server_renegotiate_reused_session;
- TestCase == client_no_wrap_sequence_number;
- TestCase == server_no_wrap_sequence_number;
- TestCase == renegotiate_dos_mitigate_active;
- TestCase == renegotiate_dos_mitigate_passive;
- TestCase == renegotiate_dos_mitigate_absolute ->
- ssl_test_lib:ct_log_supported_protocol_versions(Config),
- ct:timetrap({seconds, ?SEC_RENEGOTIATION_TIMEOUT + 5}),
- Config;
-
-init_per_testcase(TestCase, Config) when TestCase == versions_option;
- TestCase == tls_tcp_connect_big ->
- ssl_test_lib:ct_log_supported_protocol_versions(Config),
- ct:timetrap({seconds, 60}),
- Config;
-
-init_per_testcase(version_option, Config) ->
- ssl_test_lib:ct_log_supported_protocol_versions(Config),
- ct:timetrap({seconds, 10}),
- Config;
-
-init_per_testcase(reuse_session, Config) ->
- ssl_test_lib:ct_log_supported_protocol_versions(Config),
- ct:timetrap({seconds, 10}),
- Config;
-
-init_per_testcase(rizzo, Config) ->
- ssl_test_lib:ct_log_supported_protocol_versions(Config),
- ct:timetrap({seconds, 60}),
- Config;
-
-init_per_testcase(no_rizzo_rc4, Config) ->
- ssl_test_lib:ct_log_supported_protocol_versions(Config),
- ct:timetrap({seconds, 60}),
- Config;
-
-init_per_testcase(rizzo_one_n_minus_one, Config) ->
- ct:log("TLS/SSL version ~p~n ", [tls_record:supported_protocol_versions()]),
- ct:timetrap({seconds, 60}),
- rizzo_add_mitigation_option(one_n_minus_one, Config);
-
-init_per_testcase(rizzo_zero_n, Config) ->
- ct:log("TLS/SSL version ~p~n ", [tls_record:supported_protocol_versions()]),
- ct:timetrap({seconds, 60}),
- rizzo_add_mitigation_option(zero_n, Config);
-
-init_per_testcase(rizzo_disabled, Config) ->
- ct:log("TLS/SSL version ~p~n ", [tls_record:supported_protocol_versions()]),
- ct:timetrap({seconds, 60}),
- rizzo_add_mitigation_option(disabled, Config);
-
-init_per_testcase(TestCase, Config) when TestCase == no_reuses_session_server_restart_new_cert_file;
- TestCase == no_reuses_session_server_restart_new_cert ->
- ct:log("TLS/SSL version ~p~n ", [tls_record:supported_protocol_versions()]),
- ct:timetrap({seconds, 15}),
- Config;
-
-init_per_testcase(prf, Config) ->
- ct:log("TLS/SSL version ~p~n ", [tls_record:supported_protocol_versions()]),
- ct:timetrap({seconds, 40}),
- case proplists:get_value(tc_group_path, Config) of
- [] -> Prop = [];
- [Prop] -> Prop
- end,
- case proplists:get_value(name, Prop) of
- undefined -> TlsVersions = [sslv3, tlsv1, 'tlsv1.1', 'tlsv1.2'];
- TlsVersion when is_atom(TlsVersion) ->
- TlsVersions = [TlsVersion]
- end,
- PRFS=[md5, sha, sha256, sha384, sha512],
- %All are the result of running tls_v1:prf(PrfAlgo, <<>>, <<>>, <<>>, 16)
- %with the specified PRF algorithm
- ExpectedPrfResults=
- [{md5, <<96,139,180,171,236,210,13,10,28,32,2,23,88,224,235,199>>},
- {sha, <<95,3,183,114,33,169,197,187,231,243,19,242,220,228,70,151>>},
- {sha256, <<166,249,145,171,43,95,158,232,6,60,17,90,183,180,0,155>>},
- {sha384, <<153,182,217,96,186,130,105,85,65,103,123,247,146,91,47,106>>},
- {sha512, <<145,8,98,38,243,96,42,94,163,33,53,49,241,4,127,28>>},
- %TLS 1.0 and 1.1 PRF:
- {md5sha, <<63,136,3,217,205,123,200,177,251,211,17,229,132,4,173,80>>}],
- TestPlan = prf_create_plan(TlsVersions, PRFS, ExpectedPrfResults),
- [{prf_test_plan, TestPlan} | Config];
-
-init_per_testcase(TestCase, Config) when TestCase == tls_ssl_accept_timeout;
- TestCase == tls_client_closes_socket;
- TestCase == tls_closed_in_active_once;
- TestCase == tls_downgrade ->
- ssl:stop(),
- ssl:start(),
- ssl_test_lib:ct_log_supported_protocol_versions(Config),
- ct:timetrap({seconds, 15}),
- Config;
-init_per_testcase(TestCase, Config) when TestCase == clear_pem_cache;
- TestCase == der_input;
- TestCase == defaults ->
- ssl_test_lib:ct_log_supported_protocol_versions(Config),
- %% White box test need clean start
- ssl:stop(),
- ssl:start(),
- ct:timetrap({seconds, 20}),
- Config;
-init_per_testcase(raw_ssl_option, Config) ->
- ct:timetrap({seconds, 5}),
- case os:type() of
- {unix,linux} ->
- Config;
- _ ->
- {skip, "Raw options are platform-specific"}
- end;
-
-init_per_testcase(accept_pool, Config) ->
- ct:timetrap({seconds, 5}),
- case proplists:get_value(protocol, Config) of
- dtls ->
- {skip, "Not yet supported on DTLS sockets"};
- _ ->
- ssl_test_lib:ct_log_supported_protocol_versions(Config),
- Config
- end;
-
-init_per_testcase(internal_active_1, Config) ->
- ssl:stop(),
- application:load(ssl),
- application:set_env(ssl, internal_active_n, 1),
- ssl:start(),
- ct:timetrap({seconds, 5}),
- Config;
-
-init_per_testcase(controller_dies, Config) ->
- ct:timetrap({seconds, 10}),
- Config;
init_per_testcase(eccs, Config) ->
case ssl:eccs() of
[] ->
@@ -545,23 +107,8 @@ init_per_testcase(_TestCase, Config) ->
ct:timetrap({seconds, 5}),
Config.
-end_per_testcase(reuse_session_expired, Config) ->
- application:unset_env(ssl, session_lifetime),
- application:unset_env(ssl, session_delay_cleanup_time),
- end_per_testcase(default_action, Config);
-
-end_per_testcase(internal_active_n, Config) ->
- application:unset_env(ssl, internal_active_n),
- end_per_testcase(default_action, Config);
-
-end_per_testcase(Case, Config) when Case == protocol_versions;
- Case == empty_protocol_versions->
- application:unset_env(ssl, protocol_versions),
- end_per_testcase(default_action, Config);
-
end_per_testcase(_TestCase, Config) ->
Config.
-
%%--------------------------------------------------------------------
%% Test Cases --------------------------------------------------------
%%--------------------------------------------------------------------
@@ -575,669 +122,76 @@ appup() ->
appup(Config) when is_list(Config) ->
ok = ?t:appup_test(ssl).
%%--------------------------------------------------------------------
-alerts() ->
- [{doc, "Test ssl_alert:alert_txt/1"}].
-alerts(Config) when is_list(Config) ->
- Descriptions = [?CLOSE_NOTIFY, ?UNEXPECTED_MESSAGE, ?BAD_RECORD_MAC,
- ?DECRYPTION_FAILED_RESERVED, ?RECORD_OVERFLOW, ?DECOMPRESSION_FAILURE,
- ?HANDSHAKE_FAILURE, ?BAD_CERTIFICATE, ?UNSUPPORTED_CERTIFICATE,
- ?CERTIFICATE_REVOKED,?CERTIFICATE_EXPIRED, ?CERTIFICATE_UNKNOWN,
- ?ILLEGAL_PARAMETER, ?UNKNOWN_CA, ?ACCESS_DENIED, ?DECODE_ERROR,
- ?DECRYPT_ERROR, ?EXPORT_RESTRICTION, ?PROTOCOL_VERSION,
- ?INSUFFICIENT_SECURITY, ?INTERNAL_ERROR, ?USER_CANCELED,
- ?NO_RENEGOTIATION, ?UNSUPPORTED_EXTENSION, ?CERTIFICATE_UNOBTAINABLE,
- ?UNRECOGNISED_NAME, ?BAD_CERTIFICATE_STATUS_RESPONSE,
- ?BAD_CERTIFICATE_HASH_VALUE, ?UNKNOWN_PSK_IDENTITY,
- 255 %% Unsupported/unknow alert will result in a description too
- ],
- Alerts = [?ALERT_REC(?WARNING, ?CLOSE_NOTIFY) |
- [?ALERT_REC(?FATAL, Desc) || Desc <- Descriptions]],
- lists:foreach(fun(Alert) ->
- try ssl_alert:alert_txt(Alert)
- catch
- C:E:T ->
- ct:fail({unexpected, {C, E, T}})
- end
- end, Alerts).
-%%--------------------------------------------------------------------
-alert_details() ->
- [{doc, "Test that ssl_alert:alert_txt/1 result contains extendend error description"}].
-alert_details(Config) when is_list(Config) ->
- Unique = make_ref(),
- UniqueStr = lists:flatten(io_lib:format("~w", [Unique])),
- Alert = ?ALERT_REC(?WARNING, ?CLOSE_NOTIFY, Unique),
- case string:str(ssl_alert:alert_txt(Alert), UniqueStr) of
- 0 ->
- ct:fail(error_details_missing);
- _ ->
- ok
- end.
-
-%%--------------------------------------------------------------------
-alert_details_not_too_big() ->
- [{doc, "Test that ssl_alert:alert_txt/1 limits printed depth of extended error description"}].
-alert_details_not_too_big(Config) when is_list(Config) ->
- Reason = lists:duplicate(10, lists:duplicate(10, lists:duplicate(10, {some, data}))),
- Alert = ?ALERT_REC(?WARNING, ?CLOSE_NOTIFY, Reason),
- case length(ssl_alert:alert_txt(Alert)) < 1000 of
- true ->
- ok;
- false ->
- ct:fail(ssl_alert_text_too_big)
- end.
-
-%%--------------------------------------------------------------------
-new_options_in_accept() ->
- [{doc,"Test that you can set ssl options in ssl_accept/3 and not only in tcp upgrade"}].
-new_options_in_accept(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts0 = ssl_test_lib:ssl_options(server_dsa_opts, Config),
- [_ , _ | ServerSslOpts] = ssl_test_lib:ssl_options(server_opts, Config), %% Remove non ssl opts
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Version = ssl_test_lib:protocol_options(Config, [{tls, sslv3}, {dtls, dtlsv1}]),
- Cipher = ssl_test_lib:protocol_options(Config, [{tls, #{key_exchange =>rsa,
- cipher => rc4_128,
- mac => sha,
- prf => default_prf
- }},
- {dtls, #{key_exchange =>rsa,
- cipher => aes_128_cbc,
- mac => sha,
- prf => default_prf
- }}]),
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {ssl_extra_opts, [{versions, [Version]},
- {ciphers,[Cipher]} | ServerSslOpts]}, %% To be set in ssl_accept/3
- {mfa, {?MODULE, connection_info_result, []}},
- {options, proplists:delete(cacertfile, ServerOpts0)}]),
-
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, connection_info_result, []}},
- {options, [{versions, [Version]},
- {ciphers,[Cipher]} | ClientOpts]}]),
-
- ct:log("Testcase ~p, Client ~p Server ~p ~n",
- [self(), Client, Server]),
-
- ServerMsg = ClientMsg = {ok, {Version, Cipher}},
+version_option() ->
+ [{doc, "Use version option and do no specify ciphers list. Bug specified incorrect ciphers"}].
+version_option(Config) when is_list(Config) ->
+ Versions = proplists:get_value(supported, ssl:versions()),
+ [version_option_test(Config, Version) || Version <- Versions].
- ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-%%--------------------------------------------------------------------
-handshake_continue() ->
- [{doc, "Test API function ssl:handshake_continue/3"}].
-handshake_continue(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, ssl_test_lib:ssl_options([{reuseaddr, true}, {handshake, hello}],
- Config)},
- {continue_options, proplists:delete(reuseaddr, 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, ssl_test_lib:ssl_options([{handshake, hello}],
- Config)},
- {continue_options, proplists:delete(reuseaddr, ClientOpts)}]),
-
- ssl_test_lib:check_result(Server, ok, Client, ok),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-
-%%--------------------------------------------------------------------
-handshake_continue_tls13_client() ->
- [{doc, "Test API function ssl:handshake_continue/3"}].
-handshake_continue_tls13_client(Config) when is_list(Config) ->
- ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
- ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}|ClientOpts0],
-
- ClientOptsHello0 = ssl_test_lib:ssl_options([{handshake, hello}], Config),
- ClientOptsHello = [{versions, ['tlsv1.2','tlsv1.3']}|ClientOptsHello0],
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, ssl_test_lib:ssl_options([{reuseaddr, true}, {handshake, hello}],
- Config)},
- {continue_options, proplists:delete(reuseaddr, 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, ClientOptsHello},
- {continue_options, proplists:delete(reuseaddr, ClientOpts)}]),
-
- ssl_test_lib:check_result(Server, ok, Client, ok),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-
-%%------------------------------------------------------------------
-handshake_continue_timeout() ->
- [{doc, "Test API function ssl:handshake_continue/3 with short timeout"}].
-handshake_continue_timeout(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(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()},
- {timeout, 1},
- {options, ssl_test_lib:ssl_options([{reuseaddr, true}, {handshake, hello}],
- Config)},
- {continue_options, proplists:delete(reuseaddr, ServerOpts)}
- ]),
-
- Port = ssl_test_lib:inet_port(Server),
-
-
- {connect_failed, _} = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {options, ClientOpts}]),
-
- ssl_test_lib:check_result(Server, {error,timeout}),
- ssl_test_lib:close(Server).
-
-
-%%--------------------------------------------------------------------
-hello_client_cancel() ->
- [{doc, "Test API function ssl:handshake_cancel/1 on the client side"}].
-hello_client_cancel(Config) when is_list(Config) ->
- ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(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()},
- {options, ssl_test_lib:ssl_options([{handshake, hello}], Config)},
- {continue_options, proplists:delete(reuseaddr, ServerOpts)}]),
-
- Port = ssl_test_lib:inet_port(Server),
-
- %% That is ssl:handshake_cancel returns ok
- {connect_failed, ok} = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {options, ssl_test_lib:ssl_options([{handshake, hello}], Config)},
- {continue_options, cancel}]),
- ssl_test_lib:check_server_alert(Server, user_canceled).
%%--------------------------------------------------------------------
-hello_server_cancel() ->
- [{doc, "Test API function ssl:handshake_cancel/1 on the server side"}].
-hello_server_cancel(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {options, ssl_test_lib:ssl_options([{handshake, hello}], Config)},
- {continue_options, cancel}]),
-
- Port = ssl_test_lib:inet_port(Server),
-
- {connect_failed, _} = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {options, ssl_test_lib:ssl_options([{handshake, hello}], Config)},
- {continue_options, proplists:delete(reuseaddr, ClientOpts)}]),
-
- ssl_test_lib:check_result(Server, ok).
-
-%%--------------------------------------------------------------------
-prf() ->
- [{doc,"Test that ssl:prf/5 uses the negotiated PRF."}].
-prf(Config) when is_list(Config) ->
- TestPlan = proplists:get_value(prf_test_plan, Config),
- case TestPlan of
- [] -> ct:fail({error, empty_prf_test_plan});
- _ -> lists:foreach(fun(Suite) ->
- lists:foreach(
- fun(Test) ->
- V = proplists:get_value(tls_ver, Test),
- C = proplists:get_value(ciphers, Test),
- E = proplists:get_value(expected, Test),
- P = proplists:get_value(prf, Test),
- prf_run_test(Config, V, C, E, P)
- end, Suite)
- end, TestPlan)
- end.
-
-%%--------------------------------------------------------------------
-
-secret_connection_info() ->
- [{doc,"Test the API function ssl:connection_information/2"}].
-secret_connection_info(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, secret_connection_info_result, []}},
- {options, ServerOpts}]),
-
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, secret_connection_info_result, []}},
- {options, ClientOpts}]),
-
- ct:log("Testcase ~p, Client ~p Server ~p ~n",
- [self(), Client, Server]),
-
- ssl_test_lib:check_result(Server, true, Client, true),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-
-%%--------------------------------------------------------------------
-
-connection_information() ->
- [{doc,"Test the API function ssl:connection_information/1"}].
-connection_information(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, connection_information_result, []}},
- {options, ServerOpts}]),
-
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, connection_information_result, []}},
- {options, ClientOpts}]),
-
- ct:log("Testcase ~p, Client ~p Server ~p ~n",
- [self(), Client, Server]),
-
- ServerMsg = ClientMsg = ok,
-
- ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-
-%%--------------------------------------------------------------------
-protocol_versions() ->
- [{doc,"Test to set a list of protocol versions in app environment."}].
-
-protocol_versions(Config) when is_list(Config) ->
- basic_test(Config).
-
-%%--------------------------------------------------------------------
-empty_protocol_versions() ->
- [{doc,"Test to set an empty list of protocol versions in app environment."}].
-
-empty_protocol_versions(Config) when is_list(Config) ->
- basic_test(Config).
-
-%%--------------------------------------------------------------------
-
-controlling_process() ->
- [{doc,"Test API function controlling_process/2"}].
-
-controlling_process(Config) when is_list(Config) ->
+connect_twice() ->
+ [{doc,""}].
+connect_twice(Config) when is_list(Config) ->
ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- ClientMsg = "Server hello",
- ServerMsg = "Client hello",
-
- Server = ssl_test_lib:start_server([
- {node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE,
- controlling_process_result, [self(),
- ServerMsg]}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
- {Client, CSocket} = ssl_test_lib:start_client([return_socket,
- {node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE,
- controlling_process_result, [self(),
- ClientMsg]}},
- {options, ClientOpts}]),
-
- ct:log("Testcase ~p, Client ~p Server ~p ~n",
- [self(), Client, Server]),
-
- ServerMsg = ssl_test_lib:active_recv(CSocket, length(ServerMsg)),
- %% We do not have the TLS server socket but all messages form the client
- %% socket are now read, so ramining are form the server socket
- ClientMsg = ssl_active_recv(length(ClientMsg)),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-%%--------------------------------------------------------------------
-getstat() ->
- [{doc,"Test API function getstat/2"}].
-getstat(Config) when is_list(Config) ->
- ClientOpts = ?config(client_opts, Config),
- ServerOpts = ?config(server_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server1 =
- ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result, []}},
- {options, [{active, false} | ServerOpts]}]),
- Port1 = ssl_test_lib:inet_port(Server1),
- Server2 =
- ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result, []}},
- {options, [{active, false} | ServerOpts]}]),
- Port2 = ssl_test_lib:inet_port(Server2),
- {ok, ActiveC} = rpc:call(ClientNode, ssl, connect,
- [Hostname,Port1,[{active, once}|ClientOpts]]),
- {ok, PassiveC} = rpc:call(ClientNode, ssl, connect,
- [Hostname,Port2,[{active, false}|ClientOpts]]),
-
- ct:log("Testcase ~p, Client ~p Servers ~p, ~p ~n",
- [self(), self(), Server1, Server2]),
-
- %% We only check that the values are non-zero initially
- %% (due to the handshake), and that sending more changes the values.
-
- %% Passive socket.
-
- {ok, InitialStats} = ssl:getstat(PassiveC),
- ct:pal("InitialStats ~p~n", [InitialStats]),
- [true] = lists:usort([0 =/= proplists:get_value(Name, InitialStats)
- || Name <- [recv_cnt, recv_oct, recv_avg, recv_max, send_cnt, send_oct, send_avg, send_max]]),
-
- ok = ssl:send(PassiveC, "Hello world"),
- wait_for_send(PassiveC),
- {ok, SStats} = ssl:getstat(PassiveC, [send_cnt, send_oct]),
- ct:pal("SStats ~p~n", [SStats]),
- [true] = lists:usort([proplists:get_value(Name, SStats) =/= proplists:get_value(Name, InitialStats)
- || Name <- [send_cnt, send_oct]]),
-
- %% Active socket.
-
- {ok, InitialAStats} = ssl:getstat(ActiveC),
- ct:pal("InitialAStats ~p~n", [InitialAStats]),
- [true] = lists:usort([0 =/= proplists:get_value(Name, InitialAStats)
- || Name <- [recv_cnt, recv_oct, recv_avg, recv_max, send_cnt, send_oct, send_avg, send_max]]),
- _ = receive
- {ssl, ActiveC, _} ->
- ok
- after
- ?SLEEP ->
- exit(timeout)
- end,
-
- ok = ssl:send(ActiveC, "Hello world"),
- wait_for_send(ActiveC),
- {ok, ASStats} = ssl:getstat(ActiveC, [send_cnt, send_oct]),
- ct:pal("ASStats ~p~n", [ASStats]),
- [true] = lists:usort([proplists:get_value(Name, ASStats) =/= proplists:get_value(Name, InitialAStats)
- || Name <- [send_cnt, send_oct]]),
-
- ok.
-
-%%--------------------------------------------------------------------
-controller_dies() ->
- [{doc,"Test that the socket is closed after controlling process dies"}].
-controller_dies(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- ClientMsg = "Hello server",
- ServerMsg = "Hello client",
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE,
- controller_dies_result, [self(),
- ServerMsg]}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE,
- controller_dies_result, [self(),
- ClientMsg]}},
- {options, ClientOpts}]),
-
- ct:log("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]),
- ct:sleep(?SLEEP), %% so that they are connected
-
- process_flag(trap_exit, true),
-
- %% Test that clients die
- exit(Client, killed),
- get_close(Client, ?LINE),
-
- %% Test that clients die when process disappear
- Server ! listen,
- Tester = self(),
- Connect = fun(Pid) ->
- {ok, Socket} = ssl:connect(Hostname, Port, ClientOpts),
- %% Make sure server finishes and verification
- %% and is in coonection state before
- %% killing client
- ct:sleep(?SLEEP),
- Pid ! {self(), connected, Socket},
- receive die_nice -> normal end
- end,
- Client2 = spawn_link(fun() -> Connect(Tester) end),
- receive {Client2, connected, _Socket} -> Client2 ! die_nice end,
-
- get_close(Client2, ?LINE),
-
- %% Test that clients die when the controlling process have changed
- Server ! listen,
-
- Client3 = spawn_link(fun() -> Connect(Tester) end),
- Controller = spawn_link(fun() -> receive die_nice -> normal end end),
- receive
- {Client3, connected, Socket} ->
- ok = ssl:controlling_process(Socket, Controller),
- Client3 ! die_nice
- end,
-
- ct:log("Wating on exit ~p~n",[Client3]),
- receive {'EXIT', Client3, normal} -> ok end,
-
- receive %% Client3 is dead but that doesn't matter, socket should not be closed.
- Unexpected ->
- ct:log("Unexpected ~p~n",[Unexpected]),
- ct:fail({line, ?LINE-1})
- after 1000 ->
- ok
- end,
- Controller ! die_nice,
- get_close(Controller, ?LINE),
-
- %% Test that servers die
- Server ! listen,
- LastClient = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE,
- controller_dies_result, [self(),
- ClientMsg]}},
- {options, ClientOpts}]),
- ct:sleep(?SLEEP), %% so that they are connected
-
- exit(Server, killed),
- get_close(Server, ?LINE),
- process_flag(trap_exit, false),
- ssl_test_lib:close(LastClient).
-
-%%--------------------------------------------------------------------
-tls_client_closes_socket() ->
- [{doc,"Test what happens when client closes socket before handshake is compleated"}].
-
-tls_client_closes_socket(Config) when is_list(Config) ->
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- TcpOpts = [binary, {reuseaddr, true}],
-
- Server = ssl_test_lib:start_upgrade_server_error([{node, ServerNode}, {port, 0},
- {from, self()},
- {tcp_options, TcpOpts},
- {ssl_options, ServerOpts}]),
+ Server =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result, []}},
+ {options, [{keepalive, true},{active, false}
+ | 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, []}},
+ {options, [{keepalive, true},{active, false}
+ | ClientOpts]}]),
+ Server ! listen,
- Connect = fun() ->
- {ok, _Socket} = rpc:call(ClientNode, gen_tcp, connect,
- [Hostname, Port, [binary]]),
- %% Make sure that ssl_accept is called before
- %% client process ends and closes socket.
- ct:sleep(?SLEEP)
- end,
-
- _Client = spawn_link(Connect),
-
- ssl_test_lib:check_result(Server, {error,closed}).
-
-%%--------------------------------------------------------------------
-tls_closed_in_active_once() ->
- [{doc, "Test that ssl_closed is delivered in active once with non-empty buffer, check ERL-420."}].
-
-tls_closed_in_active_once(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- {_ClientNode, _ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- TcpOpts = [binary, {reuseaddr, true}],
- Port = ssl_test_lib:inet_port(node()),
- Server = fun() ->
- {ok, Listen} = gen_tcp:listen(Port, TcpOpts),
- {ok, TcpServerSocket} = gen_tcp:accept(Listen),
- {ok, ServerSocket} = ssl:ssl_accept(TcpServerSocket, ServerOpts),
- lists:foreach(
- fun(_) ->
- ssl:send(ServerSocket, "some random message\r\n")
- end, lists:seq(1, 20)),
- %% Close TCP instead of SSL socket to trigger the bug:
- gen_tcp:close(TcpServerSocket),
- gen_tcp:close(Listen)
- end,
- spawn_link(Server),
- {ok, Socket} = ssl:connect(Hostname, Port, [{active, false} | ClientOpts]),
- Result = tls_closed_in_active_once_loop(Socket),
- ssl:close(Socket),
- case Result of
- ok -> ok;
- _ -> ct:fail(Result)
- end.
-
-tls_closed_in_active_once_loop(Socket) ->
- case ssl:setopts(Socket, [{active, once}]) of
- ok ->
- receive
- {ssl, Socket, _} ->
- tls_closed_in_active_once_loop(Socket);
- {ssl_closed, Socket} ->
- ok
- after 5000 ->
- no_ssl_closed_received
- end;
- {error, closed} ->
- ok
- end.
-%%--------------------------------------------------------------------
-connect_dist() ->
- [{doc,"Test a simple connect as is used by distribution"}].
-
-connect_dist(Config) when is_list(Config) ->
- ClientOpts0 = ssl_test_lib:ssl_options(client_kc_opts, Config),
- ClientOpts = [{ssl_imp, new},{active, false}, {packet,4}|ClientOpts0],
- ServerOpts0 = ssl_test_lib:ssl_options(server_kc_opts, Config),
- ServerOpts = [{ssl_imp, new},{active, false}, {packet,4}|ServerOpts0],
+ {Client1, #sslsocket{}} =
+ ssl_test_lib:start_client([return_socket,
+ {node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result, []}},
+ {options, [{keepalive, true},{active, false}
+ | ClientOpts]}]),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ ct:log("Testcase ~p, Client ~p Server ~p ~n",
+ [self(), Client, Server]),
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, connect_dist_s, []}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, connect_dist_c, []}},
- {options, ClientOpts}]),
-
ssl_test_lib:check_result(Server, ok, Client, ok),
+ ssl_test_lib:check_result(Server, ok, Client1, ok),
ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-%%--------------------------------------------------------------------
-
-clear_pem_cache() ->
- [{doc,"Test that internal reference tabel is cleaned properly even when "
- " the PEM cache is cleared" }].
-clear_pem_cache(Config) when is_list(Config) ->
- {status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)),
- [_, _,_, _, Prop] = StatusInfo,
- State = ssl_test_lib:state(Prop),
- [_,{FilRefDb, _} |_] = element(6, State),
- {Server, Client} = basic_verify_test_no_close(Config),
- CountReferencedFiles = fun({_, -1}, Acc) ->
- Acc;
- ({_, N}, Acc) ->
- N + Acc
- end,
-
- 2 = ets:foldl(CountReferencedFiles, 0, FilRefDb),
- ssl:clear_pem_cache(),
- _ = sys:get_status(whereis(ssl_manager)),
- {Server1, Client1} = basic_verify_test_no_close(Config),
- 4 = ets:foldl(CountReferencedFiles, 0, FilRefDb),
- ssl_test_lib:close(Server),
ssl_test_lib:close(Client),
- ct:sleep(2000),
- _ = sys:get_status(whereis(ssl_manager)),
- 2 = ets:foldl(CountReferencedFiles, 0, FilRefDb),
- ssl_test_lib:close(Server1),
- ssl_test_lib:close(Client1),
- ct:sleep(2000),
- _ = sys:get_status(whereis(ssl_manager)),
- 0 = ets:foldl(CountReferencedFiles, 0, FilRefDb).
+ ssl_test_lib:close(Client1).
+defaults(Config) when is_list(Config)->
+ Versions = ssl:versions(),
+ true = lists:member(sslv3, proplists:get_value(available, Versions)),
+ false = lists:member(sslv3, proplists:get_value(supported, Versions)),
+ true = lists:member('tlsv1', proplists:get_value(available, Versions)),
+ false = lists:member('tlsv1', proplists:get_value(supported, Versions)),
+ true = lists:member('tlsv1.1', proplists:get_value(available, Versions)),
+ false = lists:member('tlsv1.1', proplists:get_value(supported, Versions)),
+ true = lists:member('tlsv1.2', proplists:get_value(available, Versions)),
+ true = lists:member('tlsv1.2', proplists:get_value(supported, Versions)),
+ false = lists:member({rsa,rc4_128,sha}, ssl:cipher_suites()),
+ true = lists:member({rsa,rc4_128,sha}, ssl:cipher_suites(all)),
+ false = lists:member({rsa,des_cbc,sha}, ssl:cipher_suites()),
+ true = lists:member({rsa,des_cbc,sha}, ssl:cipher_suites(all)),
+ false = lists:member({dhe_rsa,des_cbc,sha}, ssl:cipher_suites()),
+ true = lists:member({dhe_rsa,des_cbc,sha}, ssl:cipher_suites(all)),
+ true = lists:member('dtlsv1.2', proplists:get_value(available_dtls, Versions)),
+ true = lists:member('dtlsv1', proplists:get_value(available_dtls, Versions)),
+ true = lists:member('dtlsv1.2', proplists:get_value(supported_dtls, Versions)),
+ false = lists:member('dtlsv1', proplists:get_value(supported_dtls, Versions)).
-%%--------------------------------------------------------------------
fallback() ->
[{doc, "Test TLS_FALLBACK_SCSV downgrade prevention"}].
@@ -1263,7 +217,6 @@ fallback(Config) when is_list(Config) ->
| ClientOpts]}]),
ssl_test_lib:check_server_alert(Server, Client, inappropriate_fallback).
-
%%--------------------------------------------------------------------
cipher_format() ->
[{doc, "Test that cipher conversion from maps | tuples | stings to binarys works"}].
@@ -1277,175 +230,6 @@ cipher_format(Config) when is_list(Config) ->
ssl:close(Socket2).
%%--------------------------------------------------------------------
-suite_to_str() ->
- [{doc, "Test that the suite_to_str API works"}].
-suite_to_str(Config) when is_list(Config) ->
- "TLS_EMPTY_RENEGOTIATION_INFO_SCSV" =
- ssl:suite_to_str(#{key_exchange => null,
- cipher => null,
- mac => null,
- prf => null}),
- "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256" =
- ssl:suite_to_str(#{key_exchange => ecdhe_ecdsa,
- cipher => aes_128_gcm,
- mac => aead,
- prf => sha256}),
- "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256" =
- ssl:suite_to_str(#{key_exchange => ecdh_rsa,
- cipher => aes_128_cbc,
- mac => sha256,
- prf => sha256}).
-
-%%--------------------------------------------------------------------
-
-peername() ->
- [{doc,"Test API function peername/1"}].
-
-peername(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, peername_result, []}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, peername_result, []}},
- {options, [{port, 0} | ClientOpts]}]),
-
- ClientPort = ssl_test_lib:inet_port(Client),
- ServerIp = ssl_test_lib:node_to_hostip(ServerNode, server),
- ClientIp = ssl_test_lib:node_to_hostip(ClientNode, client),
- ServerMsg = {ok, {ClientIp, ClientPort}},
- ClientMsg = {ok, {ServerIp, Port}},
-
- ct:log("Testcase ~p, Client ~p Server ~p ~n",
- [self(), Client, Server]),
-
- ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-%%--------------------------------------------------------------------
-peercert() ->
- [{doc,"Test API function peercert/1"}].
-peercert(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, peercert_result, []}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, peercert_result, []}},
- {options, ClientOpts}]),
-
- CertFile = proplists:get_value(certfile, ServerOpts),
- [{'Certificate', BinCert, _}]= ssl_test_lib:pem_to_der(CertFile),
-
- ServerMsg = {error, no_peercert},
- ClientMsg = {ok, BinCert},
-
- ct:log("Testcase ~p, Client ~p Server ~p ~n",
- [self(), Client, Server]),
-
- ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-peercert_result(Socket) ->
- ssl:peercert(Socket).
-%%--------------------------------------------------------------------
-
-peercert_with_client_cert() ->
- [{doc,"Test API function peercert/1"}].
-peercert_with_client_cert(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_dsa_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_dsa_verify_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, peercert_result, []}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, peercert_result, []}},
- {options, ClientOpts}]),
-
- ServerCertFile = proplists:get_value(certfile, ServerOpts),
- [{'Certificate', ServerBinCert, _}]= ssl_test_lib:pem_to_der(ServerCertFile),
- ClientCertFile = proplists:get_value(certfile, ClientOpts),
- [{'Certificate', ClientBinCert, _}]= ssl_test_lib:pem_to_der(ClientCertFile),
-
- ServerMsg = {ok, ClientBinCert},
- ClientMsg = {ok, ServerBinCert},
-
- ct:log("Testcase ~p, Client ~p Server ~p ~n",
- [self(), Client, Server]),
-
- ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-%%--------------------------------------------------------------------
-sockname() ->
- [{doc,"Test API function sockname/1"}].
-sockname(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, sockname_result, []}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, sockname_result, []}},
- {options, [{port, 0} | ClientOpts]}]),
-
- ClientPort = ssl_test_lib:inet_port(Client),
- ServerIp =
- case proplists:get_value(protocol, Config) of
- dtls ->
- %% DTLS sockets are not connected on the server side,
- %% so we can only get a ClientIP, ServerIP will always be 0.0.0.0
- {0,0,0,0};
- _ ->
- ssl_test_lib:node_to_hostip(ServerNode, server)
- end,
-
- ClientIp = ssl_test_lib:node_to_hostip(ClientNode, client),
- ServerMsg = {ok, {ServerIp, Port}},
- ClientMsg = {ok, {ClientIp, ClientPort}},
-
- ct:log("Testcase ~p, Client ~p Server ~p ~n",
- [self(), Client, Server]),
-
- ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-sockname_result(S) ->
- ssl:sockname(S).
-
-%%--------------------------------------------------------------------
cipher_suites() ->
[{doc,"Test API function cipher_suites/2, filter_cipher_suites/2"
@@ -1542,2750 +326,95 @@ cipher_suites_mix(Config) when is_list(Config) ->
ssl_test_lib:check_result(Server, ok, Client, ok),
ssl_test_lib:close(Server),
ssl_test_lib:close(Client).
-%%--------------------------------------------------------------------
-tls_socket_options() ->
- [{doc,"Test API function getopts/2 and setopts/2"}].
+unordered_protocol_versions_server() ->
+ [{doc,"Test that the highest protocol is selected even"
+ " when it is not first in the versions list."}].
-tls_socket_options(Config) when is_list(Config) ->
+unordered_protocol_versions_server(Config) when is_list(Config) ->
ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Values = [{mode, list}, {packet, 0}, {header, 0},
- {active, true}],
- %% Shall be the reverse order of Values!
- Options = [active, header, packet, mode],
-
- NewValues = [{mode, binary}, {active, once}],
- %% Shall be the reverse order of NewValues!
- NewOptions = [active, mode],
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, tls_socket_options_result,
- [Options, Values, NewOptions, NewValues]}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, tls_socket_options_result,
- [Options, Values, NewOptions, NewValues]}},
- {options, ClientOpts}]),
-
- ssl_test_lib:check_result(Server, ok, Client, ok),
-
- ssl_test_lib:close(Server),
-
- {ok, Listen} = ssl:listen(0, ServerOpts),
- {ok,[{mode,list}]} = ssl:getopts(Listen, [mode]),
- ok = ssl:setopts(Listen, [{mode, binary}]),
- {ok,[{mode, binary}]} = ssl:getopts(Listen, [mode]),
- {ok,[{recbuf, _}]} = ssl:getopts(Listen, [recbuf]),
- ssl:close(Listen).
-
-tls_socket_options_result(Socket, Options, DefaultValues, NewOptions, NewValues) ->
- %% Test get/set emulated opts
- {ok, DefaultValues} = ssl:getopts(Socket, Options),
- ssl:setopts(Socket, NewValues),
- {ok, NewValues} = ssl:getopts(Socket, NewOptions),
- %% Test get/set inet opts
- {ok,[{nodelay,false}]} = ssl:getopts(Socket, [nodelay]),
- ssl:setopts(Socket, [{nodelay, true}]),
- {ok,[{nodelay, true}]} = ssl:getopts(Socket, [nodelay]),
- {ok, All} = ssl:getopts(Socket, []),
- ct:log("All opts ~p~n", [All]),
- ok.
-
-
-%%--------------------------------------------------------------------
-socket_options() ->
- [{doc,"Test API function getopts/2 and setopts/2"}].
+ ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
-socket_options(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Values = [{mode, list}, {active, true}],
- %% Shall be the reverse order of Values!
- Options = [active, mode],
-
- NewValues = [{mode, binary}, {active, once}],
- %% Shall be the reverse order of NewValues!
- NewOptions = [active, mode],
-
Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
{from, self()},
- {mfa, {?MODULE, socket_options_result,
- [Options, Values, NewOptions, NewValues]}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, socket_options_result,
- [Options, Values, NewOptions, NewValues]}},
- {options, ClientOpts}]),
-
- ssl_test_lib:check_result(Server, ok, Client, ok),
-
- ssl_test_lib:close(Server),
-
- {ok, Listen} = ssl:listen(0, ServerOpts),
- {ok,[{mode,list}]} = ssl:getopts(Listen, [mode]),
- ok = ssl:setopts(Listen, [{mode, binary}]),
- {ok,[{mode, binary}]} = ssl:getopts(Listen, [mode]),
- {ok,[{recbuf, _}]} = ssl:getopts(Listen, [recbuf]),
- ssl:close(Listen).
-
-
-socket_options_result(Socket, Options, DefaultValues, NewOptions, NewValues) ->
- %% Test get/set emulated opts
- {ok, DefaultValues} = ssl:getopts(Socket, Options),
- ssl:setopts(Socket, NewValues),
- {ok, NewValues} = ssl:getopts(Socket, NewOptions),
- %% Test get/set inet opts
- {ok,[{reuseaddr, _}]} = ssl:getopts(Socket, [reuseaddr]),
- {ok, All} = ssl:getopts(Socket, []),
- ct:log("All opts ~p~n", [All]),
- ok.
-
-
-%%--------------------------------------------------------------------
-invalid_inet_get_option() ->
- [{doc,"Test handling of invalid inet options in getopts"}].
-
-invalid_inet_get_option(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, get_invalid_inet_option, []}},
- {options, 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, no_result, []}},
- {options, ClientOpts}]),
-
- ct:log("Testcase ~p, Client ~p Server ~p ~n",
- [self(), Client, Server]),
-
- ssl_test_lib:check_result(Server, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-%%--------------------------------------------------------------------
-invalid_inet_get_option_not_list() ->
- [{doc,"Test handling of invalid type in getopts"}].
-
-invalid_inet_get_option_not_list(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, get_invalid_inet_option_not_list, []}},
- {options, 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, no_result, []}},
- {options, ClientOpts}]),
-
- ct:log("Testcase ~p, Client ~p Server ~p ~n",
- [self(), Client, Server]),
-
- ssl_test_lib:check_result(Server, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-
-get_invalid_inet_option_not_list(Socket) ->
- {error, {options, {socket_options, some_invalid_atom_here}}}
- = ssl:getopts(Socket, some_invalid_atom_here),
- ok.
-
-%%--------------------------------------------------------------------
-invalid_inet_get_option_improper_list() ->
- [{doc,"Test handling of invalid type in getopts"}].
-
-invalid_inet_get_option_improper_list(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, get_invalid_inet_option_improper_list, []}},
- {options, 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, no_result, []}},
- {options, ClientOpts}]),
-
- ct:log("Testcase ~p, Client ~p Server ~p ~n",
- [self(), Client, Server]),
-
- ssl_test_lib:check_result(Server, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-
-get_invalid_inet_option_improper_list(Socket) ->
- {error, {options, {socket_options, foo,_}}} = ssl:getopts(Socket, [packet | foo]),
- ok.
-
-%%--------------------------------------------------------------------
-invalid_inet_set_option() ->
- [{doc,"Test handling of invalid inet options in setopts"}].
-
-invalid_inet_set_option(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, set_invalid_inet_option, []}},
- {options, 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, no_result, []}},
- {options, ClientOpts}]),
-
- ct:log("Testcase ~p, Client ~p Server ~p ~n",
- [self(), Client, Server]),
-
- ssl_test_lib:check_result(Server, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-set_invalid_inet_option(Socket) ->
- {error, {options, {socket_options, {packet, foo}}}} = ssl:setopts(Socket, [{packet, foo}]),
- {error, {options, {socket_options, {header, foo}}}} = ssl:setopts(Socket, [{header, foo}]),
- {error, {options, {socket_options, {active, foo}}}} = ssl:setopts(Socket, [{active, foo}]),
- {error, {options, {socket_options, {mode, foo}}}} = ssl:setopts(Socket, [{mode, foo}]),
- ok.
-%%--------------------------------------------------------------------
-invalid_inet_set_option_not_list() ->
- [{doc,"Test handling of invalid type in setopts"}].
-
-invalid_inet_set_option_not_list(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, set_invalid_inet_option_not_list, []}},
- {options, 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, no_result, []}},
- {options, ClientOpts}]),
-
- ct:log("Testcase ~p, Client ~p Server ~p ~n",
- [self(), Client, Server]),
-
- ssl_test_lib:check_result(Server, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-
-set_invalid_inet_option_not_list(Socket) ->
- {error, {options, {not_a_proplist, some_invalid_atom_here}}}
- = ssl:setopts(Socket, some_invalid_atom_here),
- ok.
-
-%%--------------------------------------------------------------------
-invalid_inet_set_option_improper_list() ->
- [{doc,"Test handling of invalid tye in setopts"}].
-
-invalid_inet_set_option_improper_list(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, set_invalid_inet_option_improper_list, []}},
- {options, 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, no_result, []}},
- {options, ClientOpts}]),
-
- ct:log("Testcase ~p, Client ~p Server ~p ~n",
- [self(), Client, Server]),
-
- ssl_test_lib:check_result(Server, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-set_invalid_inet_option_improper_list(Socket) ->
- {error, {options, {not_a_proplist, [{packet, 0} | {foo, 2}]}}} =
- ssl:setopts(Socket, [{packet, 0} | {foo, 2}]),
- ok.
-
-%%--------------------------------------------------------------------
-tls_misc_ssl_options() ->
- [{doc,"Test what happens when we give valid options"}].
-
-tls_misc_ssl_options(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- %% Check that ssl options not tested elsewhere are filtered away e.i. not passed to inet.
- TestOpts = [{depth, 1},
- {key, undefined},
- {password, []},
- {reuse_session, fun(_,_,_,_) -> true end},
- {cb_info, {gen_tcp, tcp, tcp_closed, tcp_error}}],
-
- Server =
- ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, TestOpts ++ 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, TestOpts ++ ClientOpts}]),
-
- ct:log("Testcase ~p, Client ~p Server ~p ~n",
- [self(), Client, Server]),
-
- ssl_test_lib:check_result(Server, ok, Client, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-%%--------------------------------------------------------------------
-ssl_options_not_proplist() ->
- [{doc,"Test what happens if an option is not a key value tuple"}].
-
-ssl_options_not_proplist(Config) when is_list(Config) ->
- BadOption = {client_preferred_next_protocols,
- client, [<<"spdy/3">>,<<"http/1.1">>], <<"http/1.1">>},
- {option_not_a_key_value_tuple, BadOption} =
- ssl:connect("twitter.com", 443, [binary, {active, false},
- BadOption]).
-
-%%--------------------------------------------------------------------
-raw_ssl_option() ->
- [{doc,"Ensure that a single 'raw' option is passed to ssl:listen correctly."}].
-
-raw_ssl_option(Config) when is_list(Config) ->
- % 'raw' option values are platform-specific; these are the Linux values:
- IpProtoTcp = 6,
- % Use TCP_KEEPIDLE, because (e.g.) TCP_MAXSEG can't be read back reliably.
- TcpKeepIdle = 4,
- KeepAliveTimeSecs = 55,
- LOptions = [{raw, IpProtoTcp, TcpKeepIdle, <<KeepAliveTimeSecs:32/native>>}],
- {ok, LSocket} = ssl:listen(0, LOptions),
- % Per http://www.erlang.org/doc/man/inet.html#getopts-2, we have to specify
- % exactly which raw option we want, and the size of the buffer.
- {ok, [{raw, IpProtoTcp, TcpKeepIdle, <<KeepAliveTimeSecs:32/native>>}]} = ssl:getopts(LSocket, [{raw, IpProtoTcp, TcpKeepIdle, 4}]).
-
-
-%%--------------------------------------------------------------------
-versions() ->
- [{doc,"Test API function versions/0"}].
-
-versions(Config) when is_list(Config) ->
- [_|_] = Versions = ssl:versions(),
- ct:log("~p~n", [Versions]).
-
-
-%%--------------------------------------------------------------------
-eccs() ->
- [{doc, "Test API functions eccs/0 and eccs/1"}].
-
-eccs(Config) when is_list(Config) ->
- [_|_] = All = ssl:eccs(),
- [] = SSL3 = ssl:eccs(sslv3),
- [_|_] = Tls = ssl:eccs(tlsv1),
- [_|_] = Tls1 = ssl:eccs('tlsv1.1'),
- [_|_] = Tls2 = ssl:eccs('tlsv1.2'),
- [_|_] = Tls1 = ssl:eccs('dtlsv1'),
- [_|_] = Tls2 = ssl:eccs('dtlsv1.2'),
- %% ordering is currently unverified by the test
- true = lists:sort(All) =:= lists:usort(SSL3 ++ Tls ++ Tls1 ++ Tls2),
- ok.
-
-%%--------------------------------------------------------------------
-send_recv() ->
- [{doc,""}].
-send_recv(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server =
- ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result, []}},
- {options, [{active, false} | 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, []}},
- {options, [{active, false} | ClientOpts]}]),
-
- ct:log("Testcase ~p, Client ~p Server ~p ~n",
- [self(), Client, Server]),
-
- ssl_test_lib:check_result(Server, ok, Client, ok),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-%%--------------------------------------------------------------------
-tls_send_close() ->
- [{doc,""}].
-tls_send_close(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server =
- ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result, []}},
- {options, [{active, false} | ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
- {ok, TcpS} = rpc:call(ClientNode, gen_tcp, connect,
- [Hostname,Port,[binary, {active, false}]]),
- {ok, SslS} = rpc:call(ClientNode, ssl, connect,
- [TcpS,[{active, false}|ClientOpts]]),
-
- ct:log("Testcase ~p, Client ~p Server ~p ~n",
- [self(), self(), Server]),
- ok = ssl:send(SslS, "Hello world"),
- {ok,<<"Hello world">>} = ssl:recv(SslS, 11),
- gen_tcp:close(TcpS),
- {error, _} = ssl:send(SslS, "Hello world").
-
-%%--------------------------------------------------------------------
-version_option() ->
- [{doc, "Use version option and do no specify ciphers list. Bug specified incorrect ciphers"}].
-version_option(Config) when is_list(Config) ->
- Versions = proplists:get_value(supported, ssl:versions()),
- [version_option_test(Config, Version) || Version <- Versions].
-
-%%--------------------------------------------------------------------
-close_transport_accept() ->
- [{doc,"Tests closing ssl socket when waiting on ssl:transport_accept/1"}].
-
-close_transport_accept(Config) when is_list(Config) ->
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- {_ClientNode, ServerNode, _Hostname} = ssl_test_lib:run_where(Config),
-
- Port = 0,
- Opts = [{active, false} | ServerOpts],
- {ok, ListenSocket} = rpc:call(ServerNode, ssl, listen, [Port, Opts]),
- spawn_link(fun() ->
- ct:sleep(?SLEEP),
- rpc:call(ServerNode, ssl, close, [ListenSocket])
- end),
- case rpc:call(ServerNode, ssl, transport_accept, [ListenSocket]) of
- {error, closed} ->
- ok;
- Other ->
- exit({?LINE, Other})
- end.
-%%--------------------------------------------------------------------
-recv_active() ->
- [{doc,"Test recv on active socket"}].
-
-recv_active(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server =
- ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, try_recv_active, []}},
- {options, [{active, true} | ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
- Client =
- ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, try_recv_active, []}},
- {options, [{active, true} | ClientOpts]}]),
-
- ssl_test_lib:check_result(Server, ok, Client, ok),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-%%--------------------------------------------------------------------
-recv_active_once() ->
- [{doc,"Test recv on active (once) socket"}].
-
-recv_active_once(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server =
- ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, try_recv_active_once, []}},
- {options, [{active, once} | ServerOpts]}]),
+ {mfa, {?MODULE, protocol_info_result, []}},
+ {options, [{versions, ['tlsv1.1', 'tlsv1.2']} | ServerOpts]}]),
Port = ssl_test_lib:inet_port(Server),
- Client =
- ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, try_recv_active_once, []}},
- {options, [{active, once} | ClientOpts]}]),
-
- ssl_test_lib:check_result(Server, ok, Client, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-
-
-
-
-%%--------------------------------------------------------------------
-recv_active_n() ->
- [{doc,"Test recv on active (n) socket"}].
-
-recv_active_n(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server =
- ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, try_recv_active_once, []}},
- {options, [{active, 1} | ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
- Client =
- ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, try_recv_active_once, []}},
- {options, [{active, 1} | ClientOpts]}]),
-
- ssl_test_lib:check_result(Server, ok, Client, ok),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-
-%%--------------------------------------------------------------------
-%% Test case adapted from gen_tcp_misc_SUITE.
-active_n() ->
- [{doc,"Test {active,N} option"}].
-
-active_n(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- Port = ssl_test_lib:inet_port(node()),
- N = 3,
- LS = ok(ssl:listen(Port, [{active,N}|ServerOpts])),
- [{active,N}] = ok(ssl:getopts(LS, [active])),
- active_n_common(LS, N),
- Self = self(),
- spawn_link(fun() ->
- S0 = ok(ssl:transport_accept(LS)),
- {ok, S} = ssl:handshake(S0),
- ok = ssl:setopts(S, [{active,N}]),
- [{active,N}] = ok(ssl:getopts(S, [active])),
- ssl:controlling_process(S, Self),
- Self ! {server, S}
- end),
- C = ok(ssl:connect("localhost", Port, [{active,N}|ClientOpts])),
- [{active,N}] = ok(ssl:getopts(C, [active])),
- S = receive
- {server, S0} -> S0
- after
- 1000 ->
- exit({error, connect})
- end,
- active_n_common(C, N),
- active_n_common(S, N),
- ok = ssl:setopts(C, [{active,N}]),
- ok = ssl:setopts(S, [{active,N}]),
- ReceiveMsg = fun(Socket, Msg) ->
- receive
- {ssl,Socket,Msg} ->
- ok;
- {ssl,Socket,Begin} ->
- receive
- {ssl,Socket,End} ->
- Msg = Begin ++ End,
- ok
- after 1000 ->
- exit(timeout)
- end
- after 1000 ->
- exit(timeout)
- end
- end,
- repeat(3, fun(I) ->
- Msg = "message "++integer_to_list(I),
- ok = ssl:send(C, Msg),
- ReceiveMsg(S, Msg),
- ok = ssl:send(S, Msg),
- ReceiveMsg(C, Msg)
- end),
- receive
- {ssl_passive,S} ->
- [{active,false}] = ok(ssl:getopts(S, [active]))
- after
- 1000 ->
- exit({error,ssl_passive})
- end,
- receive
- {ssl_passive,C} ->
- [{active,false}] = ok(ssl:getopts(C, [active]))
- after
- 1000 ->
- exit({error,ssl_passive})
- end,
- LS2 = ok(ssl:listen(0, [{active,0}])),
- receive
- {ssl_passive,LS2} ->
- [{active,false}] = ok(ssl:getopts(LS2, [active]))
- after
- 1000 ->
- exit({error,ssl_passive})
- end,
- ok = ssl:close(LS2),
- ok = ssl:close(C),
- ok = ssl:close(S),
- ok = ssl:close(LS),
- ok.
-
-active_n_common(S, N) ->
- ok = ssl:setopts(S, [{active,-N}]),
- receive
- {ssl_passive, S} -> ok
- after
- 1000 ->
- error({error,ssl_passive_failure})
- end,
- [{active,false}] = ok(ssl:getopts(S, [active])),
- ok = ssl:setopts(S, [{active,0}]),
- receive
- {ssl_passive, S} -> ok
- after
- 1000 ->
- error({error,ssl_passive_failure})
- end,
- ok = ssl:setopts(S, [{active,32767}]),
- {error,{options,_}} = ssl:setopts(S, [{active,1}]),
- {error,{options,_}} = ssl:setopts(S, [{active,-32769}]),
- ok = ssl:setopts(S, [{active,-32768}]),
- receive
- {ssl_passive, S} -> ok
- after
- 1000 ->
- error({error,ssl_passive_failure})
- end,
- [{active,false}] = ok(ssl:getopts(S, [active])),
- ok = ssl:setopts(S, [{active,N}]),
- ok = ssl:setopts(S, [{active,true}]),
- [{active,true}] = ok(ssl:getopts(S, [active])),
- receive
- _ -> error({error,active_n})
- after
- 0 ->
- ok
- end,
- ok = ssl:setopts(S, [{active,N}]),
- ok = ssl:setopts(S, [{active,once}]),
- [{active,once}] = ok(ssl:getopts(S, [active])),
- receive
- _ -> error({error,active_n})
- after
- 0 ->
- ok
- end,
- {error,{options,_}} = ssl:setopts(S, [{active,32768}]),
- ok = ssl:setopts(S, [{active,false}]),
- [{active,false}] = ok(ssl:getopts(S, [active])),
- ok.
-
-ok({ok,V}) -> V.
-
-repeat(N, Fun) ->
- repeat(N, N, Fun).
-
-repeat(N, T, Fun) when is_integer(N), N > 0 ->
- Fun(T-N),
- repeat(N-1, T, Fun);
-repeat(_, _, _) ->
- ok.
-
-%%--------------------------------------------------------------------
-dh_params() ->
- [{doc,"Test to specify DH-params file in server."}].
-
-dh_params(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- DataDir = proplists:get_value(data_dir, Config),
- DHParamFile = filename:join(DataDir, "dHParam.pem"),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, [{dhfile, DHParamFile} | 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,
- [{ciphers,[{dhe_rsa,aes_256_cbc,sha}]} |
- ClientOpts]}]),
-
- ssl_test_lib:check_result(Server, ok, Client, ok),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-%%--------------------------------------------------------------------
-tls_upgrade() ->
- [{doc,"Test that you can upgrade an tcp connection to an ssl connection"}].
-
-tls_upgrade(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- TcpOpts = [binary, {reuseaddr, true}],
-
- Server = ssl_test_lib:start_upgrade_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE,
- upgrade_result, []}},
- {tcp_options,
- [{active, false} | TcpOpts]},
- {ssl_options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_upgrade_client([{node, ClientNode},
- {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, upgrade_result, []}},
- {tcp_options, [binary]},
- {ssl_options, ClientOpts}]),
-
- ct:log("Testcase ~p, Client ~p Server ~p ~n",
- [self(), Client, Server]),
-
- ssl_test_lib:check_result(Server, ok, Client, ok),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-upgrade_result(Socket) ->
- ssl:setopts(Socket, [{active, true}]),
- ok = ssl:send(Socket, "Hello world"),
- %% Make sure binary is inherited from tcp socket and that we do
- %% not get the list default!
- receive
- {ssl, _, <<"H">>} ->
- receive
- {ssl, _, <<"ello world">>} ->
- ok
- end;
- {ssl, _, <<"Hello world">>} ->
- ok
- end.
-
-
-%%--------------------------------------------------------------------
-internal_active_1() ->
- [{doc,"Test internal active 1 (behave as internal active once)"}].
-
-internal_active_1(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server =
- ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, [{active, true} | 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, [{active, true} | ClientOpts]}]),
-
- ssl_test_lib:check_result(Server, ok, Client, ok),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-%%--------------------------------------------------------------------
-tls_upgrade_with_timeout() ->
- [{doc,"Test ssl_accept/3"}].
-
-tls_upgrade_with_timeout(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- TcpOpts = [binary, {reuseaddr, true}],
-
- Server = ssl_test_lib:start_upgrade_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {timeout, 5000},
- {mfa, {?MODULE,
- upgrade_result, []}},
- {tcp_options,
- [{active, false} | TcpOpts]},
- {ssl_options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_upgrade_client([{node, ClientNode},
- {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, upgrade_result, []}},
- {tcp_options, TcpOpts},
- {ssl_options, ClientOpts}]),
-
- ct:log("Testcase ~p, Client ~p Server ~p ~n",
- [self(), Client, Server]),
-
- ssl_test_lib:check_result(Server, ok, Client, ok),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-%%--------------------------------------------------------------------
-tls_downgrade() ->
- [{doc,"Test that you can downgarde an ssl connection to an tcp connection"}].
-tls_downgrade(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, tls_downgrade_result, [self()]}},
- {options, [{active, false} | ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, tls_downgrade_result, [self()]}},
- {options, [{active, false} |ClientOpts]}]),
-
-
- ssl_test_lib:check_result(Server, ready, Client, ready),
-
- Server ! go,
- Client ! go,
-
- ssl_test_lib:check_result(Server, ok, Client, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-%%--------------------------------------------------------------------
-close_with_timeout() ->
- [{doc,"Test normal (not downgrade) ssl:close/2"}].
-close_with_timeout(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, tls_close, []}},
- {options,[{active, false} | ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, tls_close, []}},
- {options, [{active, false} |ClientOpts]}]),
-
- ssl_test_lib:check_result(Server, ok, Client, ok).
-
-
-%%--------------------------------------------------------------------
-tls_tcp_connect() ->
- [{doc,"Test what happens when a tcp tries to connect, i,e. a bad (ssl) packet is sent first"}].
-
-tls_tcp_connect(Config) when is_list(Config) ->
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- TcpOpts = [binary, {reuseaddr, true}, {active, false}],
-
- Server = ssl_test_lib:start_upgrade_server_error([{node, ServerNode}, {port, 0},
- {from, self()},
- {timeout, 5000},
- {mfa, {?MODULE, dummy, []}},
- {tcp_options, TcpOpts},
- {ssl_options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
-
- {ok, Socket} = gen_tcp:connect(Hostname, Port, [binary, {packet, 0}]),
- ct:log("Testcase ~p connected to Server ~p ~n", [self(), Server]),
- gen_tcp:send(Socket, "<SOME GARBLED NON SSL MESSAGE>"),
-
- receive
- {tcp_closed, Socket} ->
- receive
- {Server, {error, Error}} ->
- ct:log("Error ~p", [Error])
- end
- end.
-%%--------------------------------------------------------------------
-tls_tcp_connect_big() ->
- [{doc,"Test what happens when a tcp tries to connect, i,e. a bad big (ssl) packet is sent first"}].
-
-tls_tcp_connect_big(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- TcpOpts = [binary, {reuseaddr, true}],
-
- Rand = crypto:strong_rand_bytes(?MAX_CIPHER_TEXT_LENGTH+1),
- Server = ssl_test_lib:start_upgrade_server_error([{node, ServerNode}, {port, 0},
- {from, self()},
- {timeout, 5000},
- {mfa, {?MODULE, dummy, []}},
- {tcp_options, TcpOpts},
- {ssl_options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
-
- {ok, Socket} = gen_tcp:connect(Hostname, Port, [binary, {packet, 0}]),
- ct:log("Testcase ~p connected to Server ~p ~n", [self(), Server]),
-
- gen_tcp:send(Socket, <<?BYTE(0),
- ?BYTE(3), ?BYTE(1), ?UINT16(?MAX_CIPHER_TEXT_LENGTH), Rand/binary>>),
-
- receive
- {tcp_closed, Socket} ->
- receive
- {Server, {error, timeout}} ->
- ct:fail("hangs");
- {Server, {error, Error}} ->
- ct:log("Error ~p", [Error]);
- {'EXIT', Server, _} ->
- ok
- end
- end.
-
-%%--------------------------------------------------------------------
-ipv6() ->
- [{require, ipv6_hosts},
- {doc,"Test ipv6."}].
-ipv6(Config) when is_list(Config) ->
- {ok, Hostname0} = inet:gethostname(),
-
- case lists:member(list_to_atom(Hostname0), ct:get_config(ipv6_hosts)) of
- true ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- {ClientNode, ServerNode, Hostname} =
- ssl_test_lib:run_where(Config, ipv6),
- Server = ssl_test_lib:start_server([{node, ServerNode},
- {port, 0}, {from, self()},
- {mfa, {ssl_test_lib, send_recv_result, []}},
- {options,
- [inet6, {active, false} | 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, []}},
- {options,
- [inet6, {active, false} | ClientOpts]}]),
-
- ct:log("Testcase ~p, Client ~p Server ~p ~n",
- [self(), Client, Server]),
-
- ssl_test_lib:check_result(Server, ok, Client, ok),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client);
- false ->
- {skip, "Host does not support IPv6"}
- end.
-
-%%--------------------------------------------------------------------
-
-invalid_keyfile() ->
- [{doc,"Test what happens with an invalid key file"}].
-invalid_keyfile(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- BadOpts = ssl_test_lib:ssl_options(server_bad_key, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server =
- ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
- {from, self()},
- {options, BadOpts}]),
-
- Port = ssl_test_lib:inet_port(Server),
-
- Client =
- ssl_test_lib:start_client_error([{node, ClientNode},
- {port, Port}, {host, Hostname},
- {from, self()}, {options, ClientOpts}]),
+ {from, self()},
+ {mfa, {?MODULE, protocol_info_result, []}},
+ {options, ClientOpts}]),
- File = proplists:get_value(keyfile,BadOpts),
- ssl_test_lib:check_result(Server, {error,{options, {keyfile, File, {error,enoent}}}}, Client,
- {error, closed}).
+ ServerMsg = ClientMsg = {ok,'tlsv1.2'},
+ ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg).
%%--------------------------------------------------------------------
+unordered_protocol_versions_client() ->
+ [{doc,"Test that the highest protocol is selected even"
+ " when it is not first in the versions list."}].
-invalid_certfile() ->
- [{doc,"Test what happens with an invalid cert file"}].
-
-invalid_certfile(Config) when is_list(Config) ->
+unordered_protocol_versions_client(Config) when is_list(Config) ->
ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerBadOpts = ssl_test_lib:ssl_options(server_bad_cert, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server =
- ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
- {from, self()},
- {options, ServerBadOpts}]),
-
- Port = ssl_test_lib:inet_port(Server),
-
- Client =
- ssl_test_lib:start_client_error([{node, ClientNode},
- {port, Port}, {host, Hostname},
- {from, self()},
- {options, ClientOpts}]),
- File = proplists:get_value(certfile, ServerBadOpts),
- ssl_test_lib:check_result(Server, {error,{options, {certfile, File, {error,enoent}}}},
- Client, {error, closed}).
-
-
-%%--------------------------------------------------------------------
-invalid_cacertfile() ->
- [{doc,"Test what happens with an invalid cacert file"}].
-
-invalid_cacertfile(Config) when is_list(Config) ->
- ClientOpts = [{reuseaddr, true}|ssl_test_lib:ssl_options(client_opts, Config)],
- ServerBadOpts = [{reuseaddr, true}|ssl_test_lib:ssl_options(server_bad_ca, Config)],
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server0 =
- ssl_test_lib:start_server_error([{node, ServerNode},
- {port, 0}, {from, self()},
- {options, ServerBadOpts}]),
-
- Port0 = ssl_test_lib:inet_port(Server0),
-
-
- Client0 =
- ssl_test_lib:start_client_error([{node, ClientNode},
- {port, Port0}, {host, Hostname},
- {from, self()},
- {options, ClientOpts}]),
-
- File0 = proplists:get_value(cacertfile, ServerBadOpts),
-
- ssl_test_lib:check_result(Server0, {error, {options, {cacertfile, File0,{error,enoent}}}},
- Client0, {error, closed}),
-
- File = File0 ++ "do_not_exit.pem",
- ServerBadOpts1 = [{cacertfile, File}|proplists:delete(cacertfile, ServerBadOpts)],
-
- Server1 =
- ssl_test_lib:start_server_error([{node, ServerNode},
- {port, 0}, {from, self()},
- {options, ServerBadOpts1}]),
-
- Port1 = ssl_test_lib:inet_port(Server1),
-
- Client1 =
- ssl_test_lib:start_client_error([{node, ClientNode},
- {port, Port1}, {host, Hostname},
- {from, self()},
- {options, ClientOpts}]),
-
-
- ssl_test_lib:check_result(Server1, {error, {options, {cacertfile, File,{error,enoent}}}},
- Client1, {error, closed}),
- ok.
-
-
-
-%%--------------------------------------------------------------------
-invalid_options() ->
- [{doc,"Test what happens when we give invalid options"}].
-
-invalid_options(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Check = fun(Client, Server, {versions, [sslv2, sslv3]} = Option) ->
- ssl_test_lib:check_result(Server,
- {error, {options, {sslv2, Option}}},
- Client,
- {error, {options, {sslv2, Option}}});
- (Client, Server, Option) ->
- ssl_test_lib:check_result(Server,
- {error, {options, Option}},
- Client,
- {error, {options, Option}})
- end,
-
- TestOpts =
- [{versions, [sslv2, sslv3]},
- {verify, 4},
- {verify_fun, function},
- {fail_if_no_peer_cert, 0},
- {verify_client_once, 1},
- {depth, four},
- {certfile, 'cert.pem'},
- {keyfile,'key.pem' },
- {password, foo},
- {cacertfile, ""},
- {dhfile,'dh.pem' },
- {ciphers, [{foo, bar, sha, ignore}]},
- {reuse_session, foo},
- {reuse_sessions, 0},
- {renegotiate_at, "10"},
- {mode, depech},
- {packet, 8.0},
- {packet_size, "2"},
- {header, a},
- {active, trice},
- {key, 'key.pem' }],
-
- [begin
- Server =
- ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
- {from, self()},
- {options, [TestOpt | ServerOpts]}]),
- %% Will never reach a point where port is used.
- Client =
- ssl_test_lib:start_client_error([{node, ClientNode}, {port, 0},
- {host, Hostname}, {from, self()},
- {options, [TestOpt | ClientOpts]}]),
- Check(Client, Server, TestOpt),
- ok
- end || TestOpt <- TestOpts],
- ok.
+ ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
-%%--------------------------------------------------------------------
-tls_shutdown() ->
- [{doc,"Test API function ssl:shutdown/2"}].
-tls_shutdown(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
{from, self()},
- {mfa, {?MODULE, tls_shutdown_result, [server]}},
- {options, [{exit_on_close, false},
- {active, false} | ServerOpts]}]),
+ {mfa, {?MODULE, protocol_info_result, []}},
+ {options, ServerOpts }]),
Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa,
- {?MODULE, tls_shutdown_result, [client]}},
- {options,
- [{exit_on_close, false},
- {active, false} | ClientOpts]}]),
-
- ssl_test_lib:check_result(Server, ok, Client, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-%%--------------------------------------------------------------------
-tls_shutdown_write() ->
- [{doc,"Test API function ssl:shutdown/2 with option write."}].
-tls_shutdown_write(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, tls_shutdown_write_result, [server]}},
- {options, [{active, false} | ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
{host, Hostname},
- {from, self()},
- {mfa, {?MODULE, tls_shutdown_write_result, [client]}},
- {options, [{active, false} | ClientOpts]}]),
-
- ssl_test_lib:check_result(Server, ok, Client, {error, closed}).
-
-%%--------------------------------------------------------------------
-tls_shutdown_both() ->
- [{doc,"Test API function ssl:shutdown/2 with option both."}].
-tls_shutdown_both(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
{from, self()},
- {mfa, {?MODULE, tls_shutdown_both_result, [server]}},
- {options, [{active, false} | ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, tls_shutdown_both_result, [client]}},
- {options, [{active, false} | ClientOpts]}]),
-
- ssl_test_lib:check_result(Server, ok, Client, {error, closed}).
-
-%%--------------------------------------------------------------------
-tls_shutdown_error() ->
- [{doc,"Test ssl:shutdown/2 error handling"}].
-tls_shutdown_error(Config) when is_list(Config) ->
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- Port = ssl_test_lib:inet_port(node()),
- {ok, Listen} = ssl:listen(Port, ServerOpts),
- {error, enotconn} = ssl:shutdown(Listen, read_write),
- ok = ssl:close(Listen),
- {error, closed} = ssl:shutdown(Listen, read_write).
-
-%%--------------------------------------------------------------------
-default_reject_anonymous()->
- [{doc,"Test that by default anonymous cipher suites are rejected "}].
-default_reject_anonymous(Config) when is_list(Config) ->
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- Version = ssl_test_lib:protocol_version(Config),
- TLSVersion = ssl_test_lib:tls_version(Version),
-
- [CipherSuite | _] = ssl_test_lib:ecdh_dh_anonymous_suites(TLSVersion),
-
- Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
- {from, self()},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {options,
- [{ciphers,[CipherSuite]} |
- ClientOpts]}]),
-
- ssl_test_lib:check_server_alert(Server, Client, insufficient_security).
-
-%%--------------------------------------------------------------------
-reuse_session() ->
- [{doc,"Test reuse of sessions (short handshake)"}].
-reuse_session(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
-
- ssl_test_lib:reuse_session(ClientOpts, ServerOpts, Config).
-%%--------------------------------------------------------------------
-reuse_session_expired() ->
- [{doc,"Test sessions is not reused when it has expired"}].
-reuse_session_expired(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server0 =
- ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, no_result, []}},
- {tcp_options, [{active, false}]},
- {options, ServerOpts}]),
- Port0 = ssl_test_lib:inet_port(Server0),
-
- Client0 = ssl_test_lib:start_client([{node, ClientNode},
- {port, Port0}, {host, Hostname},
- {mfa, {ssl_test_lib, session_id, []}},
- {from, self()}, {options, [{reuse_sessions, save} | ClientOpts]}]),
- Server0 ! listen,
-
- Client1 = ssl_test_lib:start_client([{node, ClientNode},
- {port, Port0}, {host, Hostname},
- {mfa, {ssl_test_lib, session_id, []}},
- {from, self()}, {options, ClientOpts}]),
-
- SID = receive
- {Client0, Id0} ->
- Id0
- end,
-
- receive
- {Client1, SID} ->
- ok
- after ?SLEEP ->
- ct:fail(session_not_reused)
- end,
-
- Server0 ! listen,
-
- %% Make sure session is unregistered due to expiration
- ct:sleep((?EXPIRE*2)),
-
- make_sure_expired(Hostname, Port0, SID),
-
- Client2 =
- ssl_test_lib:start_client([{node, ClientNode},
- {port, Port0}, {host, Hostname},
- {mfa, {ssl_test_lib, session_id, []}},
- {from, self()}, {options, ClientOpts}]),
- receive
- {Client2, SID} ->
- ct:fail(session_reused_when_session_expired);
- {Client2, _} ->
- ok
- end,
- process_flag(trap_exit, false),
- ssl_test_lib:close(Server0),
- ssl_test_lib:close(Client0),
- ssl_test_lib:close(Client1),
- ssl_test_lib:close(Client2).
-
-make_sure_expired(Host, Port, Id) ->
- {status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)),
- [_, _,_, _, Prop] = StatusInfo,
- State = ssl_test_lib:state(Prop),
- ClientCache = element(2, State),
-
- case ssl_session_cache:lookup(ClientCache, {{Host, Port}, Id}) of
- undefined ->
- ok;
- #session{is_resumable = false} ->
- ok;
- _ ->
- ct:sleep(?SLEEP),
- make_sure_expired(Host, Port, Id)
- end.
-
-%%--------------------------------------------------------------------
-server_does_not_want_to_reuse_session() ->
- [{doc,"Test reuse of sessions (short handshake)"}].
-server_does_not_want_to_reuse_session(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server =
- ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, session_info_result, []}},
- {options, [{reuse_session, fun(_,_,_,_) ->
- false
- end} |
- ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
- Client0 =
- ssl_test_lib:start_client([{node, ClientNode},
- {port, Port}, {host, Hostname},
- {mfa, {ssl_test_lib, no_result, []}},
- {from, self()}, {options, ClientOpts}]),
- SessionInfo =
- receive
- {Server, Info} ->
- Info
- end,
-
- Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}},
-
- %% Make sure session is registered
- ct:sleep(?SLEEP),
- ssl_test_lib:close(Client0),
-
- Client1 =
- ssl_test_lib:start_client([{node, ClientNode},
- {port, Port}, {host, Hostname},
- {mfa, {ssl_test_lib, session_info_result, []}},
- {from, self()}, {options, ClientOpts}]),
- receive
- {Client1, SessionInfo} ->
- ct:fail(session_reused_when_server_does_not_want_to);
- {Client1, _Other} ->
- ok
- end,
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client1).
-
-%%--------------------------------------------------------------------
-client_renegotiate() ->
- [{doc,"Test ssl:renegotiate/1 on client."}].
-client_renegotiate(Config) when is_list(Config) ->
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Data = "From erlang to erlang",
-
- Server =
- ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
-
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE,
- renegotiate, [Data]}},
- {options, [{reuse_sessions, false} | ClientOpts]}]),
-
- ssl_test_lib:check_result(Client, ok, Server, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-%%--------------------------------------------------------------------
-client_secure_renegotiate() ->
- [{doc,"Test ssl:renegotiate/1 on client."}].
-client_secure_renegotiate(Config) when is_list(Config) ->
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Data = "From erlang to erlang",
-
- Server =
- ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
- {options, [{secure_renegotiate, true} | ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
-
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE,
- renegotiate, [Data]}},
- {options, [{reuse_sessions, false},
- {secure_renegotiate, true}| ClientOpts]}]),
-
- ssl_test_lib:check_result(Client, ok, Server, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-%%--------------------------------------------------------------------
-client_secure_renegotiate_fallback() ->
- [{doc,"Test that we can set secure_renegotiate to false that is "
- "fallback option, we however do not have a insecure server to test against!"}].
-client_secure_renegotiate_fallback(Config) when is_list(Config) ->
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Data = "From erlang to erlang",
-
- Server =
- ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
- {options, [{secure_renegotiate, false} | ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
+ {mfa, {?MODULE, protocol_info_result, []}},
+ {options, [{versions, ['tlsv1.1', 'tlsv1.2']} | ClientOpts]}]),
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE,
- renegotiate, [Data]}},
- {options, [{reuse_sessions, false},
- {secure_renegotiate, false}| ClientOpts]}]),
-
- ssl_test_lib:check_result(Client, ok, Server, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
+ ServerMsg = ClientMsg = {ok, 'tlsv1.2'},
+ ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg).
+
+connect_dist() ->
+ [{doc,"Test a simple connect as is used by distribution"}].
-%%--------------------------------------------------------------------
-server_renegotiate() ->
- [{doc,"Test ssl:renegotiate/1 on server."}].
-server_renegotiate(Config) when is_list(Config) ->
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
+connect_dist(Config) when is_list(Config) ->
+ ClientOpts0 = ssl_test_lib:ssl_options(client_kc_opts, Config),
+ ClientOpts = [{ssl_imp, new},{active, false}, {packet,4}|ClientOpts0],
+ ServerOpts0 = ssl_test_lib:ssl_options(server_kc_opts, Config),
+ ServerOpts = [{ssl_imp, new},{active, false}, {packet,4}|ServerOpts0],
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Data = "From erlang to erlang",
-
Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
{from, self()},
- {mfa, {?MODULE,
- renegotiate, [Data]}},
+ {mfa, {?MODULE, connect_dist_s, []}},
{options, ServerOpts}]),
Port = ssl_test_lib:inet_port(Server),
-
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
- {options, [{reuse_sessions, false} | ClientOpts]}]),
-
- ssl_test_lib:check_result(Server, ok, Client, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-%%--------------------------------------------------------------------
-client_renegotiate_reused_session() ->
- [{doc,"Test ssl:renegotiate/1 on client when the ssl session will be reused."}].
-client_renegotiate_reused_session(Config) when is_list(Config) ->
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Data = "From erlang to erlang",
-
- Server =
- ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
-
Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
{host, Hostname},
- {from, self()},
- {mfa, {?MODULE,
- renegotiate_reuse_session, [Data]}},
- {options, [{reuse_sessions, true} | ClientOpts]}]),
-
- ssl_test_lib:check_result(Client, ok, Server, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-%%--------------------------------------------------------------------
-server_renegotiate_reused_session() ->
- [{doc,"Test ssl:renegotiate/1 on server when the ssl session will be reused."}].
-server_renegotiate_reused_session(Config) when is_list(Config) ->
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Data = "From erlang to erlang",
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE,
- renegotiate_reuse_session, [Data]}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
-
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
- {options, [{reuse_sessions, true} | ClientOpts]}]),
-
- ssl_test_lib:check_result(Server, ok, Client, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-%%--------------------------------------------------------------------
-client_no_wrap_sequence_number() ->
- [{doc,"Test that erlang client will renegotiate session when",
- "max sequence number celing is about to be reached. Although"
- "in the testcase we use the test option renegotiate_at"
- " to lower treashold substantially."}].
-
-client_no_wrap_sequence_number(Config) when is_list(Config) ->
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- ErlData = "From erlang to erlang",
- N = 12,
-
- Server =
- ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, no_result, []}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
-
- Version = ssl_test_lib:protocol_version(Config, tuple),
-
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {ssl_test_lib,
- trigger_renegotiate, [[ErlData, treashold(N, Version)]]}},
- {options, [{reuse_sessions, false},
- {renegotiate_at, N} | ClientOpts]}]),
-
- ssl_test_lib:check_result(Client, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-%%--------------------------------------------------------------------
-server_no_wrap_sequence_number() ->
- [{doc, "Test that erlang server will renegotiate session when",
- "max sequence number celing is about to be reached. Although"
- "in the testcase we use the test option renegotiate_at"
- " to lower treashold substantially."}].
-
-server_no_wrap_sequence_number(Config) when is_list(Config) ->
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Data = "From erlang to erlang",
- N = 12,
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib,
- trigger_renegotiate, [[Data, N+2]]}},
- {options, [{renegotiate_at, N} | 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, no_result, []}},
- {options, [{reuse_sessions, false} | ClientOpts]}]),
-
- ssl_test_lib:check_result(Server, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-%%--------------------------------------------------------------------
-der_input() ->
- [{doc,"Test to input certs and key as der"}].
-
-der_input(Config) when is_list(Config) ->
- DataDir = proplists:get_value(data_dir, Config),
- DHParamFile = filename:join(DataDir, "dHParam.pem"),
-
- {status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)),
- [_, _,_, _, Prop] = StatusInfo,
- State = ssl_test_lib:state(Prop),
- [CADb | _] = element(6, State),
-
- Size = ets:info(CADb, size),
-
- SeverVerifyOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
- {ServerCert, ServerKey, ServerCaCerts, DHParams} = der_input_opts([{dhfile, DHParamFile} |
- SeverVerifyOpts]),
- ClientVerifyOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
- {ClientCert, ClientKey, ClientCaCerts, DHParams} = der_input_opts([{dhfile, DHParamFile} |
- ClientVerifyOpts]),
- ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true},
- {dh, DHParams},
- {cert, ServerCert}, {key, ServerKey}, {cacerts, ServerCaCerts}],
- ClientOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true},
- {dh, DHParams},
- {cert, ClientCert}, {key, ClientKey}, {cacerts, ClientCaCerts}],
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result, []}},
- {options, [{active, false} | 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, []}},
- {options, [{active, false} | ClientOpts]}]),
-
- ssl_test_lib:check_result(Server, ok, Client, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client),
- Size = ets:info(CADb, size).
-
-%%--------------------------------------------------------------------
-der_input_opts(Opts) ->
- Certfile = proplists:get_value(certfile, Opts),
- CaCertsfile = proplists:get_value(cacertfile, Opts),
- Keyfile = proplists:get_value(keyfile, Opts),
- Dhfile = proplists:get_value(dhfile, Opts),
- [{_, Cert, _}] = ssl_test_lib:pem_to_der(Certfile),
- [{Asn1Type, Key, _}] = ssl_test_lib:pem_to_der(Keyfile),
- [{_, DHParams, _}] = ssl_test_lib:pem_to_der(Dhfile),
- CaCerts =
- lists:map(fun(Entry) ->
- {_, CaCert, _} = Entry,
- CaCert
- end, ssl_test_lib:pem_to_der(CaCertsfile)),
- {Cert, {Asn1Type, Key}, CaCerts, DHParams}.
-
-%%--------------------------------------------------------------------
-no_reuses_session_server_restart_new_cert() ->
- [{doc,"Check that a session is not reused if the server is restarted with a new cert."}].
-no_reuses_session_server_restart_new_cert(Config) when is_list(Config) ->
-
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- DsaServerOpts = ssl_test_lib:ssl_options(server_dsa_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server =
- ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, session_info_result, []}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
- Client0 =
- ssl_test_lib:start_client([{node, ClientNode},
- {port, Port}, {host, Hostname},
- {mfa, {ssl_test_lib, no_result, []}},
- {from, self()}, {options, ClientOpts}]),
- SessionInfo =
- receive
- {Server, Info} ->
- Info
- end,
-
- %% Make sure session is registered
- ct:sleep(?SLEEP),
- Monitor = erlang:monitor(process, Server),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client0),
- receive
- {'DOWN', Monitor, _, _, _} ->
- ok
- end,
-
- Server1 =
- ssl_test_lib:start_server([{node, ServerNode}, {port, Port},
- {from, self()},
- {mfa, {ssl_test_lib, no_result, []}},
- {options, [{reuseaddr, true} | DsaServerOpts]}]),
-
- Client1 =
- ssl_test_lib:start_client([{node, ClientNode},
- {port, Port}, {host, Hostname},
- {mfa, {ssl_test_lib, session_info_result, []}},
- {from, self()}, {options, ClientOpts}]),
- receive
- {Client1, SessionInfo} ->
- ct:fail(session_reused_when_server_has_new_cert);
- {Client1, _Other} ->
- ok
- end,
- ssl_test_lib:close(Server1),
- ssl_test_lib:close(Client1).
-
-%%--------------------------------------------------------------------
-no_reuses_session_server_restart_new_cert_file() ->
- [{doc,"Check that a session is not reused if a server is restarted with a new "
- "cert contained in a file with the same name as the old cert."}].
-
-no_reuses_session_server_restart_new_cert_file(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config),
- DsaServerOpts = ssl_test_lib:ssl_options(server_dsa_opts, Config),
- PrivDir = proplists:get_value(priv_dir, Config),
-
- NewServerOpts0 = new_config(PrivDir, ServerOpts),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server =
- ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, session_info_result, []}},
- {options, NewServerOpts0}]),
- Port = ssl_test_lib:inet_port(Server),
- Client0 =
- ssl_test_lib:start_client([{node, ClientNode},
- {port, Port}, {host, Hostname},
- {mfa, {ssl_test_lib, no_result, []}},
- {from, self()}, {options, ClientOpts}]),
- SessionInfo =
- receive
- {Server, Info} ->
- Info
- end,
-
- %% Make sure session is registered and we get
- %% new file time stamp when calling new_config!
- ct:sleep(?SLEEP* 2),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client0),
-
- ssl:clear_pem_cache(),
-
- NewServerOpts1 = new_config(PrivDir, DsaServerOpts),
-
- Server1 =
- ssl_test_lib:start_server([{node, ServerNode}, {port, Port},
- {from, self()},
- {mfa, {ssl_test_lib, no_result, []}},
- {options, [{reuseaddr, true} | NewServerOpts1]}]),
- Client1 =
- ssl_test_lib:start_client([{node, ClientNode},
- {port, Port}, {host, Hostname},
- {mfa, {ssl_test_lib, session_info_result, []}},
- {from, self()}, {options, ClientOpts}]),
- receive
- {Client1, SessionInfo} ->
- ct:fail(session_reused_when_server_has_new_cert);
- {Client1, _Other} ->
- ok
- end,
- ssl_test_lib:close(Server1),
- ssl_test_lib:close(Client1).
-
-%%--------------------------------------------------------------------
-defaults(Config) when is_list(Config)->
- Versions = ssl:versions(),
- true = lists:member(sslv3, proplists:get_value(available, Versions)),
- false = lists:member(sslv3, proplists:get_value(supported, Versions)),
- true = lists:member('tlsv1', proplists:get_value(available, Versions)),
- false = lists:member('tlsv1', proplists:get_value(supported, Versions)),
- true = lists:member('tlsv1.1', proplists:get_value(available, Versions)),
- false = lists:member('tlsv1.1', proplists:get_value(supported, Versions)),
- true = lists:member('tlsv1.2', proplists:get_value(available, Versions)),
- true = lists:member('tlsv1.2', proplists:get_value(supported, Versions)),
- false = lists:member({rsa,rc4_128,sha}, ssl:cipher_suites()),
- true = lists:member({rsa,rc4_128,sha}, ssl:cipher_suites(all)),
- false = lists:member({rsa,des_cbc,sha}, ssl:cipher_suites()),
- true = lists:member({rsa,des_cbc,sha}, ssl:cipher_suites(all)),
- false = lists:member({dhe_rsa,des_cbc,sha}, ssl:cipher_suites()),
- true = lists:member({dhe_rsa,des_cbc,sha}, ssl:cipher_suites(all)),
- true = lists:member('dtlsv1.2', proplists:get_value(available_dtls, Versions)),
- true = lists:member('dtlsv1', proplists:get_value(available_dtls, Versions)),
- true = lists:member('dtlsv1.2', proplists:get_value(supported_dtls, Versions)),
- false = lists:member('dtlsv1', proplists:get_value(supported_dtls, Versions)).
-
-%%--------------------------------------------------------------------
-reuseaddr() ->
- [{doc,"Test reuseaddr option"}].
-
-reuseaddr(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server =
- ssl_test_lib:start_server([{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([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {ssl_test_lib, no_result, []}},
- {options, [{active, false} | ClientOpts]}]),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client),
-
- Server1 =
- ssl_test_lib:start_server([{node, ServerNode}, {port, Port},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result, []}},
- {options, [{active, false} | ServerOpts]}]),
- Client1 =
- ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result, []}},
- {options, [{active, false} | ClientOpts]}]),
-
- ssl_test_lib:check_result(Server1, ok, Client1, ok),
- ssl_test_lib:close(Server1),
- ssl_test_lib:close(Client1).
-
-%%--------------------------------------------------------------------
-tls_tcp_reuseaddr() ->
- [{doc, "Reference test case."}].
-tls_tcp_reuseaddr(Config) when is_list(Config) ->
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server =
- ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {transport, gen_tcp},
- {mfa, {ssl_test_lib, no_result, []}},
- {options, [{active, false}, {reuseaddr, true}]}]),
- Port = ssl_test_lib:inet_port(Server),
- Client =
- ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {transport, gen_tcp},
- {from, self()},
- {mfa, {ssl_test_lib, no_result, []}},
- {options, [{active, false}]}]),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client),
-
- Server1 =
- ssl_test_lib:start_server([{node, ServerNode}, {port, Port},
- {from, self()},
- {transport, gen_tcp},
- {mfa, {?MODULE, tcp_send_recv_result, []}},
- {options, [{active, false}, {reuseaddr, true}]}]),
- Client1 =
- ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {transport, gen_tcp},
- {mfa, {?MODULE, tcp_send_recv_result, []}},
- {options, [{active, false}]}]),
-
- ssl_test_lib:check_result(Server1, ok, Client1, ok),
- ssl_test_lib:close(Server1),
- ssl_test_lib:close(Client1).
-
-%%--------------------------------------------------------------------
-
-honor_server_cipher_order() ->
- [{doc,"Test API honor server cipher order."}].
-honor_server_cipher_order(Config) when is_list(Config) ->
- ClientCiphers = [#{key_exchange => dhe_rsa,
- cipher => aes_128_cbc,
- mac => sha,
- prf => default_prf},
- #{key_exchange => dhe_rsa,
- cipher => aes_256_cbc,
- mac => sha,
- prf => default_prf}],
- ServerCiphers = [#{key_exchange => dhe_rsa,
- cipher => aes_256_cbc,
- mac =>sha,
- prf => default_prf},
- #{key_exchange => dhe_rsa,
- cipher => aes_128_cbc,
- mac => sha,
- prf => default_prf}],
- honor_cipher_order(Config, true, ServerCiphers, ClientCiphers, #{key_exchange => dhe_rsa,
- cipher => aes_256_cbc,
- mac => sha,
- prf => default_prf}).
-
-honor_client_cipher_order() ->
- [{doc,"Test API honor server cipher order."}].
-honor_client_cipher_order(Config) when is_list(Config) ->
- ClientCiphers = [#{key_exchange => dhe_rsa,
- cipher => aes_128_cbc,
- mac => sha,
- prf => default_prf},
- #{key_exchange => dhe_rsa,
- cipher => aes_256_cbc,
- mac => sha,
- prf => default_prf}],
- ServerCiphers = [#{key_exchange => dhe_rsa,
- cipher => aes_256_cbc,
- mac =>sha,
- prf => default_prf},
- #{key_exchange => dhe_rsa,
- cipher => aes_128_cbc,
- mac => sha,
- prf => default_prf}],
-honor_cipher_order(Config, false, ServerCiphers, ClientCiphers, #{key_exchange => dhe_rsa,
- cipher => aes_128_cbc,
- mac => sha,
- prf => default_prf}).
-
-honor_cipher_order(Config, Honor, ServerCiphers, ClientCiphers, Expected) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, connection_info_result, []}},
- {options, [{ciphers, ServerCiphers}, {honor_cipher_order, Honor}
- | ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, connection_info_result, []}},
- {options, [{ciphers, ClientCiphers}, {honor_cipher_order, Honor}
- | ClientOpts]}]),
-
- Version = ssl_test_lib:protocol_version(Config),
-
- ServerMsg = ClientMsg = {ok, {Version, Expected}},
-
- ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-%%--------------------------------------------------------------------
-tls_ciphersuite_vs_version() ->
- [{doc,"Test a SSLv3 client cannot negotiate a TLSv* cipher suite."}].
-tls_ciphersuite_vs_version(Config) when is_list(Config) ->
-
- {_ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
-
- Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
- {from, self()},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
-
- {ok, Socket} = gen_tcp:connect(Hostname, Port, [binary, {active, false}]),
- ok = gen_tcp:send(Socket,
- <<22, 3,0, 49:16, % handshake, SSL 3.0, length
- 1, 45:24, % client_hello, length
- 3,0, % SSL 3.0
- 16#deadbeef:256, % 32 'random' bytes = 256 bits
- 0, % no session ID
- %% three cipher suites -- null, one with sha256 hash and one with sha hash
- 6:16, 0,255, 0,61, 0,57,
- 1, 0 % no compression
- >>),
- {ok, <<22, RecMajor:8, RecMinor:8, _RecLen:16, 2, HelloLen:24>>} = gen_tcp:recv(Socket, 9, 10000),
- {ok, <<HelloBin:HelloLen/binary>>} = gen_tcp:recv(Socket, HelloLen, 5000),
- ServerHello = tls_handshake:decode_handshake({RecMajor, RecMinor}, 2, HelloBin),
- case ServerHello of
- #server_hello{server_version = {3,0}, cipher_suite = <<0,57>>} ->
- ok;
- _ ->
- ct:fail({unexpected_server_hello, ServerHello})
- end.
-
-%%--------------------------------------------------------------------
-conf_signature_algs() ->
- [{doc,"Test to set the signature_algs option on both client and server"}].
-conf_signature_algs(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server =
- ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result, []}},
- {options, [{active, false}, {signature_algs, [{sha, rsa}]} | 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, []}},
- {options, [{active, false}, {signature_algs, [{sha, rsa}]} | ClientOpts]}]),
-
- ct:log("Testcase ~p, Client ~p Server ~p ~n",
- [self(), Client, Server]),
-
- ssl_test_lib:check_result(Server, ok, Client, ok),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-
-%%--------------------------------------------------------------------
-no_common_signature_algs() ->
- [{doc,"Set the signature_algs option so that there client and server does not share any hash sign algorithms"}].
-no_common_signature_algs(Config) when is_list(Config) ->
-
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
-
- Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
- {from, self()},
- {options, [{signature_algs, [{sha256, rsa}]}
- | ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {options, [{signature_algs, [{sha384, rsa}]}
- | ClientOpts]}]),
-
- ssl_test_lib:check_server_alert(Server, Client, insufficient_security).
-
-%%--------------------------------------------------------------------
-
-tls_dont_crash_on_handshake_garbage() ->
- [{doc, "Ensure SSL server worker thows an alert on garbage during handshake "
- "instead of crashing and exposing state to user code"}].
-
-tls_dont_crash_on_handshake_garbage(Config) ->
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
-
- {_ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, ServerOpts}]),
- unlink(Server), monitor(process, Server),
- Port = ssl_test_lib:inet_port(Server),
-
- {ok, Socket} = gen_tcp:connect(Hostname, Port, [binary, {active, false}]),
-
- % Send hello and garbage record
- ok = gen_tcp:send(Socket,
- [<<22, 3,3, 49:16, 1, 45:24, 3,3, % client_hello
- 16#deadbeef:256, % 32 'random' bytes = 256 bits
- 0, 6:16, 0,255, 0,61, 0,57, 1, 0 >>, % some hello values
-
- <<22, 3,3, 5:16, 92,64,37,228,209>> % garbage
- ]),
- % Send unexpected change_cipher_spec
- ok = gen_tcp:send(Socket, <<20, 3,3, 12:16, 111,40,244,7,137,224,16,109,197,110,249,152>>),
-
- % Ensure we receive an alert, not sudden disconnect
- {ok, <<21, _/binary>>} = drop_handshakes(Socket, 1000).
-
-drop_handshakes(Socket, Timeout) ->
- {ok, <<RecType:8, _RecMajor:8, _RecMinor:8, RecLen:16>> = Header} = gen_tcp:recv(Socket, 5, Timeout),
- {ok, <<Frag:RecLen/binary>>} = gen_tcp:recv(Socket, RecLen, Timeout),
- case RecType of
- 22 -> drop_handshakes(Socket, Timeout);
- _ -> {ok, <<Header/binary, Frag/binary>>}
- end.
-
-
-%%--------------------------------------------------------------------
-
-hibernate() ->
- [{doc,"Check that an SSL connection that is started with option "
- "{hibernate_after, 1000} indeed hibernates after 1000ms of "
- "inactivity"}].
-
-hibernate(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
- {Client, #sslsocket{pid=[Pid|_]}} = ssl_test_lib:start_client([return_socket,
- {node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, [{hibernate_after, 1000}|ClientOpts]}]),
- {current_function, _} =
- process_info(Pid, current_function),
-
- ssl_test_lib:check_result(Server, ok, Client, ok),
-
- ct:sleep(1500),
- {current_function, {erlang, hibernate, 3}} =
- process_info(Pid, current_function),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-%%--------------------------------------------------------------------
-
-hibernate_right_away() ->
- [{doc,"Check that an SSL connection that is configured to hibernate "
- "after 0 or 1 milliseconds hibernates as soon as possible and not "
- "crashes"}].
-
-hibernate_right_away(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- StartServerOpts = [{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, ServerOpts}],
- StartClientOpts = [return_socket,
- {node, ClientNode},
- {host, Hostname},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}}],
-
- Server1 = ssl_test_lib:start_server(StartServerOpts),
- Port1 = ssl_test_lib:inet_port(Server1),
- {Client1, #sslsocket{pid = [Pid1|_]}} = ssl_test_lib:start_client(StartClientOpts ++
- [{port, Port1}, {options, [{hibernate_after, 0}|ClientOpts]}]),
-
- ssl_test_lib:check_result(Server1, ok, Client1, ok),
-
- ct:sleep(1000), %% Schedule out
-
- {current_function, {erlang, hibernate, 3}} =
- process_info(Pid1, current_function),
- ssl_test_lib:close(Server1),
- ssl_test_lib:close(Client1),
-
- Server2 = ssl_test_lib:start_server(StartServerOpts),
- Port2 = ssl_test_lib:inet_port(Server2),
- {Client2, #sslsocket{pid = [Pid2|_]}} = ssl_test_lib:start_client(StartClientOpts ++
- [{port, Port2}, {options, [{hibernate_after, 1}|ClientOpts]}]),
-
- ssl_test_lib:check_result(Server2, ok, Client2, ok),
-
- ct:sleep(1000), %% Schedule out
-
- {current_function, {erlang, hibernate, 3}} =
- process_info(Pid2, current_function),
-
- ssl_test_lib:close(Server2),
- ssl_test_lib:close(Client2).
-
-%%--------------------------------------------------------------------
-listen_socket() ->
- [{doc,"Check error handling and inet compliance when calling API functions with listen sockets."}].
-
-listen_socket(Config) ->
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- {ok, ListenSocket} = ssl:listen(0, ServerOpts),
-
- %% This can be a valid thing to do as
- %% options are inherited by the accept socket
- ok = ssl:controlling_process(ListenSocket, self()),
-
- {ok, _} = ssl:sockname(ListenSocket),
-
- {error, enotconn} = ssl:send(ListenSocket, <<"data">>),
- {error, enotconn} = ssl:recv(ListenSocket, 0),
- {error, enotconn} = ssl:connection_information(ListenSocket),
- {error, enotconn} = ssl:peername(ListenSocket),
- {error, enotconn} = ssl:peercert(ListenSocket),
- {error, enotconn} = ssl:renegotiate(ListenSocket),
- {error, enotconn} = ssl:prf(ListenSocket, 'master_secret', <<"Label">>, [client_random], 256),
- {error, enotconn} = ssl:shutdown(ListenSocket, read_write),
-
- ok = ssl:close(ListenSocket).
-%%--------------------------------------------------------------------
-tls_ssl_accept_timeout() ->
- [{doc,"Test ssl:ssl_accept timeout"}].
-
-tls_ssl_accept_timeout(Config) ->
- process_flag(trap_exit, true),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {timeout, 5000},
- {mfa, {ssl_test_lib,
- no_result_msg, []}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
- {ok, CSocket} = gen_tcp:connect(Hostname, Port, [binary, {active, true}]),
-
- receive
- {tcp_closed, CSocket} ->
- ssl_test_lib:check_result(Server, {error, timeout}),
- receive
- {'EXIT', Server, _} ->
- %% Make sure supervisor had time to react on process exit
- %% Could we come up with a better solution to this?
- ct:sleep(500),
- [] = supervisor:which_children(tls_connection_sup)
- end
- end.
-
-%%--------------------------------------------------------------------
-ssl_recv_timeout() ->
- [{doc,"Test ssl:ssl_accept timeout"}].
-
-ssl_recv_timeout(Config) ->
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server =
- ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, send_recv_result_timeout_server, []}},
- {options, [{active, false} | ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
-
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE,
- send_recv_result_timeout_client, []}},
- {options, [{active, false} | ClientOpts]}]),
-
- ssl_test_lib:check_result(Client, ok, Server, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-%%--------------------------------------------------------------------
-connect_twice() ->
- [{doc,""}].
-connect_twice(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server =
- ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result, []}},
- {options, [{keepalive, true},{active, false}
- | 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, []}},
- {options, [{keepalive, true},{active, false}
- | ClientOpts]}]),
- Server ! listen,
-
- {Client1, #sslsocket{}} =
- ssl_test_lib:start_client([return_socket,
- {node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result, []}},
- {options, [{keepalive, true},{active, false}
- | ClientOpts]}]),
-
- ct:log("Testcase ~p, Client ~p Server ~p ~n",
- [self(), Client, Server]),
-
- ssl_test_lib:check_result(Server, ok, Client, ok),
- ssl_test_lib:check_result(Server, ok, Client1, ok),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client),
- ssl_test_lib:close(Client1).
-
-%%--------------------------------------------------------------------
-renegotiate_dos_mitigate_active() ->
- [{doc, "Mitigate DOS computational attack by not allowing client to renegotiate many times in a row",
- "immediately after each other"}].
-renegotiate_dos_mitigate_active(Config) when is_list(Config) ->
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server =
- ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
-
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE,
- renegotiate_immediately, []}},
- {options, ClientOpts}]),
-
- ssl_test_lib:check_result(Client, ok, Server, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-%%--------------------------------------------------------------------
-renegotiate_dos_mitigate_passive() ->
- [{doc, "Mitigate DOS computational attack by not allowing client to renegotiate many times in a row",
- "immediately after each other"}].
-renegotiate_dos_mitigate_passive(Config) when is_list(Config) ->
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server =
- ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result, []}},
- {options, [{active, false} | ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
-
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
{from, self()},
- {mfa, {?MODULE,
- renegotiate_immediately, []}},
- {options, ClientOpts}]),
-
- ssl_test_lib:check_result(Client, ok, Server, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-%%--------------------------------------------------------------------
-renegotiate_dos_mitigate_absolute() ->
- [{doc, "Mitigate DOS computational attack by not allowing client to initiate renegotiation"}].
-renegotiate_dos_mitigate_absolute(Config) when is_list(Config) ->
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server =
- ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, [{client_renegotiation, false} | ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
-
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE,
- renegotiate_rejected,
- []}},
+ {mfa, {?MODULE, connect_dist_c, []}},
{options, ClientOpts}]),
-
- ssl_test_lib:check_result(Client, ok, Server, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-%%--------------------------------------------------------------------
-tls_tcp_error_propagation_in_active_mode() ->
- [{doc,"Test that process recives {ssl_error, Socket, closed} when tcp error ocurres"}].
-tls_tcp_error_propagation_in_active_mode(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, no_result, []}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
- {Client, #sslsocket{pid=[Pid|_]} = SslSocket} = ssl_test_lib:start_client([return_socket,
- {node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, receive_msg, []}},
- {options, ClientOpts}]),
-
- {status, _, _, StatusInfo} = sys:get_status(Pid),
- [_, _,_, _, Prop] = StatusInfo,
- State = ssl_test_lib:state(Prop),
- StaticEnv = element(2, State),
- Socket = element(11, StaticEnv),
- %% Fake tcp error
- Pid ! {tcp_error, Socket, etimedout},
-
- ssl_test_lib:check_result(Client, {ssl_closed, SslSocket}).
-
-%%--------------------------------------------------------------------
-recv_error_handling() ->
- [{doc,"Special case of call error handling"}].
-recv_error_handling(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, recv_close, []}},
- {options, [{active, false} | ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
- {_Client, #sslsocket{} = SslSocket} = ssl_test_lib:start_client([return_socket,
- {node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {ssl_test_lib, no_result, []}},
- {options, ClientOpts}]),
- ssl:close(SslSocket),
- ssl_test_lib:check_result(Server, ok).
-
-
-
-%%--------------------------------------------------------------------
-call_in_error_state() ->
- [{doc,"Special case of call error handling"}].
-call_in_error_state(Config) when is_list(Config) ->
- ServerOpts0 = ssl_test_lib:ssl_options(server_opts, Config),
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = [{cacertfile, "foo.pem"} | proplists:delete(cacertfile, ServerOpts0)],
- Pid = spawn_link(?MODULE, run_error_server, [[self() | ServerOpts]]),
- receive
- {Pid, Port} ->
- spawn_link(?MODULE, run_client_error, [[Port, ClientOpts]])
- end,
- receive
- {error, closed} ->
- ok;
- Other ->
- ct:fail(Other)
- end.
-
-run_client_error([Port, Opts]) ->
- ssl:connect("localhost", Port, Opts).
-run_error_server([ Pid | Opts]) ->
- {ok, Listen} = ssl:listen(0, Opts),
- {ok,{_, Port}} = ssl:sockname(Listen),
- Pid ! {self(), Port},
- {ok, Socket} = ssl:transport_accept(Listen),
- Pid ! ssl:controlling_process(Socket, self()).
-
-%%--------------------------------------------------------------------
-
-close_in_error_state() ->
- [{doc,"Special case of closing socket in error state"}].
-close_in_error_state(Config) when is_list(Config) ->
- ServerOpts0 = ssl_test_lib:ssl_options(server_opts, Config),
- ServerOpts = [{cacertfile, "foo.pem"} | proplists:delete(cacertfile, ServerOpts0)],
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- _ = spawn_link(?MODULE, run_error_server_close, [[self() | ServerOpts]]),
- receive
- {_Pid, Port} ->
- spawn_link(?MODULE, run_client_error, [[Port, ClientOpts]])
- end,
- receive
- ok ->
- ok;
- Other ->
- ct:fail(Other)
- end.
-%%--------------------------------------------------------------------
-abuse_transport_accept_socket() ->
- [{doc,"Only ssl:handshake and ssl:controlling_process is allowed for transport_accept:sockets"}].
-abuse_transport_accept_socket(Config) when is_list(Config) ->
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ ssl_test_lib:check_result(Server, ok, Client, ok),
- Server = ssl_test_lib:start_server_transport_abuse_socket([{node, ServerNode},
- {port, 0},
- {from, self()},
- {options, 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, no_result, []}},
- {options, ClientOpts}]),
- ssl_test_lib:check_result(Server, ok),
ssl_test_lib:close(Server),
ssl_test_lib:close(Client).
-
-
-%%--------------------------------------------------------------------
-controlling_process_transport_accept_socket() ->
- [{doc,"Only ssl:handshake and ssl:controlling_process is allowed for transport_accept:sockets"}].
-controlling_process_transport_accept_socket(Config) when is_list(Config) ->
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server_transport_control([{node, ServerNode},
- {port, 0},
- {from, self()},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
-
- _Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {options, ClientOpts}]),
- ssl_test_lib:check_result(Server, ok),
- ssl_test_lib:close(Server).
-
-%%--------------------------------------------------------------------
-run_error_server_close([Pid | Opts]) ->
- {ok, Listen} = ssl:listen(0, Opts),
- {ok,{_, Port}} = ssl:sockname(Listen),
- Pid ! {self(), Port},
- {ok, Socket} = ssl:transport_accept(Listen),
- Pid ! ssl:close(Socket).
-
-%%--------------------------------------------------------------------
-
-rizzo() ->
- [{doc, "Test that there is a 1/n-1-split for non RC4 in 'TLS < 1.1' as it is
- vunrable to Rizzo/Dungon attack"}].
-
-rizzo(Config) when is_list(Config) ->
- Prop = proplists:get_value(tc_group_properties, Config),
- Version = proplists:get_value(name, Prop),
- NVersion = ssl_test_lib:protocol_version(Config, tuple),
- Ciphers = ssl:filter_cipher_suites(ssl:cipher_suites(all, NVersion),
- [{key_exchange,
- fun(Alg) when Alg == ecdh_rsa; Alg == ecdhe_rsa->
- true;
- (_) ->
- false
- end},
- {cipher,
- fun(rc4_128) ->
- false;
- (chacha20_poly1305) ->
- false;
- (_) ->
- true
- end}]),
-
- run_send_recv_rizzo(Ciphers, Config, Version,
- {?MODULE, send_recv_result_active_rizzo, []}).
-%%--------------------------------------------------------------------
-no_rizzo_rc4() ->
- [{doc,"Test that there is no 1/n-1-split for RC4 as it is not vunrable to Rizzo/Dungon attack"}].
-
-no_rizzo_rc4(Config) when is_list(Config) ->
- Prop = proplists:get_value(tc_group_properties, Config),
- Version = proplists:get_value(name, Prop),
- NVersion = ssl_test_lib:protocol_version(Config, tuple),
- %% Test uses RSA certs
- Ciphers = ssl:filter_cipher_suites(ssl_test_lib:rc4_suites(NVersion),
- [{key_exchange,
- fun(Alg) when Alg == ecdh_rsa; Alg == ecdhe_rsa->
- true;
- (_) ->
- false
- end}]),
- run_send_recv_rizzo(Ciphers, Config, Version,
- {?MODULE, send_recv_result_active_no_rizzo, []}).
-
-rizzo_one_n_minus_one() ->
- [{doc,"Test that the 1/n-1-split mitigation of Rizzo/Dungon attack can be explicitly selected"}].
-
-rizzo_one_n_minus_one(Config) when is_list(Config) ->
- Prop = proplists:get_value(tc_group_properties, Config),
- Version = proplists:get_value(name, Prop),
- NVersion = ssl_test_lib:protocol_version(Config, tuple),
- Ciphers = ssl:filter_cipher_suites(ssl:cipher_suites(all, NVersion),
- [{key_exchange,
- fun(Alg) when Alg == ecdh_rsa; Alg == ecdhe_rsa->
- true;
- (_) ->
- false
- end},
- {cipher,
- fun(rc4_128) ->
- false;
- %% TODO: remove this clause when chacha is fixed!
- (chacha20_poly1305) ->
- false;
- (_) ->
- true
- end}]),
- run_send_recv_rizzo(Ciphers, Config, Version,
- {?MODULE, send_recv_result_active_rizzo, []}).
-
-rizzo_zero_n() ->
- [{doc,"Test that the 0/n-split mitigation of Rizzo/Dungon attack can be explicitly selected"}].
-
-rizzo_zero_n(Config) when is_list(Config) ->
- Prop = proplists:get_value(tc_group_properties, Config),
- Version = proplists:get_value(name, Prop),
- NVersion = ssl_test_lib:protocol_version(Config, tuple),
- Ciphers = ssl:filter_cipher_suites(ssl:cipher_suites(default, NVersion),
- [{cipher,
- fun(rc4_128) ->
- false;
- (_) ->
- true
- end}]),
- run_send_recv_rizzo(Ciphers, Config, Version,
- {?MODULE, send_recv_result_active_no_rizzo, []}).
-
-rizzo_disabled() ->
- [{doc,"Test that the mitigation of Rizzo/Dungon attack can be explicitly disabled"}].
-
-rizzo_disabled(Config) when is_list(Config) ->
- Prop = proplists:get_value(tc_group_properties, Config),
- Version = proplists:get_value(name, Prop),
- NVersion = ssl_test_lib:protocol_version(Config, tuple),
- Ciphers = ssl:filter_cipher_suites(ssl:cipher_suites(default, NVersion),
- [{cipher,
- fun(rc4_128) ->
- false;
- (_) ->
- true
- end}]),
- run_send_recv_rizzo(Ciphers, Config, Version,
- {?MODULE, send_recv_result_active_no_rizzo, []}).
-
-%%--------------------------------------------------------------------
-new_server_wants_peer_cert() ->
- [{doc, "Test that server configured to do client certification does"
- " not reuse session without a client certificate."}].
-new_server_wants_peer_cert(Config) when is_list(Config) ->
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
- VServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true}
- | ssl_test_lib:ssl_options(server_verification_opts, Config)],
- ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server =
- ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, peercert_result, []}},
- {options, [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, no_result, []}},
- {options, ClientOpts}]),
-
- Monitor = erlang:monitor(process, Server),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client),
- receive
- {'DOWN', Monitor, _, _, _} ->
- ok
- end,
-
- Server1 = ssl_test_lib:start_server([{node, ServerNode}, {port, Port},
- {from, self()},
- {mfa, {?MODULE, peercert_result, []}},
- {options, VServerOpts}]),
- Client1 =
- ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {ssl_test_lib, no_result, []}},
- {options, [ClientOpts]}]),
-
- CertFile = proplists:get_value(certfile, ClientOpts),
- [{'Certificate', BinCert, _}]= ssl_test_lib:pem_to_der(CertFile),
-
- ServerMsg = {error, no_peercert},
- Sever1Msg = {ok, BinCert},
-
- ssl_test_lib:check_result(Server, ServerMsg, Server1, Sever1Msg),
-
- ssl_test_lib:close(Server1),
- ssl_test_lib:close(Client),
- ssl_test_lib:close(Client1).
-
-%%--------------------------------------------------------------------
-session_cache_process_list() ->
- [{doc,"Test reuse of sessions (short handshake)"}].
-session_cache_process_list(Config) when is_list(Config) ->
- session_cache_process(list,Config).
-%%--------------------------------------------------------------------
-session_cache_process_mnesia() ->
- [{doc,"Test reuse of sessions (short handshake)"}].
-session_cache_process_mnesia(Config) when is_list(Config) ->
- session_cache_process(mnesia,Config).
+eccs() ->
+ [{doc, "Test API functions eccs/0 and eccs/1"}].
-%%--------------------------------------------------------------------
+eccs(Config) when is_list(Config) ->
+ [_|_] = All = ssl:eccs(),
+ [] = ssl:eccs(sslv3),
+ [_|_] = Tls = ssl:eccs(tlsv1),
+ [_|_] = Tls1 = ssl:eccs('tlsv1.1'),
+ [_|_] = Tls2 = ssl:eccs('tlsv1.2'),
+ [_|_] = Tls1 = ssl:eccs('dtlsv1'),
+ [_|_] = Tls2 = ssl:eccs('dtlsv1.2'),
+ %% ordering is currently not verified by the test
+ true = lists:sort(All) =:= lists:usort(Tls ++ Tls1 ++ Tls2),
+ ok.
tls_versions_option() ->
[{doc,"Test API versions option to connect/listen."}].
@@ -4323,2140 +452,6 @@ tls_versions_option(Config) when is_list(Config) ->
%%--------------------------------------------------------------------
-unordered_protocol_versions_server() ->
- [{doc,"Test that the highest protocol is selected even"
- " when it is not first in the versions list."}].
-
-unordered_protocol_versions_server(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, protocol_info_result, []}},
- {options, [{versions, ['tlsv1.1', 'tlsv1.2']} | ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
-
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, protocol_info_result, []}},
- {options, ClientOpts}]),
-
- ServerMsg = ClientMsg = {ok,'tlsv1.2'},
- ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg).
-
-%%--------------------------------------------------------------------
-unordered_protocol_versions_client() ->
- [{doc,"Test that the highest protocol is selected even"
- " when it is not first in the versions list."}].
-
-unordered_protocol_versions_client(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, protocol_info_result, []}},
- {options, ServerOpts }]),
- Port = ssl_test_lib:inet_port(Server),
-
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, protocol_info_result, []}},
- {options, [{versions, ['tlsv1.1', 'tlsv1.2']} | ClientOpts]}]),
-
- ServerMsg = ClientMsg = {ok, 'tlsv1.2'},
- ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg).
-
-%%--------------------------------------------------------------------
-max_handshake_size() ->
- [{doc,"Test that we can set max_handshake_size to max value."}].
-
-max_handshake_size(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, [{max_handshake_size, 8388607} |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, [{max_handshake_size, 8388607} | ClientOpts]}]),
-
- ssl_test_lib:check_result(Server, ok, Client, ok).
-
-%%--------------------------------------------------------------------
-
-server_name_indication_option() ->
- [{doc,"Test API server_name_indication option to connect."}].
-server_name_indication_option(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
-
- Client0 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options,
- [{server_name_indication, disable} |
- ClientOpts]}
- ]),
-
- ssl_test_lib:check_result(Server, ok, Client0, ok),
- Server ! listen,
-
- Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options,
- [{server_name_indication, Hostname} | ClientOpts]
- }]),
- ssl_test_lib:check_result(Server, ok, Client1, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client0),
- ssl_test_lib:close(Client1).
-%%--------------------------------------------------------------------
-
-accept_pool() ->
- [{doc,"Test having an accept pool."}].
-accept_pool(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server0 = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {accepters, 3},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server0),
- [Server1, Server2] = ssl_test_lib:accepters(2),
-
- Client0 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, ClientOpts}
- ]),
-
- Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, ClientOpts}
- ]),
-
- Client2 = 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_ok([Server0, Server1, Server2, Client0, Client1, Client2]),
-
- ssl_test_lib:close(Server0),
- ssl_test_lib:close(Server1),
- ssl_test_lib:close(Server2),
- ssl_test_lib:close(Client0),
- ssl_test_lib:close(Client1),
- ssl_test_lib:close(Client2).
-
-%%--------------------------------------------------------------------
-%% TLS 1.3
-%%--------------------------------------------------------------------
-
-tls13_enable_client_side() ->
- [{doc,"Test that a TLS 1.3 client can connect to a TLS 1.2 server."}].
-
-tls13_enable_client_side(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, protocol_info_result, []}},
- {options, [{versions,
- ['tlsv1.1', 'tlsv1.2']} | ServerOpts] }]),
- Port = ssl_test_lib:inet_port(Server),
-
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, protocol_info_result, []}},
- {options, [{versions,
- ['tlsv1.2', 'tlsv1.3']} | ClientOpts]}]),
-
- ServerMsg = ClientMsg = {ok, 'tlsv1.2'},
- ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg).
-
-tls13_enable_server_side() ->
- [{doc,"Test that a TLS 1.2 client can connect to a TLS 1.3 server."}].
-
-tls13_enable_server_side(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, protocol_info_result, []}},
- {options, [{versions,
- ['tlsv1.2', 'tlsv1.3']} | ServerOpts] }]),
- Port = ssl_test_lib:inet_port(Server),
-
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, protocol_info_result, []}},
- {options, [{versions,
- ['tlsv1.2', 'tlsv1.1']} | ClientOpts]}]),
-
- ServerMsg = ClientMsg = {ok, 'tlsv1.2'},
- ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg).
-
-tls_record_1_3_encode_decode() ->
- [{doc,"Test TLS 1.3 record encode/decode functions"}].
-
-tls_record_1_3_encode_decode(_Config) ->
- ConnectionStates =
- #{current_read =>
- #{beast_mitigation => one_n_minus_one,
- cipher_state =>
- {cipher_state,
- <<14,172,111,243,199,170,242,203,126,205,34,93,122,115,226,14,
- 15,117,155,48,24,112,61,15,113,208,127,51,179,227,194,232>>,
- <<197,54,168,218,54,91,157,58,30,201,197,142,51,58,53,231,228,
- 131,57,122,170,78,82,196,30,48,23,16,95,255,185,236>>,
- undefined,undefined,undefined,16},
- client_verify_data => undefined,compression_state => undefined,
- mac_secret => undefined,secure_renegotiation => undefined,
- security_parameters =>
- {security_parameters,
- <<19,2>>,
- 0,8,2,undefined,undefined,undefined,undefined,undefined,
- sha384,undefined,undefined,
- {handshake_secret,
- <<128,229,186,211,62,127,182,20,62,166,233,23,135,64,121,
- 3,104,251,214,161,253,31,3,2,232,37,8,221,189,72,64,218,
- 121,41,112,148,254,34,68,164,228,60,161,201,132,55,56,
- 157>>},
- undefined,
- <<92,24,205,75,244,60,136,212,250,32,214,20,37,3,213,87,61,207,
- 147,61,168,145,177,118,160,153,33,53,48,108,191,174>>,
- undefined},
- sequence_number => 0,server_verify_data => undefined},
- current_write =>
- #{beast_mitigation => one_n_minus_one,
- cipher_state =>
- {cipher_state,
- <<14,172,111,243,199,170,242,203,126,205,34,93,122,115,226,14,
- 15,117,155,48,24,112,61,15,113,208,127,51,179,227,194,232>>,
- <<197,54,168,218,54,91,157,58,30,201,197,142,51,58,53,231,228,
- 131,57,122,170,78,82,196,30,48,23,16,95,255,185,236>>,
- undefined,undefined,undefined,16},
- client_verify_data => undefined,compression_state => undefined,
- mac_secret => undefined,secure_renegotiation => undefined,
- security_parameters =>
- {security_parameters,
- <<19,2>>,
- 0,8,2,undefined,undefined,undefined,undefined,undefined,
- sha384,undefined,undefined,
- {handshake_secret,
- <<128,229,186,211,62,127,182,20,62,166,233,23,135,64,121,
- 3,104,251,214,161,253,31,3,2,232,37,8,221,189,72,64,218,
- 121,41,112,148,254,34,68,164,228,60,161,201,132,55,56,
- 157>>},
- undefined,
- <<92,24,205,75,244,60,136,212,250,32,214,20,37,3,213,87,61,207,
- 147,61,168,145,177,118,160,153,33,53,48,108,191,174>>,
- undefined},
- sequence_number => 0,server_verify_data => undefined}},
-
- PlainText = [11,
- <<0,2,175>>,
- <<0,0,2,171,0,2,166,48,130,2,162,48,130,1,138,2,9,0,186,57,220,137,88,255,
- 191,235,48,13,6,9,42,134,72,134,247,13,1,1,11,5,0,48,18,49,16,48,14,6,3,85,
- 4,3,12,7,84,101,115,116,32,67,65,48,30,23,13,49,56,48,53,48,52,49,52,49,50,
- 51,56,90,23,13,50,56,48,50,48,52,49,52,49,50,51,56,90,48,20,49,18,48,16,6,
- 3,85,4,3,12,9,108,111,99,97,108,104,111,115,116,48,130,1,34,48,13,6,9,42,
- 134,72,134,247,13,1,1,1,5,0,3,130,1,15,0,48,130,1,10,2,130,1,1,0,169,40,
- 144,176,121,63,134,97,144,126,243,183,225,157,37,131,183,225,87,243,23,88,
- 230,70,9,134,32,147,7,27,167,98,51,81,224,75,199,12,229,251,195,207,75,179,
- 181,78,128,3,255,44,58,39,43,172,142,45,186,58,51,65,187,199,154,153,245,
- 70,133,137,1,27,87,42,116,65,251,129,109,145,233,97,171,71,54,213,185,74,
- 209,166,11,218,189,119,206,86,170,60,212,213,85,189,30,50,215,23,185,53,
- 132,238,132,176,198,250,139,251,198,221,225,128,109,113,23,220,39,143,71,
- 30,59,189,51,244,61,158,214,146,180,196,103,169,189,221,136,78,129,216,148,
- 2,9,8,65,37,224,215,233,13,209,21,235,20,143,33,74,59,53,208,90,152,94,251,
- 54,114,171,39,88,230,227,158,211,135,37,182,67,205,161,59,20,138,58,253,15,
- 53,48,8,157,9,95,197,9,177,116,21,54,9,125,78,109,182,83,20,16,234,223,116,
- 41,155,123,87,77,17,120,153,246,239,124,130,105,219,166,146,242,151,66,198,
- 75,72,63,28,246,86,16,244,223,22,36,50,15,247,222,98,6,152,136,154,72,150,
- 73,127,2,3,1,0,1,48,13,6,9,42,134,72,134,247,13,1,1,11,5,0,3,130,1,1,0,76,
- 33,54,160,229,219,219,193,150,116,245,252,18,39,235,145,86,12,167,171,52,
- 117,166,30,83,5,216,245,177,217,247,95,1,136,94,246,212,108,248,230,111,
- 225,202,189,6,129,8,70,128,245,18,204,215,87,82,129,253,227,122,66,182,184,
- 189,30,193,169,144,218,216,109,105,110,215,144,60,104,162,178,101,164,218,
- 122,60,37,41,143,57,150,52,59,51,112,238,113,239,168,114,69,183,143,154,73,
- 61,58,80,247,172,95,251,55,28,186,28,200,206,230,118,243,92,202,189,49,76,
- 124,252,76,0,247,112,85,194,69,59,222,163,228,103,49,110,104,109,251,155,
- 138,9,37,167,49,189,48,134,52,158,185,129,24,96,153,196,251,90,206,76,239,
- 175,119,174,165,133,108,222,125,237,125,187,149,152,83,190,16,202,94,202,
- 201,40,218,22,254,63,189,41,174,97,140,203,70,18,196,118,237,175,134,79,78,
- 246,2,61,54,77,186,112,32,17,193,192,188,217,252,215,200,7,245,180,179,132,
- 183,212,229,155,15,152,206,135,56,81,88,3,123,244,149,110,182,72,109,70,62,
- 146,152,146,151,107,126,216,210,9,93,0,0>>],
-
- {[_Header|Encoded], _} = tls_record_1_3:encode_plain_text(22, PlainText, ConnectionStates),
- CipherText = #ssl_tls{type = 23, version = {3,3}, fragment = Encoded},
-
- {#ssl_tls{type = 22, version = {3,4}, fragment = DecodedText}, _} =
- tls_record_1_3:decode_cipher_text(CipherText, ConnectionStates),
-
- DecodedText = iolist_to_binary(PlainText),
- ct:log("Decoded: ~p ~n", [DecodedText]),
- ok.
-
-tls13_1_RTT_handshake() ->
- [{doc,"Test TLS 1.3 1-RTT Handshake"}].
-
-tls13_1_RTT_handshake(_Config) ->
- %% ConnectionStates with NULL cipher
- ConnStatesNull =
- #{current_write =>
- #{security_parameters =>
- #security_parameters{cipher_suite = ?TLS_NULL_WITH_NULL_NULL},
- sequence_number => 0
- }
- },
-
- %% {client} construct a ClientHello handshake message:
- %%
- %% ClientHello (196 octets): 01 00 00 c0 03 03 cb 34 ec b1 e7 81 63
- %% ba 1c 38 c6 da cb 19 6a 6d ff a2 1a 8d 99 12 ec 18 a2 ef 62 83
- %% 02 4d ec e7 00 00 06 13 01 13 03 13 02 01 00 00 91 00 00 00 0b
- %% 00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 00 00 0a 00 14 00
- %% 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 01 03 01 04 00 23
- %% 00 00 00 33 00 26 00 24 00 1d 00 20 99 38 1d e5 60 e4 bd 43 d2
- %% 3d 8e 43 5a 7d ba fe b3 c0 6e 51 c1 3c ae 4d 54 13 69 1e 52 9a
- %% af 2c 00 2b 00 03 02 03 04 00 0d 00 20 00 1e 04 03 05 03 06 03
- %% 02 03 08 04 08 05 08 06 04 01 05 01 06 01 02 01 04 02 05 02 06
- %% 02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01
- %%
- %% {client} send handshake record:
- %%
- %% payload (196 octets): 01 00 00 c0 03 03 cb 34 ec b1 e7 81 63 ba
- %% 1c 38 c6 da cb 19 6a 6d ff a2 1a 8d 99 12 ec 18 a2 ef 62 83 02
- %% 4d ec e7 00 00 06 13 01 13 03 13 02 01 00 00 91 00 00 00 0b 00
- %% 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 00 00 0a 00 14 00 12
- %% 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 01 03 01 04 00 23 00
- %% 00 00 33 00 26 00 24 00 1d 00 20 99 38 1d e5 60 e4 bd 43 d2 3d
- %% 8e 43 5a 7d ba fe b3 c0 6e 51 c1 3c ae 4d 54 13 69 1e 52 9a af
- %% 2c 00 2b 00 03 02 03 04 00 0d 00 20 00 1e 04 03 05 03 06 03 02
- %% 03 08 04 08 05 08 06 04 01 05 01 06 01 02 01 04 02 05 02 06 02
- %% 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01
- %%
- %% complete record (201 octets): 16 03 01 00 c4 01 00 00 c0 03 03 cb
- %% 34 ec b1 e7 81 63 ba 1c 38 c6 da cb 19 6a 6d ff a2 1a 8d 99 12
- %% ec 18 a2 ef 62 83 02 4d ec e7 00 00 06 13 01 13 03 13 02 01 00
- %% 00 91 00 00 00 0b 00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01
- %% 00 00 0a 00 14 00 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02
- %% 01 03 01 04 00 23 00 00 00 33 00 26 00 24 00 1d 00 20 99 38 1d
- %% e5 60 e4 bd 43 d2 3d 8e 43 5a 7d ba fe b3 c0 6e 51 c1 3c ae 4d
- %% 54 13 69 1e 52 9a af 2c 00 2b 00 03 02 03 04 00 0d 00 20 00 1e
- %% 04 03 05 03 06 03 02 03 08 04 08 05 08 06 04 01 05 01 06 01 02
- %% 01 04 02 05 02 06 02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01
- ClientHello =
- hexstr2bin("01 00 00 c0 03 03 cb 34 ec b1 e7 81 63
- ba 1c 38 c6 da cb 19 6a 6d ff a2 1a 8d 99 12 ec 18 a2 ef 62 83
- 02 4d ec e7 00 00 06 13 01 13 03 13 02 01 00 00 91 00 00 00 0b
- 00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 00 00 0a 00 14 00
- 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 01 03 01 04 00 23
- 00 00 00 33 00 26 00 24 00 1d 00 20 99 38 1d e5 60 e4 bd 43 d2
- 3d 8e 43 5a 7d ba fe b3 c0 6e 51 c1 3c ae 4d 54 13 69 1e 52 9a
- af 2c 00 2b 00 03 02 03 04 00 0d 00 20 00 1e 04 03 05 03 06 03
- 02 03 08 04 08 05 08 06 04 01 05 01 06 01 02 01 04 02 05 02 06
- 02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01"),
-
- ClientHelloRecord =
- %% Current implementation always sets
- %% legacy_record_version to Ox0303
- hexstr2bin("16 03 03 00 c4 01 00 00 c0 03 03 cb
- 34 ec b1 e7 81 63 ba 1c 38 c6 da cb 19 6a 6d ff a2 1a 8d 99 12
- ec 18 a2 ef 62 83 02 4d ec e7 00 00 06 13 01 13 03 13 02 01 00
- 00 91 00 00 00 0b 00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01
- 00 00 0a 00 14 00 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02
- 01 03 01 04 00 23 00 00 00 33 00 26 00 24 00 1d 00 20 99 38 1d
- e5 60 e4 bd 43 d2 3d 8e 43 5a 7d ba fe b3 c0 6e 51 c1 3c ae 4d
- 54 13 69 1e 52 9a af 2c 00 2b 00 03 02 03 04 00 0d 00 20 00 1e
- 04 03 05 03 06 03 02 03 08 04 08 05 08 06 04 01 05 01 06 01 02
- 01 04 02 05 02 06 02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01"),
-
- {CHEncrypted, _} =
- tls_record:encode_handshake(ClientHello, {3,4}, ConnStatesNull),
- ClientHelloRecord = iolist_to_binary(CHEncrypted),
-
- %% {server} extract secret "early":
- %%
- %% salt: 0 (all zero octets)
- %%
- %% IKM (32 octets): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- %% 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- %%
- %% secret (32 octets): 33 ad 0a 1c 60 7e c0 3b 09 e6 cd 98 93 68 0c
- %% e2 10 ad f3 00 aa 1f 26 60 e1 b2 2e 10 f1 70 f9 2a
- HKDFAlgo = sha256,
- Salt = binary:copy(<<?BYTE(0)>>, 32),
- IKM = binary:copy(<<?BYTE(0)>>, 32),
- EarlySecret =
- hexstr2bin("33 ad 0a 1c 60 7e c0 3b 09 e6 cd 98 93 68 0c
- e2 10 ad f3 00 aa 1f 26 60 e1 b2 2e 10 f1 70 f9 2a"),
-
- {early_secret, EarlySecret} = tls_v1:key_schedule(early_secret, HKDFAlgo, {psk, Salt}),
-
- %% {client} create an ephemeral x25519 key pair:
- %%
- %% private key (32 octets): 49 af 42 ba 7f 79 94 85 2d 71 3e f2 78
- %% 4b cb ca a7 91 1d e2 6a dc 56 42 cb 63 45 40 e7 ea 50 05
- %%
- %% public key (32 octets): 99 38 1d e5 60 e4 bd 43 d2 3d 8e 43 5a 7d
- %% ba fe b3 c0 6e 51 c1 3c ae 4d 54 13 69 1e 52 9a af 2c
- CPublicKey =
- hexstr2bin("99 38 1d e5 60 e4 bd 43 d2 3d 8e 43 5a 7d
- ba fe b3 c0 6e 51 c1 3c ae 4d 54 13 69 1e 52 9a af 2c"),
-
- %% {server} create an ephemeral x25519 key pair:
- %%
- %% private key (32 octets): b1 58 0e ea df 6d d5 89 b8 ef 4f 2d 56
- %% 52 57 8c c8 10 e9 98 01 91 ec 8d 05 83 08 ce a2 16 a2 1e
- %%
- %% public key (32 octets): c9 82 88 76 11 20 95 fe 66 76 2b db f7 c6
- %% 72 e1 56 d6 cc 25 3b 83 3d f1 dd 69 b1 b0 4e 75 1f 0f
- SPrivateKey =
- hexstr2bin("b1 58 0e ea df 6d d5 89 b8 ef 4f 2d 56
- 52 57 8c c8 10 e9 98 01 91 ec 8d 05 83 08 ce a2 16 a2 1e"),
-
- SPublicKey =
- hexstr2bin("c9 82 88 76 11 20 95 fe 66 76 2b db f7 c6
- 72 e1 56 d6 cc 25 3b 83 3d f1 dd 69 b1 b0 4e 75 1f 0f"),
-
- %% {server} construct a ServerHello handshake message:
- %%
- %% ServerHello (90 octets): 02 00 00 56 03 03 a6 af 06 a4 12 18 60
- %% dc 5e 6e 60 24 9c d3 4c 95 93 0c 8a c5 cb 14 34 da c1 55 77 2e
- %% d3 e2 69 28 00 13 01 00 00 2e 00 33 00 24 00 1d 00 20 c9 82 88
- %% 76 11 20 95 fe 66 76 2b db f7 c6 72 e1 56 d6 cc 25 3b 83 3d f1
- %% dd 69 b1 b0 4e 75 1f 0f 00 2b 00 02 03 04
- ServerHello =
- hexstr2bin("02 00 00 56 03 03 a6 af 06 a4 12 18 60
- dc 5e 6e 60 24 9c d3 4c 95 93 0c 8a c5 cb 14 34 da c1 55 77 2e
- d3 e2 69 28 00 13 01 00 00 2e 00 33 00 24 00 1d 00 20 c9 82 88
- 76 11 20 95 fe 66 76 2b db f7 c6 72 e1 56 d6 cc 25 3b 83 3d f1
- dd 69 b1 b0 4e 75 1f 0f 00 2b 00 02 03 04"),
-
- %% {server} derive secret for handshake "tls13 derived":
- %%
- %% PRK (32 octets): 33 ad 0a 1c 60 7e c0 3b 09 e6 cd 98 93 68 0c e2
- %% 10 ad f3 00 aa 1f 26 60 e1 b2 2e 10 f1 70 f9 2a
- %%
- %% hash (32 octets): e3 b0 c4 42 98 fc 1c 14 9a fb f4 c8 99 6f b9 24
- %% 27 ae 41 e4 64 9b 93 4c a4 95 99 1b 78 52 b8 55
- %%
- %% info (49 octets): 00 20 0d 74 6c 73 31 33 20 64 65 72 69 76 65 64
- %% 20 e3 b0 c4 42 98 fc 1c 14 9a fb f4 c8 99 6f b9 24 27 ae 41 e4
- %% 64 9b 93 4c a4 95 99 1b 78 52 b8 55
- %%
- %% expanded (32 octets): 6f 26 15 a1 08 c7 02 c5 67 8f 54 fc 9d ba
- %% b6 97 16 c0 76 18 9c 48 25 0c eb ea c3 57 6c 36 11 ba
- Hash =
- hexstr2bin("e3 b0 c4 42 98 fc 1c 14 9a fb f4 c8 99 6f b9 24
- 27 ae 41 e4 64 9b 93 4c a4 95 99 1b 78 52 b8 55"),
-
- Hash = crypto:hash(HKDFAlgo, <<>>),
-
- Info =
- hexstr2bin("00 20 0d 74 6c 73 31 33 20 64 65 72 69 76 65 64
- 20 e3 b0 c4 42 98 fc 1c 14 9a fb f4 c8 99 6f b9 24 27 ae 41 e4
- 64 9b 93 4c a4 95 99 1b 78 52 b8 55"),
-
- Info = tls_v1:create_info(<<"derived">>, Hash, ssl_cipher:hash_size(HKDFAlgo)),
-
- Expanded =
- hexstr2bin("6f 26 15 a1 08 c7 02 c5 67 8f 54 fc 9d ba
- b6 97 16 c0 76 18 9c 48 25 0c eb ea c3 57 6c 36 11 ba"),
-
- Expanded = tls_v1:derive_secret(EarlySecret, <<"derived">>, <<>>, HKDFAlgo),
-
- %% {server} extract secret "handshake":
- %%
- %% salt (32 octets): 6f 26 15 a1 08 c7 02 c5 67 8f 54 fc 9d ba b6 97
- %% 16 c0 76 18 9c 48 25 0c eb ea c3 57 6c 36 11 ba
- %%
- %% IKM (32 octets): 8b d4 05 4f b5 5b 9d 63 fd fb ac f9 f0 4b 9f 0d
- %% 35 e6 d6 3f 53 75 63 ef d4 62 72 90 0f 89 49 2d
- %%
- %% secret (32 octets): 1d c8 26 e9 36 06 aa 6f dc 0a ad c1 2f 74 1b
- %% 01 04 6a a6 b9 9f 69 1e d2 21 a9 f0 ca 04 3f be ac
-
- %% salt = Expanded
- HandshakeIKM =
- hexstr2bin("8b d4 05 4f b5 5b 9d 63 fd fb ac f9 f0 4b 9f 0d
- 35 e6 d6 3f 53 75 63 ef d4 62 72 90 0f 89 49 2d"),
-
- HandshakeSecret =
- hexstr2bin("1d c8 26 e9 36 06 aa 6f dc 0a ad c1 2f 74 1b
- 01 04 6a a6 b9 9f 69 1e d2 21 a9 f0 ca 04 3f be ac"),
-
- HandshakeIKM = crypto:compute_key(ecdh, CPublicKey, SPrivateKey, x25519),
-
- {handshake_secret, HandshakeSecret} =
- tls_v1:key_schedule(handshake_secret, HKDFAlgo, HandshakeIKM,
- {early_secret, EarlySecret}),
-
- %% {server} derive secret "tls13 c hs traffic":
- %%
- %% PRK (32 octets): 1d c8 26 e9 36 06 aa 6f dc 0a ad c1 2f 74 1b 01
- %% 04 6a a6 b9 9f 69 1e d2 21 a9 f0 ca 04 3f be ac
- %%
- %% hash (32 octets): 86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58 ed
- %% d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8
- %%
- %% info (54 octets): 00 20 12 74 6c 73 31 33 20 63 20 68 73 20 74 72
- %% 61 66 66 69 63 20 86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58
- %% ed d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8
- %%
- %% expanded (32 octets): b3 ed db 12 6e 06 7f 35 a7 80 b3 ab f4 5e
- %% 2d 8f 3b 1a 95 07 38 f5 2e 96 00 74 6a 0e 27 a5 5a 21
-
- %% PRK = HandshakeSecret
- CHSTHash =
- hexstr2bin("86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58 ed
- d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8"),
-
- CHSTInfo =
- hexstr2bin("00 20 12 74 6c 73 31 33 20 63 20 68 73 20 74 72
- 61 66 66 69 63 20 86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58
- ed d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8"),
-
- CHSTrafficSecret =
- hexstr2bin(" b3 ed db 12 6e 06 7f 35 a7 80 b3 ab f4 5e
- 2d 8f 3b 1a 95 07 38 f5 2e 96 00 74 6a 0e 27 a5 5a 21"),
-
- CHSH = <<ClientHello/binary,ServerHello/binary>>,
- CHSTHash = crypto:hash(HKDFAlgo, CHSH),
- CHSTInfo = tls_v1:create_info(<<"c hs traffic">>, CHSTHash, ssl_cipher:hash_size(HKDFAlgo)),
-
- CHSTrafficSecret =
- tls_v1:client_handshake_traffic_secret(HKDFAlgo, {handshake_secret, HandshakeSecret}, CHSH),
-
- %% {server} derive secret "tls13 s hs traffic":
- %%
- %% PRK (32 octets): 1d c8 26 e9 36 06 aa 6f dc 0a ad c1 2f 74 1b 01
- %% 04 6a a6 b9 9f 69 1e d2 21 a9 f0 ca 04 3f be ac
- %%
- %% hash (32 octets): 86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58 ed
- %% d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8
- %%
- %% info (54 octets): 00 20 12 74 6c 73 31 33 20 73 20 68 73 20 74 72
- %% 61 66 66 69 63 20 86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58
- %% ed d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8
- %%
- %% expanded (32 octets): b6 7b 7d 69 0c c1 6c 4e 75 e5 42 13 cb 2d
- %% 37 b4 e9 c9 12 bc de d9 10 5d 42 be fd 59 d3 91 ad 38
-
- %% PRK = HandshakeSecret
- %% hash = CHSTHash
- SHSTInfo =
- hexstr2bin("00 20 12 74 6c 73 31 33 20 73 20 68 73 20 74 72
- 61 66 66 69 63 20 86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58
- ed d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8"),
-
- SHSTrafficSecret =
- hexstr2bin("b6 7b 7d 69 0c c1 6c 4e 75 e5 42 13 cb 2d
- 37 b4 e9 c9 12 bc de d9 10 5d 42 be fd 59 d3 91 ad 38"),
-
- SHSTInfo = tls_v1:create_info(<<"s hs traffic">>, CHSTHash, ssl_cipher:hash_size(HKDFAlgo)),
-
- SHSTrafficSecret =
- tls_v1:server_handshake_traffic_secret(HKDFAlgo, {handshake_secret, HandshakeSecret}, CHSH),
-
-
- %% {server} derive secret for master "tls13 derived":
- %%
- %% PRK (32 octets): 1d c8 26 e9 36 06 aa 6f dc 0a ad c1 2f 74 1b 01
- %% 04 6a a6 b9 9f 69 1e d2 21 a9 f0 ca 04 3f be ac
- %%
- %% hash (32 octets): e3 b0 c4 42 98 fc 1c 14 9a fb f4 c8 99 6f b9 24
- %% 27 ae 41 e4 64 9b 93 4c a4 95 99 1b 78 52 b8 55
- %%
- %% info (49 octets): 00 20 0d 74 6c 73 31 33 20 64 65 72 69 76 65 64
- %% 20 e3 b0 c4 42 98 fc 1c 14 9a fb f4 c8 99 6f b9 24 27 ae 41 e4
- %% 64 9b 93 4c a4 95 99 1b 78 52 b8 55
- %%
- %% expanded (32 octets): 43 de 77 e0 c7 77 13 85 9a 94 4d b9 db 25
- %% 90 b5 31 90 a6 5b 3e e2 e4 f1 2d d7 a0 bb 7c e2 54 b4
-
- %% PRK = HandshakeSecret
- %% hash = Hash
- %% info = Info
- MasterDeriveSecret =
- hexstr2bin("43 de 77 e0 c7 77 13 85 9a 94 4d b9 db 25
- 90 b5 31 90 a6 5b 3e e2 e4 f1 2d d7 a0 bb 7c e2 54 b4"),
-
- MasterDeriveSecret = tls_v1:derive_secret(HandshakeSecret, <<"derived">>, <<>>, HKDFAlgo),
-
- %% {server} extract secret "master":
- %%
- %% salt (32 octets): 43 de 77 e0 c7 77 13 85 9a 94 4d b9 db 25 90 b5
- %% 31 90 a6 5b 3e e2 e4 f1 2d d7 a0 bb 7c e2 54 b4
- %%
- %% IKM (32 octets): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- %% 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
- %%
- %% secret (32 octets): 18 df 06 84 3d 13 a0 8b f2 a4 49 84 4c 5f 8a
- %% 47 80 01 bc 4d 4c 62 79 84 d5 a4 1d a8 d0 40 29 19
-
- %% salt = MasterDeriveSecret
- %% IKM = IKM
- MasterSecret =
- hexstr2bin("18 df 06 84 3d 13 a0 8b f2 a4 49 84 4c 5f 8a
- 47 80 01 bc 4d 4c 62 79 84 d5 a4 1d a8 d0 40 29 19"),
-
- {master_secret, MasterSecret} =
- tls_v1:key_schedule(master_secret, HKDFAlgo, {handshake_secret, HandshakeSecret}),
-
- %% {server} send handshake record:
- %%
- %% payload (90 octets): 02 00 00 56 03 03 a6 af 06 a4 12 18 60 dc 5e
- %% 6e 60 24 9c d3 4c 95 93 0c 8a c5 cb 14 34 da c1 55 77 2e d3 e2
- %% 69 28 00 13 01 00 00 2e 00 33 00 24 00 1d 00 20 c9 82 88 76 11
- %% 20 95 fe 66 76 2b db f7 c6 72 e1 56 d6 cc 25 3b 83 3d f1 dd 69
- %% b1 b0 4e 75 1f 0f 00 2b 00 02 03 04
- %%
- %% complete record (95 octets): 16 03 03 00 5a 02 00 00 56 03 03 a6
- %% af 06 a4 12 18 60 dc 5e 6e 60 24 9c d3 4c 95 93 0c 8a c5 cb 14
- %% 34 da c1 55 77 2e d3 e2 69 28 00 13 01 00 00 2e 00 33 00 24 00
- %% 1d 00 20 c9 82 88 76 11 20 95 fe 66 76 2b db f7 c6 72 e1 56 d6
- %% cc 25 3b 83 3d f1 dd 69 b1 b0 4e 75 1f 0f 00 2b 00 02 03 04
-
- %% payload = ServerHello
- ServerHelloRecord =
- hexstr2bin("16 03 03 00 5a 02 00 00 56 03 03 a6
- af 06 a4 12 18 60 dc 5e 6e 60 24 9c d3 4c 95 93 0c 8a c5 cb 14
- 34 da c1 55 77 2e d3 e2 69 28 00 13 01 00 00 2e 00 33 00 24 00
- 1d 00 20 c9 82 88 76 11 20 95 fe 66 76 2b db f7 c6 72 e1 56 d6
- cc 25 3b 83 3d f1 dd 69 b1 b0 4e 75 1f 0f 00 2b 00 02 03 04"),
-
- {SHEncrypted, _} =
- tls_record:encode_handshake(ServerHello, {3,4}, ConnStatesNull),
- ServerHelloRecord = iolist_to_binary(SHEncrypted),
-
- %% {server} derive write traffic keys for handshake data:
- %%
- %% PRK (32 octets): b6 7b 7d 69 0c c1 6c 4e 75 e5 42 13 cb 2d 37 b4
- %% e9 c9 12 bc de d9 10 5d 42 be fd 59 d3 91 ad 38
- %%
- %% key info (13 octets): 00 10 09 74 6c 73 31 33 20 6b 65 79 00
- %%
- %% key expanded (16 octets): 3f ce 51 60 09 c2 17 27 d0 f2 e4 e8 6e
- %% e4 03 bc
- %%
- %% iv info (12 octets): 00 0c 08 74 6c 73 31 33 20 69 76 00
- %%
- %% iv expanded (12 octets): 5d 31 3e b2 67 12 76 ee 13 00 0b 30
-
- %% PRK = SHSTrafficSecret
- WriteKeyInfo =
- hexstr2bin("00 10 09 74 6c 73 31 33 20 6b 65 79 00"),
-
- WriteKey =
- hexstr2bin("3f ce 51 60 09 c2 17 27 d0 f2 e4 e8 6e e4 03 bc"),
-
- WriteIVInfo =
- hexstr2bin("00 0c 08 74 6c 73 31 33 20 69 76 00"),
-
- WriteIV =
- hexstr2bin(" 5d 31 3e b2 67 12 76 ee 13 00 0b 30"),
-
- Cipher = aes_128_gcm, %% TODO: get from ServerHello
-
- WriteKeyInfo = tls_v1:create_info(<<"key">>, <<>>, ssl_cipher:key_material(Cipher)),
- %% TODO: remove hardcoded IV size
- WriteIVInfo = tls_v1:create_info(<<"iv">>, <<>>, 12),
-
- {WriteKey, WriteIV} = tls_v1:calculate_traffic_keys(HKDFAlgo, Cipher, SHSTrafficSecret),
-
- %% {server} construct an EncryptedExtensions handshake message:
- %%
- %% EncryptedExtensions (40 octets): 08 00 00 24 00 22 00 0a 00 14 00
- %% 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 01 03 01 04 00 1c
- %% 00 02 40 01 00 00 00 00
- %%
- %% {server} construct a Certificate handshake message:
- %%
- %% Certificate (445 octets): 0b 00 01 b9 00 00 01 b5 00 01 b0 30 82
- %% 01 ac 30 82 01 15 a0 03 02 01 02 02 01 02 30 0d 06 09 2a 86 48
- %% 86 f7 0d 01 01 0b 05 00 30 0e 31 0c 30 0a 06 03 55 04 03 13 03
- %% 72 73 61 30 1e 17 0d 31 36 30 37 33 30 30 31 32 33 35 39 5a 17
- %% 0d 32 36 30 37 33 30 30 31 32 33 35 39 5a 30 0e 31 0c 30 0a 06
- %% 03 55 04 03 13 03 72 73 61 30 81 9f 30 0d 06 09 2a 86 48 86 f7
- %% 0d 01 01 01 05 00 03 81 8d 00 30 81 89 02 81 81 00 b4 bb 49 8f
- %% 82 79 30 3d 98 08 36 39 9b 36 c6 98 8c 0c 68 de 55 e1 bd b8 26
- %% d3 90 1a 24 61 ea fd 2d e4 9a 91 d0 15 ab bc 9a 95 13 7a ce 6c
- %% 1a f1 9e aa 6a f9 8c 7c ed 43 12 09 98 e1 87 a8 0e e0 cc b0 52
- %% 4b 1b 01 8c 3e 0b 63 26 4d 44 9a 6d 38 e2 2a 5f da 43 08 46 74
- %% 80 30 53 0e f0 46 1c 8c a9 d9 ef bf ae 8e a6 d1 d0 3e 2b d1 93
- %% ef f0 ab 9a 80 02 c4 74 28 a6 d3 5a 8d 88 d7 9f 7f 1e 3f 02 03
- %% 01 00 01 a3 1a 30 18 30 09 06 03 55 1d 13 04 02 30 00 30 0b 06
- %% 03 55 1d 0f 04 04 03 02 05 a0 30 0d 06 09 2a 86 48 86 f7 0d 01
- %% 01 0b 05 00 03 81 81 00 85 aa d2 a0 e5 b9 27 6b 90 8c 65 f7 3a
- %% 72 67 17 06 18 a5 4c 5f 8a 7b 33 7d 2d f7 a5 94 36 54 17 f2 ea
- %% e8 f8 a5 8c 8f 81 72 f9 31 9c f3 6b 7f d6 c5 5b 80 f2 1a 03 01
- %% 51 56 72 60 96 fd 33 5e 5e 67 f2 db f1 02 70 2e 60 8c ca e6 be
- %% c1 fc 63 a4 2a 99 be 5c 3e b7 10 7c 3c 54 e9 b9 eb 2b d5 20 3b
- %% 1c 3b 84 e0 a8 b2 f7 59 40 9b a3 ea c9 d9 1d 40 2d cc 0c c8 f8
- %% 96 12 29 ac 91 87 b4 2b 4d e1 00 00
- %%
- %% {server} construct a CertificateVerify handshake message:
- %%
- %% CertificateVerify (136 octets): 0f 00 00 84 08 04 00 80 5a 74 7c
- %% 5d 88 fa 9b d2 e5 5a b0 85 a6 10 15 b7 21 1f 82 4c d4 84 14 5a
- %% b3 ff 52 f1 fd a8 47 7b 0b 7a bc 90 db 78 e2 d3 3a 5c 14 1a 07
- %% 86 53 fa 6b ef 78 0c 5e a2 48 ee aa a7 85 c4 f3 94 ca b6 d3 0b
- %% be 8d 48 59 ee 51 1f 60 29 57 b1 54 11 ac 02 76 71 45 9e 46 44
- %% 5c 9e a5 8c 18 1e 81 8e 95 b8 c3 fb 0b f3 27 84 09 d3 be 15 2a
- %% 3d a5 04 3e 06 3d da 65 cd f5 ae a2 0d 53 df ac d4 2f 74 f3
- EncryptedExtensions =
- hexstr2bin("08 00 00 24 00 22 00 0a 00 14 00
- 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 01 03 01 04 00 1c
- 00 02 40 01 00 00 00 00"),
-
- Certificate =
- hexstr2bin("0b 00 01 b9 00 00 01 b5 00 01 b0 30 82
- 01 ac 30 82 01 15 a0 03 02 01 02 02 01 02 30 0d 06 09 2a 86 48
- 86 f7 0d 01 01 0b 05 00 30 0e 31 0c 30 0a 06 03 55 04 03 13 03
- 72 73 61 30 1e 17 0d 31 36 30 37 33 30 30 31 32 33 35 39 5a 17
- 0d 32 36 30 37 33 30 30 31 32 33 35 39 5a 30 0e 31 0c 30 0a 06
- 03 55 04 03 13 03 72 73 61 30 81 9f 30 0d 06 09 2a 86 48 86 f7
- 0d 01 01 01 05 00 03 81 8d 00 30 81 89 02 81 81 00 b4 bb 49 8f
- 82 79 30 3d 98 08 36 39 9b 36 c6 98 8c 0c 68 de 55 e1 bd b8 26
- d3 90 1a 24 61 ea fd 2d e4 9a 91 d0 15 ab bc 9a 95 13 7a ce 6c
- 1a f1 9e aa 6a f9 8c 7c ed 43 12 09 98 e1 87 a8 0e e0 cc b0 52
- 4b 1b 01 8c 3e 0b 63 26 4d 44 9a 6d 38 e2 2a 5f da 43 08 46 74
- 80 30 53 0e f0 46 1c 8c a9 d9 ef bf ae 8e a6 d1 d0 3e 2b d1 93
- ef f0 ab 9a 80 02 c4 74 28 a6 d3 5a 8d 88 d7 9f 7f 1e 3f 02 03
- 01 00 01 a3 1a 30 18 30 09 06 03 55 1d 13 04 02 30 00 30 0b 06
- 03 55 1d 0f 04 04 03 02 05 a0 30 0d 06 09 2a 86 48 86 f7 0d 01
- 01 0b 05 00 03 81 81 00 85 aa d2 a0 e5 b9 27 6b 90 8c 65 f7 3a
- 72 67 17 06 18 a5 4c 5f 8a 7b 33 7d 2d f7 a5 94 36 54 17 f2 ea
- e8 f8 a5 8c 8f 81 72 f9 31 9c f3 6b 7f d6 c5 5b 80 f2 1a 03 01
- 51 56 72 60 96 fd 33 5e 5e 67 f2 db f1 02 70 2e 60 8c ca e6 be
- c1 fc 63 a4 2a 99 be 5c 3e b7 10 7c 3c 54 e9 b9 eb 2b d5 20 3b
- 1c 3b 84 e0 a8 b2 f7 59 40 9b a3 ea c9 d9 1d 40 2d cc 0c c8 f8
- 96 12 29 ac 91 87 b4 2b 4d e1 00 00"),
-
- CertificateVerify =
- hexstr2bin("0f 00 00 84 08 04 00 80 5a 74 7c
- 5d 88 fa 9b d2 e5 5a b0 85 a6 10 15 b7 21 1f 82 4c d4 84 14 5a
- b3 ff 52 f1 fd a8 47 7b 0b 7a bc 90 db 78 e2 d3 3a 5c 14 1a 07
- 86 53 fa 6b ef 78 0c 5e a2 48 ee aa a7 85 c4 f3 94 ca b6 d3 0b
- be 8d 48 59 ee 51 1f 60 29 57 b1 54 11 ac 02 76 71 45 9e 46 44
- 5c 9e a5 8c 18 1e 81 8e 95 b8 c3 fb 0b f3 27 84 09 d3 be 15 2a
- 3d a5 04 3e 06 3d da 65 cd f5 ae a2 0d 53 df ac d4 2f 74 f3"),
-
- %% {server} calculate finished "tls13 finished":
- %%
- %% PRK (32 octets): b6 7b 7d 69 0c c1 6c 4e 75 e5 42 13 cb 2d 37 b4
- %% e9 c9 12 bc de d9 10 5d 42 be fd 59 d3 91 ad 38
- %%
- %% hash (0 octets): (empty)
- %%
- %% info (18 octets): 00 20 0e 74 6c 73 31 33 20 66 69 6e 69 73 68 65
- %% 64 00
- %%
- %% expanded (32 octets): 00 8d 3b 66 f8 16 ea 55 9f 96 b5 37 e8 85
- %% c3 1f c0 68 bf 49 2c 65 2f 01 f2 88 a1 d8 cd c1 9f c8
- %%
- %% finished (32 octets): 9b 9b 14 1d 90 63 37 fb d2 cb dc e7 1d f4
- %% de da 4a b4 2c 30 95 72 cb 7f ff ee 54 54 b7 8f 07 18
-
- %% PRK = SHSTrafficSecret
- FInfo =
- hexstr2bin("00 20 0e 74 6c 73 31 33 20 66 69 6e 69 73 68 65
- 64 00"),
-
- FExpanded =
- hexstr2bin("00 8d 3b 66 f8 16 ea 55 9f 96 b5 37 e8 85
- c3 1f c0 68 bf 49 2c 65 2f 01 f2 88 a1 d8 cd c1 9f c8"),
-
- FinishedVerifyData =
- hexstr2bin("9b 9b 14 1d 90 63 37 fb d2 cb dc e7 1d f4
- de da 4a b4 2c 30 95 72 cb 7f ff ee 54 54 b7 8f 07 18"),
-
- FInfo = tls_v1:create_info(<<"finished">>, <<>>, ssl_cipher:hash_size(HKDFAlgo)),
-
- FExpanded = tls_v1:finished_key(SHSTrafficSecret, HKDFAlgo),
-
- MessageHistory0 = [CertificateVerify,
- Certificate,
- EncryptedExtensions,
- ServerHello,
- ClientHello],
-
- FinishedVerifyData = tls_v1:finished_verify_data(FExpanded, HKDFAlgo, MessageHistory0),
-
- %% {server} construct a Finished handshake message:
- %%
- %% Finished (36 octets): 14 00 00 20 9b 9b 14 1d 90 63 37 fb d2 cb
- %% dc e7 1d f4 de da 4a b4 2c 30 95 72 cb 7f ff ee 54 54 b7 8f 07
- %% 18
- FinishedHSBin =
- hexstr2bin("14 00 00 20 9b 9b 14 1d 90 63 37 fb d2 cb
- dc e7 1d f4 de da 4a b4 2c 30 95 72 cb 7f ff ee 54 54 b7 8f 07
- 18"),
-
- FinishedHS = #finished{verify_data = FinishedVerifyData},
-
- FinishedIOList = tls_handshake:encode_handshake(FinishedHS, {3,4}),
- FinishedHSBin = iolist_to_binary(FinishedIOList),
-
- %% {server} derive secret "tls13 c ap traffic":
- %%
- %% PRK (32 octets): 18 df 06 84 3d 13 a0 8b f2 a4 49 84 4c 5f 8a 47
- %% 80 01 bc 4d 4c 62 79 84 d5 a4 1d a8 d0 40 29 19
- %%
- %% hash (32 octets): 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a
- %% 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13
- %%
- %% info (54 octets): 00 20 12 74 6c 73 31 33 20 63 20 61 70 20 74 72
- %% 61 66 66 69 63 20 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b
- %% 1a 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13
- %%
- %% expanded (32 octets): 9e 40 64 6c e7 9a 7f 9d c0 5a f8 88 9b ce
- %% 65 52 87 5a fa 0b 06 df 00 87 f7 92 eb b7 c1 75 04 a5
-
- %% PRK = MasterSecret
- CAPTHash =
- hexstr2bin("96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a
- 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13"),
- CAPTInfo =
- hexstr2bin("00 20 12 74 6c 73 31 33 20 63 20 61 70 20 74 72
- 61 66 66 69 63 20 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b
- 1a 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13"),
-
- CAPTrafficSecret =
- hexstr2bin("9e 40 64 6c e7 9a 7f 9d c0 5a f8 88 9b ce
- 65 52 87 5a fa 0b 06 df 00 87 f7 92 eb b7 c1 75 04 a5"),
-
- CHSF = <<ClientHello/binary,
- ServerHello/binary,
- EncryptedExtensions/binary,
- Certificate/binary,
- CertificateVerify/binary,
- FinishedHSBin/binary>>,
-
- CAPTHash = crypto:hash(HKDFAlgo, CHSF),
-
- CAPTInfo =
- tls_v1:create_info(<<"c ap traffic">>, CAPTHash, ssl_cipher:hash_size(HKDFAlgo)),
-
- CAPTrafficSecret =
- tls_v1:client_application_traffic_secret_0(HKDFAlgo, {master_secret, MasterSecret}, CHSF),
-
- %% {server} derive secret "tls13 s ap traffic":
- %%
- %% PRK (32 octets): 18 df 06 84 3d 13 a0 8b f2 a4 49 84 4c 5f 8a 47
- %% 80 01 bc 4d 4c 62 79 84 d5 a4 1d a8 d0 40 29 19
- %%
- %% hash (32 octets): 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a
- %% 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13
- %%
- %% info (54 octets): 00 20 12 74 6c 73 31 33 20 73 20 61 70 20 74 72
- %% 61 66 66 69 63 20 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b
- %% 1a 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13
- %%
- %% expanded (32 octets): a1 1a f9 f0 55 31 f8 56 ad 47 11 6b 45 a9
- %% 50 32 82 04 b4 f4 4b fb 6b 3a 4b 4f 1f 3f cb 63 16 43
-
- %% PRK = MasterSecret
- %% hash = CAPTHash
- SAPTInfo =
- hexstr2bin(" 00 20 12 74 6c 73 31 33 20 73 20 61 70 20 74 72
- 61 66 66 69 63 20 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b
- 1a 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13"),
-
- SAPTrafficSecret =
- hexstr2bin("a1 1a f9 f0 55 31 f8 56 ad 47 11 6b 45 a9
- 50 32 82 04 b4 f4 4b fb 6b 3a 4b 4f 1f 3f cb 63 16 43"),
-
- SAPTInfo =
- tls_v1:create_info(<<"s ap traffic">>, CAPTHash, ssl_cipher:hash_size(HKDFAlgo)),
-
- SAPTrafficSecret =
- tls_v1:server_application_traffic_secret_0(HKDFAlgo, {master_secret, MasterSecret}, CHSF),
-
- %% {server} derive secret "tls13 exp master":
- %%
- %% PRK (32 octets): 18 df 06 84 3d 13 a0 8b f2 a4 49 84 4c 5f 8a 47
- %% 80 01 bc 4d 4c 62 79 84 d5 a4 1d a8 d0 40 29 19
- %%
- %% hash (32 octets): 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a
- %% 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13
- %%
- %% info (52 octets): 00 20 10 74 6c 73 31 33 20 65 78 70 20 6d 61 73
- %% 74 65 72 20 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a 00
- %% 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13
- %%
- %% expanded (32 octets): fe 22 f8 81 17 6e da 18 eb 8f 44 52 9e 67
- %% 92 c5 0c 9a 3f 89 45 2f 68 d8 ae 31 1b 43 09 d3 cf 50
-
- %% PRK = MasterSecret
- %% hash = CAPTHash
- ExporterInfo =
- hexstr2bin("00 20 10 74 6c 73 31 33 20 65 78 70 20 6d 61 73
- 74 65 72 20 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a 00
- 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13"),
-
- ExporterMasterSecret =
- hexstr2bin("fe 22 f8 81 17 6e da 18 eb 8f 44 52 9e 67
- 92 c5 0c 9a 3f 89 45 2f 68 d8 ae 31 1b 43 09 d3 cf 50"),
-
- ExporterInfo =
- tls_v1:create_info(<<"exp master">>, CAPTHash, ssl_cipher:hash_size(HKDFAlgo)),
-
- ExporterMasterSecret =
- tls_v1:exporter_master_secret(HKDFAlgo, {master_secret, MasterSecret}, CHSF),
-
- %% {server} derive write traffic keys for application data:
- %%
- %% PRK (32 octets): a1 1a f9 f0 55 31 f8 56 ad 47 11 6b 45 a9 50 32
- %% 82 04 b4 f4 4b fb 6b 3a 4b 4f 1f 3f cb 63 16 43
- %%
- %% key info (13 octets): 00 10 09 74 6c 73 31 33 20 6b 65 79 00
- %%
- %% key expanded (16 octets): 9f 02 28 3b 6c 9c 07 ef c2 6b b9 f2 ac
- %% 92 e3 56
- %%
- %% iv info (12 octets): 00 0c 08 74 6c 73 31 33 20 69 76 00
- %%
- %% iv expanded (12 octets): cf 78 2b 88 dd 83 54 9a ad f1 e9 84
-
- %% PRK = SAPTrafficsecret
- %% key info = WriteKeyInfo
- %% iv info = WrtieIVInfo
- SWKey =
- hexstr2bin("9f 02 28 3b 6c 9c 07 ef c2 6b b9 f2 ac 92 e3 56"),
-
- SWIV =
- hexstr2bin("cf 78 2b 88 dd 83 54 9a ad f1 e9 84"),
-
- {SWKey, SWIV} = tls_v1:calculate_traffic_keys(HKDFAlgo, Cipher, SAPTrafficSecret),
-
- %% {server} derive read traffic keys for handshake data:
- %%
- %% PRK (32 octets): b3 ed db 12 6e 06 7f 35 a7 80 b3 ab f4 5e 2d 8f
- %% 3b 1a 95 07 38 f5 2e 96 00 74 6a 0e 27 a5 5a 21
- %%
- %% key info (13 octets): 00 10 09 74 6c 73 31 33 20 6b 65 79 00
- %%
- %% key expanded (16 octets): db fa a6 93 d1 76 2c 5b 66 6a f5 d9 50
- %% 25 8d 01
- %%
- %% iv info (12 octets): 00 0c 08 74 6c 73 31 33 20 69 76 00
- %%
- %% iv expanded (12 octets): 5b d3 c7 1b 83 6e 0b 76 bb 73 26 5f
-
- %% PRK = CHSTrafficsecret
- %% key info = WriteKeyInfo
- %% iv info = WrtieIVInfo
- SRKey =
- hexstr2bin("db fa a6 93 d1 76 2c 5b 66 6a f5 d9 50 25 8d 01"),
-
- SRIV =
- hexstr2bin("5b d3 c7 1b 83 6e 0b 76 bb 73 26 5f"),
-
- {SRKey, SRIV} = tls_v1:calculate_traffic_keys(HKDFAlgo, Cipher, CHSTrafficSecret).
-
-
-tls13_finished_verify_data() ->
- [{doc,"Test TLS 1.3 Finished message handling"}].
-
-tls13_finished_verify_data(_Config) ->
- ClientHello =
- hexstr2bin("01 00 00 c6 03 03 00 01 02 03 04 05 06 07 08 09
- 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19
- 1a 1b 1c 1d 1e 1f 20 e0 e1 e2 e3 e4 e5 e6 e7 e8
- e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8
- f9 fa fb fc fd fe ff 00 06 13 01 13 02 13 03 01
- 00 00 77 00 00 00 18 00 16 00 00 13 65 78 61 6d
- 70 6c 65 2e 75 6c 66 68 65 69 6d 2e 6e 65 74 00
- 0a 00 08 00 06 00 1d 00 17 00 18 00 0d 00 14 00
- 12 04 03 08 04 04 01 05 03 08 05 05 01 08 06 06
- 01 02 01 00 33 00 26 00 24 00 1d 00 20 35 80 72
- d6 36 58 80 d1 ae ea 32 9a df 91 21 38 38 51 ed
- 21 a2 8e 3b 75 e9 65 d0 d2 cd 16 62 54 00 2d 00
- 02 01 01 00 2b 00 03 02 03 04"),
-
- ServerHello =
- hexstr2bin("02 00 00 76 03 03 70 71 72 73 74 75 76 77 78 79
- 7a 7b 7c 7d 7e 7f 80 81 82 83 84 85 86 87 88 89
- 8a 8b 8c 8d 8e 8f 20 e0 e1 e2 e3 e4 e5 e6 e7 e8
- e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8
- f9 fa fb fc fd fe ff 13 01 00 00 2e 00 33 00 24
- 00 1d 00 20 9f d7 ad 6d cf f4 29 8d d3 f9 6d 5b
- 1b 2a f9 10 a0 53 5b 14 88 d7 f8 fa bb 34 9a 98
- 28 80 b6 15 00 2b 00 02 03 04"),
-
- EncryptedExtensions =
- hexstr2bin("08 00 00 02 00 00"),
-
- Certificate =
- hexstr2bin("0b 00 03 2e 00 00 03 2a 00 03 25 30 82 03 21 30
- 82 02 09 a0 03 02 01 02 02 08 15 5a 92 ad c2 04
- 8f 90 30 0d 06 09 2a 86 48 86 f7 0d 01 01 0b 05
- 00 30 22 31 0b 30 09 06 03 55 04 06 13 02 55 53
- 31 13 30 11 06 03 55 04 0a 13 0a 45 78 61 6d 70
- 6c 65 20 43 41 30 1e 17 0d 31 38 31 30 30 35 30
- 31 33 38 31 37 5a 17 0d 31 39 31 30 30 35 30 31
- 33 38 31 37 5a 30 2b 31 0b 30 09 06 03 55 04 06
- 13 02 55 53 31 1c 30 1a 06 03 55 04 03 13 13 65
- 78 61 6d 70 6c 65 2e 75 6c 66 68 65 69 6d 2e 6e
- 65 74 30 82 01 22 30 0d 06 09 2a 86 48 86 f7 0d
- 01 01 01 05 00 03 82 01 0f 00 30 82 01 0a 02 82
- 01 01 00 c4 80 36 06 ba e7 47 6b 08 94 04 ec a7
- b6 91 04 3f f7 92 bc 19 ee fb 7d 74 d7 a8 0d 00
- 1e 7b 4b 3a 4a e6 0f e8 c0 71 fc 73 e7 02 4c 0d
- bc f4 bd d1 1d 39 6b ba 70 46 4a 13 e9 4a f8 3d
- f3 e1 09 59 54 7b c9 55 fb 41 2d a3 76 52 11 e1
- f3 dc 77 6c aa 53 37 6e ca 3a ec be c3 aa b7 3b
- 31 d5 6c b6 52 9c 80 98 bc c9 e0 28 18 e2 0b f7
- f8 a0 3a fd 17 04 50 9e ce 79 bd 9f 39 f1 ea 69
- ec 47 97 2e 83 0f b5 ca 95 de 95 a1 e6 04 22 d5
- ee be 52 79 54 a1 e7 bf 8a 86 f6 46 6d 0d 9f 16
- 95 1a 4c f7 a0 46 92 59 5c 13 52 f2 54 9e 5a fb
- 4e bf d7 7a 37 95 01 44 e4 c0 26 87 4c 65 3e 40
- 7d 7d 23 07 44 01 f4 84 ff d0 8f 7a 1f a0 52 10
- d1 f4 f0 d5 ce 79 70 29 32 e2 ca be 70 1f df ad
- 6b 4b b7 11 01 f4 4b ad 66 6a 11 13 0f e2 ee 82
- 9e 4d 02 9d c9 1c dd 67 16 db b9 06 18 86 ed c1
- ba 94 21 02 03 01 00 01 a3 52 30 50 30 0e 06 03
- 55 1d 0f 01 01 ff 04 04 03 02 05 a0 30 1d 06 03
- 55 1d 25 04 16 30 14 06 08 2b 06 01 05 05 07 03
- 02 06 08 2b 06 01 05 05 07 03 01 30 1f 06 03 55
- 1d 23 04 18 30 16 80 14 89 4f de 5b cc 69 e2 52
- cf 3e a3 00 df b1 97 b8 1d e1 c1 46 30 0d 06 09
- 2a 86 48 86 f7 0d 01 01 0b 05 00 03 82 01 01 00
- 59 16 45 a6 9a 2e 37 79 e4 f6 dd 27 1a ba 1c 0b
- fd 6c d7 55 99 b5 e7 c3 6e 53 3e ff 36 59 08 43
- 24 c9 e7 a5 04 07 9d 39 e0 d4 29 87 ff e3 eb dd
- 09 c1 cf 1d 91 44 55 87 0b 57 1d d1 9b df 1d 24
- f8 bb 9a 11 fe 80 fd 59 2b a0 39 8c de 11 e2 65
- 1e 61 8c e5 98 fa 96 e5 37 2e ef 3d 24 8a fd e1
- 74 63 eb bf ab b8 e4 d1 ab 50 2a 54 ec 00 64 e9
- 2f 78 19 66 0d 3f 27 cf 20 9e 66 7f ce 5a e2 e4
- ac 99 c7 c9 38 18 f8 b2 51 07 22 df ed 97 f3 2e
- 3e 93 49 d4 c6 6c 9e a6 39 6d 74 44 62 a0 6b 42
- c6 d5 ba 68 8e ac 3a 01 7b dd fc 8e 2c fc ad 27
- cb 69 d3 cc dc a2 80 41 44 65 d3 ae 34 8c e0 f3
- 4a b2 fb 9c 61 83 71 31 2b 19 10 41 64 1c 23 7f
- 11 a5 d6 5c 84 4f 04 04 84 99 38 71 2b 95 9e d6
- 85 bc 5c 5d d6 45 ed 19 90 94 73 40 29 26 dc b4
- 0e 34 69 a1 59 41 e8 e2 cc a8 4b b6 08 46 36 a0
- 00 00"),
-
- CertificateVerify =
- hexstr2bin("0f 00 01 04 08 04 01 00 17 fe b5 33 ca 6d 00 7d
- 00 58 25 79 68 42 4b bc 3a a6 90 9e 9d 49 55 75
- 76 a5 20 e0 4a 5e f0 5f 0e 86 d2 4f f4 3f 8e b8
- 61 ee f5 95 22 8d 70 32 aa 36 0f 71 4e 66 74 13
- 92 6e f4 f8 b5 80 3b 69 e3 55 19 e3 b2 3f 43 73
- df ac 67 87 06 6d cb 47 56 b5 45 60 e0 88 6e 9b
- 96 2c 4a d2 8d ab 26 ba d1 ab c2 59 16 b0 9a f2
- 86 53 7f 68 4f 80 8a ef ee 73 04 6c b7 df 0a 84
- fb b5 96 7a ca 13 1f 4b 1c f3 89 79 94 03 a3 0c
- 02 d2 9c bd ad b7 25 12 db 9c ec 2e 5e 1d 00 e5
- 0c af cf 6f 21 09 1e bc 4f 25 3c 5e ab 01 a6 79
- ba ea be ed b9 c9 61 8f 66 00 6b 82 44 d6 62 2a
- aa 56 88 7c cf c6 6a 0f 38 51 df a1 3a 78 cf f7
- 99 1e 03 cb 2c 3a 0e d8 7d 73 67 36 2e b7 80 5b
- 00 b2 52 4f f2 98 a4 da 48 7c ac de af 8a 23 36
- c5 63 1b 3e fa 93 5b b4 11 e7 53 ca 13 b0 15 fe
- c7 e4 a7 30 f1 36 9f 9e"),
-
- BaseKey =
- hexstr2bin("a2 06 72 65 e7 f0 65 2a 92 3d 5d 72 ab 04 67 c4
- 61 32 ee b9 68 b6 a3 2d 31 1c 80 58 68 54 88 14"),
-
- VerifyData =
- hexstr2bin("ea 6e e1 76 dc cc 4a f1 85 9e 9e 4e 93 f7 97 ea
- c9 a7 8c e4 39 30 1e 35 27 5a d4 3f 3c dd bd e3"),
-
- Messages = [CertificateVerify,
- Certificate,
- EncryptedExtensions,
- ServerHello,
- ClientHello],
-
- FinishedKey = tls_v1:finished_key(BaseKey, sha256),
- VerifyData = tls_v1:finished_verify_data(FinishedKey, sha256, Messages).
-
-
-tls12_ssl_server_tls13_ssl_client() ->
- [{doc,"Test basic connection between TLS 1.2 server and TLS 1.3 client"}].
-
-tls12_ssl_server_tls13_ssl_client(Config) ->
- ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config),
- ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- %% Set versions
- ServerOpts = [{versions, ['tlsv1.2']}|ServerOpts0],
- ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']},
- {signature_algs_cert, [ecdsa_secp384r1_sha384,
- rsa_pss_rsae_sha256,
- rsa_pkcs1_sha256,
- {sha256,rsa},{sha256,dsa}]}|ClientOpts0],
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, 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_port(Client).
-
-
-tls13_basic_ssl_server_openssl_client() ->
- [{doc,"Test TLS 1.3 basic connection between ssl server and openssl s_client"}].
-
-tls13_basic_ssl_server_openssl_client(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
- ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config),
- %% Set versions
- ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}|ServerOpts0],
- {_ClientNode, ServerNode, _Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
-
- Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts),
-
- ssl_test_lib:check_result(Server, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close_port(Client).
-
-tls13_basic_ssl_server_ssl_client() ->
- [{doc,"Test TLS 1.3 basic connection between ssl server and ssl client"}].
-
-tls13_basic_ssl_server_ssl_client(Config) ->
- ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config),
- ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- %% Set versions
- ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}|ServerOpts0],
- ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}|ClientOpts0],
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, 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_port(Client).
-
-
-tls13_basic_openssl_server_ssl_client() ->
- [{doc,"Test TLS 1.3 basic connection between openssl server and ssl client"}].
-
-tls13_basic_openssl_server_ssl_client(Config) ->
- process_flag(trap_exit, true),
- ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
- ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
-
- ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}|ClientOpts0],
-
- {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
-
- Data = "From openssl to erlang",
-
- Port = ssl_test_lib:inet_port(node()),
- CertFile = proplists:get_value(certfile, ServerOpts),
- CaCertFile = proplists:get_value(cacertfile, ServerOpts),
- KeyFile = proplists:get_value(keyfile, ServerOpts),
- Exe = "openssl",
- Args = ["s_server", "-accept", integer_to_list(Port),
- "-tls1_3",
- "-cert", CertFile, "-CAfile", CaCertFile,
- "-key", KeyFile, "-Verify", "2"],
-
- OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
-
- ssl_test_lib:wait_for_openssl_server(Port, tls),
-
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE,
- erlang_ssl_receive, [Data]}},
- {options, ClientOpts}]),
- true = port_command(OpensslPort, Data),
-
- ssl_test_lib:check_result(Client, ok),
-
- %% Clean close down! Server needs to be closed first !!
- ssl_test_lib:close_port(OpensslPort),
- ssl_test_lib:close(Client),
- process_flag(trap_exit, false).
-
-
-tls13_custom_groups_ssl_server_openssl_client() ->
- [{doc,"Test that ssl server can select a common group for key-exchange"}].
-
-tls13_custom_groups_ssl_server_openssl_client(Config) ->
- ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config),
- ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config),
- %% Set versions
- ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']},
- {supported_groups, [x448, secp256r1, secp384r1]}|ServerOpts0],
- {_ClientNode, ServerNode, _Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
-
- ClientOpts = [{groups,"P-384:P-256:X25519"}|ClientOpts0],
- Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts),
-
- ssl_test_lib:check_result(Server, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close_port(Client).
-
-
-tls13_custom_groups_ssl_server_ssl_client() ->
- [{doc,"Test that ssl server can select a common group for key-exchange"}].
-
-tls13_custom_groups_ssl_server_ssl_client(Config) ->
- ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config),
- ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config),
-
- %% Set versions
- ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']},
- {supported_groups, [x448, secp256r1, secp384r1]}|ServerOpts0],
- ClientOpts1 = [{versions, ['tlsv1.2','tlsv1.3']}|ClientOpts0],
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
-
- ClientOpts = [{supported_groups,[secp384r1, secp256r1, x25519]}|ClientOpts1],
-
- 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_port(Client).
-
-
-tls13_hello_retry_request_ssl_server_openssl_client() ->
- [{doc,"Test that ssl server can request a new group when the client's first key share"
- "is not supported"}].
-
-tls13_hello_retry_request_ssl_server_openssl_client(Config) ->
- ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config),
- ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config),
- %% Set versions
- ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']},
- {supported_groups, [x448, x25519]}|ServerOpts0],
- {_ClientNode, ServerNode, _Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
-
- ClientOpts = [{groups,"P-256:X25519"}|ClientOpts0],
- Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts),
-
- ssl_test_lib:check_result(Server, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close_port(Client).
-
-
-tls13_hello_retry_request_ssl_server_ssl_client() ->
- [{doc,"Test that ssl server can request a new group when the client's first key share"
- "is not supported"}].
-
-tls13_hello_retry_request_ssl_server_ssl_client(Config) ->
- ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config),
- ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config),
- %% Set versions
- ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']},
- {supported_groups, [x448, x25519]}|ServerOpts0],
- ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']},
- {supported_groups, [secp256r1, x25519]}|ClientOpts0],
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, 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_port(Client).
-
-tls13_client_auth_empty_cert_alert_ssl_server_openssl_client() ->
- [{doc,"TLS 1.3: Test client authentication when client sends an empty certificate and fail_if_no_peer_cert is set to true."}].
-
-tls13_client_auth_empty_cert_alert_ssl_server_openssl_client(Config) ->
- ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config),
- %% Delete Client Cert and Key
- ClientOpts1 = proplists:delete(certfile, ClientOpts0),
- ClientOpts = proplists:delete(keyfile, ClientOpts1),
-
- ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config),
- %% Set versions
- ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']},
- {verify, verify_peer},
- {fail_if_no_peer_cert, true}|ServerOpts0],
- {_ClientNode, ServerNode, _Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
-
- Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts),
-
- ssl_test_lib:check_server_alert(Server, certificate_required),
- ssl_test_lib:close(Server),
- ssl_test_lib:close_port(Client).
-
-
-tls13_client_auth_empty_cert_alert_ssl_server_ssl_client() ->
- [{doc,"TLS 1.3: Test client authentication when client sends an empty certificate and fail_if_no_peer_cert is set to true."}].
-
-tls13_client_auth_empty_cert_alert_ssl_server_ssl_client(Config) ->
- ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config),
- %% Delete Client Cert and Key
- ClientOpts1 = proplists:delete(certfile, ClientOpts0),
- ClientOpts2 = proplists:delete(keyfile, ClientOpts1),
-
- ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config),
- %% Set versions
- ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']},
- {verify, verify_peer},
- {fail_if_no_peer_cert, true}|ServerOpts0],
- ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}|ClientOpts2],
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, 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_server_alert(Server, certificate_required),
- ssl_test_lib:close(Server),
- ssl_test_lib:close_port(Client).
-
-
-tls13_client_auth_empty_cert_ssl_server_openssl_client() ->
- [{doc,"TLS 1.3: Test client authentication when client sends an empty certificate and fail_if_no_peer_cert is set to false."}].
-
-tls13_client_auth_empty_cert_ssl_server_openssl_client(Config) ->
- ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config),
- %% Delete Client Cert and Key
- ClientOpts1 = proplists:delete(certfile, ClientOpts0),
- ClientOpts = proplists:delete(keyfile, ClientOpts1),
-
- ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config),
- %% Set versions
- ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']},
- {verify, verify_peer},
- {fail_if_no_peer_cert, false}|ServerOpts0],
- {_ClientNode, ServerNode, _Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
-
- Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts),
-
- ssl_test_lib:check_result(Server, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close_port(Client).
-
-
-tls13_client_auth_empty_cert_ssl_server_ssl_client() ->
- [{doc,"TLS 1.3: Test client authentication when client sends an empty certificate and fail_if_no_peer_cert is set to false."}].
-
-tls13_client_auth_empty_cert_ssl_server_ssl_client(Config) ->
- ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config),
- %% Delete Client Cert and Key
- ClientOpts1 = proplists:delete(certfile, ClientOpts0),
- ClientOpts2 = proplists:delete(keyfile, ClientOpts1),
-
- ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config),
- %% Set versions
- ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']},
- {verify, verify_peer},
- {fail_if_no_peer_cert, false}|ServerOpts0],
- ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}|ClientOpts2],
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, 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_port(Client).
-
-
-tls13_client_auth_ssl_server_openssl_client() ->
- [{doc,"TLS 1.3: Test client authentication."}].
-
-tls13_client_auth_ssl_server_openssl_client(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
- ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config),
-
- %% Set versions
- ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']},
- {verify, verify_peer},
- {fail_if_no_peer_cert, true}|ServerOpts0],
- {_ClientNode, ServerNode, _Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
-
- Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts),
-
- ssl_test_lib:check_result(Server, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close_port(Client).
-
-
-tls13_client_auth_ssl_server_ssl_client() ->
- [{doc,"TLS 1.3: Test client authentication."}].
-
-tls13_client_auth_ssl_server_ssl_client(Config) ->
- ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config),
- ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config),
-
- %% Set versions
- ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']},
- {verify, verify_peer},
- {fail_if_no_peer_cert, true}|ServerOpts0],
- ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}|ClientOpts0],
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
-
- %%Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts),
- 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_port(Client).
-
-
-tls13_hrr_client_auth_empty_cert_alert_ssl_server_openssl_client() ->
- [{doc,"TLS 1.3 (HelloRetryRequest): Test client authentication when client sends an empty certificate and fail_if_no_peer_cert is set to true."}].
-
-tls13_hrr_client_auth_empty_cert_alert_ssl_server_openssl_client(Config) ->
- ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config),
- %% Delete Client Cert and Key
- ClientOpts1 = proplists:delete(certfile, ClientOpts0),
- ClientOpts2 = proplists:delete(keyfile, ClientOpts1),
- ClientOpts = [{groups,"P-256:X25519"}|ClientOpts2],
-
- ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config),
- %% Set versions
- ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']},
- {verify, verify_peer},
- {fail_if_no_peer_cert, true},
- {supported_groups, [x448, x25519]}|ServerOpts0],
- {_ClientNode, ServerNode, _Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
-
- Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts),
-
- ssl_test_lib:check_server_alert(Server, certificate_required),
- ssl_test_lib:close(Server),
- ssl_test_lib:close_port(Client).
-
-
-tls13_hrr_client_auth_empty_cert_alert_ssl_server_ssl_client() ->
- [{doc,"TLS 1.3 (HelloRetryRequest): Test client authentication when client sends an empty certificate and fail_if_no_peer_cert is set to true."}].
-
-tls13_hrr_client_auth_empty_cert_alert_ssl_server_ssl_client(Config) ->
- ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config),
- %% Delete Client Cert and Key
- ClientOpts1 = proplists:delete(certfile, ClientOpts0),
- ClientOpts2 = proplists:delete(keyfile, ClientOpts1),
-
- ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config),
- %% Set versions
- ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']},
- {verify, verify_peer},
- {fail_if_no_peer_cert, true},
- {supported_groups, [x448, x25519]}|ServerOpts0],
- ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']},
- {supported_groups, [secp256r1, x25519]}|ClientOpts2],
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, 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_server_alert(Server, certificate_required),
- ssl_test_lib:close(Server),
- ssl_test_lib:close_port(Client).
-
-
-tls13_hrr_client_auth_empty_cert_ssl_server_openssl_client() ->
- [{doc,"TLS 1.3 (HelloRetryRequest): Test client authentication when client sends an empty certificate and fail_if_no_peer_cert is set to false."}].
-
-tls13_hrr_client_auth_empty_cert_ssl_server_openssl_client(Config) ->
- ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config),
- %% Delete Client Cert and Key
- ClientOpts1 = proplists:delete(certfile, ClientOpts0),
- ClientOpts2 = proplists:delete(keyfile, ClientOpts1),
- ClientOpts = [{groups,"P-256:X25519"}|ClientOpts2],
-
- ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config),
- %% Set versions
- ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']},
- {verify, verify_peer},
- {fail_if_no_peer_cert, false},
- {supported_groups, [x448, x25519]}|ServerOpts0],
- {_ClientNode, ServerNode, _Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
-
- Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts),
-
- ssl_test_lib:check_result(Server, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close_port(Client).
-
-
-tls13_hrr_client_auth_empty_cert_ssl_server_ssl_client() ->
- [{doc,"TLS 1.3 (HelloRetryRequest): Test client authentication when client sends an empty certificate and fail_if_no_peer_cert is set to false."}].
-
-tls13_hrr_client_auth_empty_cert_ssl_server_ssl_client(Config) ->
- ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config),
- %% Delete Client Cert and Key
- ClientOpts1 = proplists:delete(certfile, ClientOpts0),
- ClientOpts2 = proplists:delete(keyfile, ClientOpts1),
-
- ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config),
- %% Set versions
- ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']},
- {verify, verify_peer},
- {fail_if_no_peer_cert, false},
- {supported_groups, [x448, x25519]}|ServerOpts0],
- ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']},
- {supported_groups, [secp256r1, x25519]}|ClientOpts2],
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, 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_port(Client).
-
-
-tls13_hrr_client_auth_ssl_server_openssl_client() ->
- [{doc,"TLS 1.3 (HelloRetryRequest): Test client authentication."}].
-
-tls13_hrr_client_auth_ssl_server_openssl_client(Config) ->
- ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config),
- ClientOpts = [{groups,"P-256:X25519"}|ClientOpts0],
-
- ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config),
- %% Set versions
- ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']},
- {verify, verify_peer},
- {fail_if_no_peer_cert, true},
- {supported_groups, [x448, x25519]}|ServerOpts0],
- {_ClientNode, ServerNode, _Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
-
- Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts),
-
- ssl_test_lib:check_result(Server, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close_port(Client).
-
-
-tls13_hrr_client_auth_ssl_server_ssl_client() ->
- [{doc,"TLS 1.3 (HelloRetryRequest): Test client authentication."}].
-
-tls13_hrr_client_auth_ssl_server_ssl_client(Config) ->
- ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config),
-
- ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config),
- %% Set versions
- ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']},
- {verify, verify_peer},
- {fail_if_no_peer_cert, true},
- {supported_groups, [x448, x25519]}|ServerOpts0],
- ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']},
- {supported_groups, [secp256r1, x25519]}|ClientOpts0],
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, 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_port(Client).
-
-
-tls13_unsupported_sign_algo_client_auth_ssl_server_openssl_client() ->
- [{doc,"TLS 1.3: Test client authentication with unsupported signature_algorithm"}].
-
-tls13_unsupported_sign_algo_client_auth_ssl_server_openssl_client(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
-
- ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config),
- %% Set versions
- ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']},
- {verify, verify_peer},
- %% Skip rsa_pkcs1_sha256!
- {signature_algs, [rsa_pkcs1_sha384, rsa_pkcs1_sha512]},
- {fail_if_no_peer_cert, true}|ServerOpts0],
- {_ClientNode, ServerNode, _Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
-
- Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts),
-
- ssl_test_lib:check_server_alert(Server, insufficient_security),
- ssl_test_lib:close(Server),
- ssl_test_lib:close_port(Client).
-
-
-tls13_unsupported_sign_algo_client_auth_ssl_server_ssl_client() ->
- [{doc,"TLS 1.3: Test client authentication with unsupported signature_algorithm"}].
-
-tls13_unsupported_sign_algo_client_auth_ssl_server_ssl_client(Config) ->
- ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config),
-
- ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config),
- %% Set versions
- ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']},
- {verify, verify_peer},
- %% Skip rsa_pkcs1_sha256!
- {signature_algs, [rsa_pkcs1_sha384, rsa_pkcs1_sha512]},
- {fail_if_no_peer_cert, true}|ServerOpts0],
- ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}|ClientOpts0],
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, 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_server_alert(Server, insufficient_security),
- ssl_test_lib:close(Server),
- ssl_test_lib:close_port(Client).
-
-
-%% Triggers a Server Alert as openssl s_client does not have a certificate with a
-%% signature algorithm supported by the server (signature_algorithms_cert extension
-%% of CertificateRequest does not contain the algorithm of the client certificate).
-%% openssl s_client sends an empty certificate.
-tls13_unsupported_sign_algo_cert_client_auth_ssl_server_openssl_client() ->
- [{doc,"TLS 1.3: Test client authentication with unsupported signature_algorithm_cert"}].
-
-tls13_unsupported_sign_algo_cert_client_auth_ssl_server_openssl_client(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
-
- ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config),
- %% Set versions
- ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']},
- {log_level, debug},
- {verify, verify_peer},
- {signature_algs, [rsa_pkcs1_sha256, rsa_pkcs1_sha384, rsa_pss_rsae_sha256]},
- %% Skip rsa_pkcs1_sha256!
- {signature_algs_cert, [rsa_pkcs1_sha384, rsa_pkcs1_sha512]},
- {fail_if_no_peer_cert, true}|ServerOpts0],
- {_ClientNode, ServerNode, _Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
-
- Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts),
-
- ssl_test_lib:check_server_alert(Server, certificate_required),
- ssl_test_lib:close(Server),
- ssl_test_lib:close_port(Client).
-
-
-%% Triggers a Server Alert as ssl client does not have a certificate with a
-%% signature algorithm supported by the server (signature_algorithms_cert extension
-%% of CertificateRequest does not contain the algorithm of the client certificate).
-%% ssl client sends an empty certificate.
-tls13_unsupported_sign_algo_cert_client_auth_ssl_server_ssl_client() ->
- [{doc,"TLS 1.3: Test client authentication with unsupported signature_algorithm_cert"}].
-
-tls13_unsupported_sign_algo_cert_client_auth_ssl_server_ssl_client(Config) ->
- ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config),
-
- ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config),
- %% Set versions
- ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']},
- {log_level, debug},
- {verify, verify_peer},
- {signature_algs, [rsa_pkcs1_sha256, rsa_pkcs1_sha384, rsa_pss_rsae_sha256]},
- %% Skip rsa_pkcs1_sha256!
- {signature_algs_cert, [rsa_pkcs1_sha384, rsa_pkcs1_sha512]},
- {fail_if_no_peer_cert, true}|ServerOpts0],
- ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}|ClientOpts0],
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, 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_server_alert(Server, certificate_required),
- ssl_test_lib:close(Server),
- ssl_test_lib:close_port(Client).
-
-
-tls13_connection_information() ->
- [{doc,"Test the API function ssl:connection_information/1 in a TLS 1.3 connection"}].
-
-tls13_connection_information(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
- ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config),
- %% Set versions
- ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}|ServerOpts0],
- {_ClientNode, ServerNode, _Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, connection_information_result, []}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
-
- Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts),
-
- ssl_test_lib:check_result(Server, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close_port(Client).
-
-
-tls13_ssl_server_with_alpn_ssl_client() ->
- [{doc,"Test TLS 1.3 between ssl server with ALPN configured and ssl client"}].
-
-tls13_ssl_server_with_alpn_ssl_client(Config) ->
- ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config),
- ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- %% Set versions
- ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']},
- {alpn_preferred_protocols, [<<5,6>>, <<1>>]}|ServerOpts0],
- ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}|ClientOpts0],
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, 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_port(Client).
-
-
-tls13_ssl_server_with_alpn_ssl_client_empty_alpn() ->
- [{doc,"Test TLS 1.3 between ssl server with ALPN configured and ssl client with empty ALPN"}].
-
-tls13_ssl_server_with_alpn_ssl_client_empty_alpn(Config) ->
- ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config),
- ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- %% Set versions
- ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']},
- {alpn_preferred_protocols, [<<5,6>>, <<1>>]}|ServerOpts0],
- ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']},
- {alpn_advertised_protocols, []}|ClientOpts0],
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, 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_server_alert(Server, no_application_protocol),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close_port(Client).
-
-
-tls13_ssl_server_with_alpn_ssl_client_bad_alpn() ->
- [{doc,"Test TLS 1.3 between ssl server with ALPN configured and ssl client with bad ALPN"}].
-
-tls13_ssl_server_with_alpn_ssl_client_bad_alpn(Config) ->
- ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config),
- ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- %% Set versions
- ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']},
- {alpn_preferred_protocols, [<<5,6>>, <<1>>]}|ServerOpts0],
- ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']},
- {alpn_advertised_protocols, [<<1,2,3,4>>]}|ClientOpts0],
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, 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_server_alert(Server, no_application_protocol),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close_port(Client).
-
-tls13_ssl_server_with_alpn_ssl_client_alpn() ->
- [{doc,"Test TLS 1.3 between ssl server with ALPN configured and ssl client with correct ALPN"}].
-
-tls13_ssl_server_with_alpn_ssl_client_alpn(Config) ->
- ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config),
- ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- %% Set versions
- ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']},
- {alpn_preferred_protocols, [<<5,6>>, <<1>>]}|ServerOpts0],
- ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']},
- {alpn_advertised_protocols, [<<1,2,3,4>>, <<5,6>>]}|ClientOpts0],
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, 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_port(Client).
-
-
-tls13_ecdsa_ssl_server_openssl_client() ->
- [{doc,"Test TLS 1.3 basic connection between ssl server and openssl s_client using ECDSA certificates"}].
-
-tls13_ecdsa_ssl_server_openssl_client(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_ecdsa_opts, Config),
- ServerOpts0 = ssl_test_lib:ssl_options(server_ecdsa_opts, Config),
- %% Set versions
- ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}|ServerOpts0],
- {_ClientNode, ServerNode, _Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
-
- Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts),
-
- ssl_test_lib:check_result(Server, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close_port(Client).
-
-tls13_ecdsa_ssl_server_ssl_client() ->
- [{doc,"Test TLS 1.3 basic connection between ssl server and ssl client using ECDSA certificates"}].
-
-tls13_ecdsa_ssl_server_ssl_client(Config) ->
- ClientOpts0 = ssl_test_lib:ssl_options(client_ecdsa_opts, Config),
- ServerOpts0 = ssl_test_lib:ssl_options(server_ecdsa_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- %% Set versions
- ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}|ServerOpts0],
- ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}|ClientOpts0],
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, 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_port(Client).
-
-
-tls13_ecdsa_openssl_server_ssl_client() ->
- [{doc,"Test TLS 1.3 basic connection between openssl server and ssl client using ECDSA certificates"}].
-
-tls13_ecdsa_openssl_server_ssl_client(Config) ->
- process_flag(trap_exit, true),
- ServerOpts = ssl_test_lib:ssl_options(server_ecdsa_verify_opts, Config),
- ClientOpts0 = ssl_test_lib:ssl_options(client_ecdsa_opts, Config),
-
- ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}|ClientOpts0],
-
- {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
-
- Data = "From openssl to erlang",
-
- Port = ssl_test_lib:inet_port(node()),
- CertFile = proplists:get_value(certfile, ServerOpts),
- CaCertFile = proplists:get_value(cacertfile, ServerOpts),
- KeyFile = proplists:get_value(keyfile, ServerOpts),
- Exe = "openssl",
- Args = ["s_server", "-accept", integer_to_list(Port),
- "-tls1_3",
- "-cert", CertFile, "-CAfile", CaCertFile,
- "-key", KeyFile, "-Verify", "2"],
-
- OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
-
- ssl_test_lib:wait_for_openssl_server(Port, tls),
-
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE,
- erlang_ssl_receive, [Data]}},
- {options, ClientOpts}]),
- true = port_command(OpensslPort, Data),
-
- ssl_test_lib:check_result(Client, ok),
-
- %% Clean close down! Server needs to be closed first !!
- ssl_test_lib:close_port(OpensslPort),
- ssl_test_lib:close(Client),
- process_flag(trap_exit, false).
-
-
-tls13_ecdsa_client_auth_ssl_server_ssl_client() ->
- [{doc,"TLS 1.3: Test client authentication with ECDSA certificates."}].
-
-tls13_ecdsa_client_auth_ssl_server_ssl_client(Config) ->
- ClientOpts0 = ssl_test_lib:ssl_options(client_ecdsa_opts, Config),
- ServerOpts0 = ssl_test_lib:ssl_options(server_ecdsa_opts, Config),
-
- %% Set versions
- ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']},
- {verify, verify_peer},
- {fail_if_no_peer_cert, true}|ServerOpts0],
- ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}|ClientOpts0],
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
-
- %%Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts),
- 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_port(Client).
-
-
-
-%%--------------------------------------------------------------------
%% Internal functions ------------------------------------------------
%%--------------------------------------------------------------------
send_recv_result(Socket) ->
@@ -6468,491 +463,9 @@ tcp_send_recv_result(Socket) ->
{ok,"Hello world"} = gen_tcp:recv(Socket, 11),
ok.
-basic_verify_test_no_close(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, 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),
- {Server, Client}.
-
-basic_test(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, 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).
-
-prf_create_plan(TlsVersions, PRFs, Results) ->
- lists:foldl(fun(Ver, Acc) ->
- A = prf_ciphers_and_expected(Ver, PRFs, Results),
- [A|Acc]
- end, [], TlsVersions).
-prf_ciphers_and_expected(TlsVer, PRFs, Results) ->
- case TlsVer of
- TlsVer when TlsVer == sslv3 orelse TlsVer == tlsv1
- orelse TlsVer == 'tlsv1.1' orelse TlsVer == 'dtlsv1' ->
- Ciphers = ssl:cipher_suites(),
- {_, Expected} = lists:keyfind(md5sha, 1, Results),
- [[{tls_ver, TlsVer}, {ciphers, Ciphers}, {expected, Expected}, {prf, md5sha}]];
- TlsVer when TlsVer == 'tlsv1.2' orelse TlsVer == 'dtlsv1.2'->
- lists:foldl(
- fun(PRF, Acc) ->
- Ciphers = prf_get_ciphers(TlsVer, PRF),
- case Ciphers of
- [] ->
- ct:log("No ciphers for PRF algorithm ~p. Skipping.", [PRF]),
- Acc;
- Ciphers ->
- {_, Expected} = lists:keyfind(PRF, 1, Results),
- [[{tls_ver, TlsVer}, {ciphers, Ciphers}, {expected, Expected},
- {prf, PRF}] | Acc]
- end
- end, [], PRFs)
- end.
-prf_get_ciphers(_, PRF) ->
- lists:filter(
- fun(C) when tuple_size(C) == 4 andalso
- element(4, C) == PRF ->
- true;
- (_) ->
- false
- end,
- ssl:cipher_suites()).
-prf_run_test(_, TlsVer, [], _, Prf) ->
- ct:fail({error, cipher_list_empty, TlsVer, Prf});
-prf_run_test(Config, TlsVer, Ciphers, Expected, Prf) ->
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- BaseOpts = [{active, true}, {versions, [TlsVer]}, {ciphers, Ciphers}, {protocol, tls_or_dtls(TlsVer)}],
- ServerOpts = BaseOpts ++ proplists:get_value(server_opts, Config),
- ClientOpts = BaseOpts ++ proplists:get_value(client_opts, Config),
- Server = ssl_test_lib:start_server(
- [{node, ServerNode}, {port, 0}, {from, self()},
- {mfa, {?MODULE, prf_verify_value, [TlsVer, Expected, Prf]}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client(
- [{node, ClientNode}, {port, Port},
- {host, Hostname}, {from, self()},
- {mfa, {?MODULE, prf_verify_value, [TlsVer, Expected, Prf]}},
- {options, ClientOpts}]),
- ssl_test_lib:check_result(Server, ok, Client, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-prf_verify_value(Socket, TlsVer, Expected, Algo) ->
- Ret = ssl:prf(Socket, <<>>, <<>>, [<<>>], 16),
- case TlsVer of
- sslv3 ->
- case Ret of
- {error, undefined} -> ok;
- _ ->
- {error, {expected, {error, undefined},
- got, Ret, tls_ver, TlsVer, prf_algorithm, Algo}}
- end;
- _ ->
- case Ret of
- {ok, Expected} -> ok;
- {ok, Val} -> {error, {expected, Expected, got, Val, tls_ver, TlsVer,
- prf_algorithm, Algo}}
- end
- end.
-
-send_recv_result_timeout_client(Socket) ->
- {error, timeout} = ssl:recv(Socket, 11, 500),
- {error, timeout} = ssl:recv(Socket, 11, 0),
- ssl:send(Socket, "Hello world"),
- receive
- Msg ->
- io:format("Msg ~p~n",[Msg])
- after 500 ->
- ok
- end,
- {ok, "Hello world"} = ssl:recv(Socket, 11, 500),
- ok.
-send_recv_result_timeout_server(Socket) ->
- ssl:send(Socket, "Hello"),
- {ok, "Hello world"} = ssl:recv(Socket, 11),
- ssl:send(Socket, " world"),
- ok.
-
-recv_close(Socket) ->
- {error, closed} = ssl:recv(Socket, 11),
- receive
- {_,{error,closed}} ->
- error_extra_close_sent_to_user_process
- after 500 ->
- ok
- end.
-
-
-send_recv_result_active_rizzo(Socket) ->
- ssl:send(Socket, "Hello world"),
- "Hello world" = ssl_test_lib:active_recv(Socket, 11),
- ok.
-
-send_recv_result_active_no_rizzo(Socket) ->
- ssl:send(Socket, "Hello world"),
- "Hello world" = ssl_test_lib:active_recv(Socket, 11),
- ok.
-
-
-ssl_active_recv(N) ->
- ssl_active_recv(N, []).
-
-ssl_active_recv(0, Acc) ->
- Acc;
-ssl_active_recv(N, Acc) ->
- receive
- {ssl, _, Bytes} ->
- ssl_active_recv(N-length(Bytes), Acc ++ Bytes)
- end.
-
result_ok(_Socket) ->
ok.
-renegotiate(Socket, Data) ->
- ct:log("Renegotiating ~n", []),
- Result = ssl:renegotiate(Socket),
- ct:log("Result ~p~n", [Result]),
- ssl:send(Socket, Data),
- case Result of
- ok ->
- ok;
- Other ->
- Other
- end.
-
-renegotiate_reuse_session(Socket, Data) ->
- %% Make sure session is registered
- ct:sleep(?SLEEP),
- renegotiate(Socket, Data).
-
-renegotiate_immediately(Socket) ->
- _ = ssl_test_lib:active_recv(Socket, 11),
- ok = ssl:renegotiate(Socket),
- {error, renegotiation_rejected} = ssl:renegotiate(Socket),
- ct:sleep(?RENEGOTIATION_DISABLE_TIME + ?SLEEP),
- ok = ssl:renegotiate(Socket),
- ct:log("Renegotiated again"),
- ssl:send(Socket, "Hello world"),
- ok.
-
-renegotiate_rejected(Socket) ->
- _ = ssl_test_lib:active_recv(Socket, 11),
- {error, renegotiation_rejected} = ssl:renegotiate(Socket),
- {error, renegotiation_rejected} = ssl:renegotiate(Socket),
- ct:sleep(?RENEGOTIATION_DISABLE_TIME +1),
- {error, renegotiation_rejected} = ssl:renegotiate(Socket),
- ct:log("Failed to renegotiate again"),
- ssl:send(Socket, "Hello world"),
- ok.
-
-rizzo_add_mitigation_option(Value, Config) ->
- lists:foldl(fun(Opt, Acc) ->
- case proplists:get_value(Opt, Acc) of
- undefined -> Acc;
- C ->
- N = lists:keystore(beast_mitigation, 1, C,
- {beast_mitigation, Value}),
- lists:keystore(Opt, 1, Acc, {Opt, N})
- end
- end, Config,
- [client_opts, client_dsa_opts, server_opts, server_dsa_opts,
- server_ecdsa_opts, server_ecdh_rsa_opts]).
-
-new_config(PrivDir, ServerOpts0) ->
- CaCertFile = proplists:get_value(cacertfile, ServerOpts0),
- CertFile = proplists:get_value(certfile, ServerOpts0),
- KeyFile = proplists:get_value(keyfile, ServerOpts0),
- NewCaCertFile = filename:join(PrivDir, "new_ca.pem"),
- NewCertFile = filename:join(PrivDir, "new_cert.pem"),
- NewKeyFile = filename:join(PrivDir, "new_key.pem"),
- file:copy(CaCertFile, NewCaCertFile),
- file:copy(CertFile, NewCertFile),
- file:copy(KeyFile, NewKeyFile),
- ServerOpts1 = proplists:delete(cacertfile, ServerOpts0),
- ServerOpts2 = proplists:delete(certfile, ServerOpts1),
- ServerOpts = proplists:delete(keyfile, ServerOpts2),
-
- {ok, PEM} = file:read_file(NewCaCertFile),
- ct:log("CA file content: ~p~n", [public_key:pem_decode(PEM)]),
-
- [{cacertfile, NewCaCertFile}, {certfile, NewCertFile},
- {keyfile, NewKeyFile} | ServerOpts].
-
-session_cache_process(_Type,Config) when is_list(Config) ->
- reuse_session(Config).
-
-init([Type]) ->
- ets:new(ssl_test, [named_table, public, set]),
- ets:insert(ssl_test, {type, Type}),
- case Type of
- list ->
- spawn(fun() -> session_loop([]) end);
- mnesia ->
- mnesia:start(),
- {atomic,ok} = mnesia:create_table(sess_cache, []),
- sess_cache
- end.
-
-session_cb() ->
- [{type, Type}] = ets:lookup(ssl_test, type),
- Type.
-
-terminate(Cache) ->
- case session_cb() of
- list ->
- Cache ! terminate;
- mnesia ->
- catch {atomic,ok} =
- mnesia:delete_table(sess_cache)
- end.
-
-lookup(Cache, Key) ->
- case session_cb() of
- list ->
- Cache ! {self(), lookup, Key},
- receive {Cache, Res} -> Res end;
- mnesia ->
- case mnesia:transaction(fun() ->
- mnesia:read(sess_cache,
- Key, read)
- end) of
- {atomic, [{sess_cache, Key, Value}]} ->
- Value;
- _ ->
- undefined
- end
- end.
-
-update(Cache, Key, Value) ->
- case session_cb() of
- list ->
- Cache ! {update, Key, Value};
- mnesia ->
- {atomic, ok} =
- mnesia:transaction(fun() ->
- mnesia:write(sess_cache,
- {sess_cache, Key, Value}, write)
- end)
- end.
-
-delete(Cache, Key) ->
- case session_cb() of
- list ->
- Cache ! {delete, Key};
- mnesia ->
- {atomic, ok} =
- mnesia:transaction(fun() ->
- mnesia:delete(sess_cache, Key)
- end)
- end.
-
-foldl(Fun, Acc, Cache) ->
- case session_cb() of
- list ->
- Cache ! {self(),foldl,Fun,Acc},
- receive {Cache, Res} -> Res end;
- mnesia ->
- Foldl = fun() ->
- mnesia:foldl(Fun, Acc, sess_cache)
- end,
- {atomic, Res} = mnesia:transaction(Foldl),
- Res
- end.
-
-select_session(Cache, PartialKey) ->
- case session_cb() of
- list ->
- Cache ! {self(),select_session, PartialKey},
- receive
- {Cache, Res} ->
- Res
- end;
- mnesia ->
- Sel = fun() ->
- mnesia:select(Cache,
- [{{sess_cache,{PartialKey,'$1'}, '$2'},
- [],['$$']}])
- end,
- {atomic, Res} = mnesia:transaction(Sel),
- Res
- end.
-
-session_loop(Sess) ->
- receive
- terminate ->
- ok;
- {Pid, lookup, Key} ->
- case lists:keysearch(Key,1,Sess) of
- {value, {Key,Value}} ->
- Pid ! {self(), Value};
- _ ->
- Pid ! {self(), undefined}
- end,
- session_loop(Sess);
- {update, Key, Value} ->
- NewSess = [{Key,Value}| lists:keydelete(Key,1,Sess)],
- session_loop(NewSess);
- {delete, Key} ->
- session_loop(lists:keydelete(Key,1,Sess));
- {Pid,foldl,Fun,Acc} ->
- Res = lists:foldl(Fun, Acc,Sess),
- Pid ! {self(), Res},
- session_loop(Sess);
- {Pid,select_session,PKey} ->
- Sel = fun({{PKey0, Id},Session}, Acc) when PKey == PKey0 ->
- [[Id, Session]|Acc];
- (_,Acc) ->
- Acc
- end,
- Sessions = lists:foldl(Sel, [], Sess),
- Pid ! {self(), Sessions},
- session_loop(Sess)
- end.
-
-
-erlang_ssl_receive(Socket, Data) ->
- case ssl_test_lib:active_recv(Socket, length(Data)) of
- Data ->
- ok;
- Other ->
- ct:fail({{expected, Data}, {got, Other}})
- end.
-
-receive_msg(_) ->
- receive
- Msg ->
- Msg
- end.
-
-controlling_process_result(Socket, Pid, Msg) ->
- ok = ssl:controlling_process(Socket, Pid),
- %% Make sure other side has evaluated controlling_process
- %% before message is sent
- ct:sleep(?SLEEP),
- ssl:send(Socket, Msg),
- no_result_msg.
-
-
-controller_dies_result(_Socket, _Pid, _Msg) ->
- receive Result -> Result end.
-
-get_close(Pid, Where) ->
- receive
- {'EXIT', Pid, _Reason} ->
- receive
- {_, {ssl_closed, Socket}} ->
- ct:log("Socket closed ~p~n",[Socket]);
- Unexpected ->
- ct:log("Unexpected ~p~n",[Unexpected]),
- ct:fail({line, ?LINE-1})
- after 5000 ->
- ct:fail({timeout, {line, ?LINE, Where}})
- end;
- Unexpected ->
- ct:log("Unexpected ~p~n",[Unexpected]),
- ct:fail({line, ?LINE-1})
- after 5000 ->
- ct:fail({timeout, {line, ?LINE, Where}})
- end.
-
-run_send_recv_rizzo(Ciphers, Config, Version, Mfa) ->
- Result = lists:map(fun(Cipher) ->
- rizzo_test(Cipher, Config, Version, Mfa) end,
- Ciphers),
- case lists:flatten(Result) of
- [] ->
- ok;
- Error ->
- ct:log("Cipher suite errors: ~p~n", [Error]),
- ct:fail(cipher_suite_failed_see_test_case_log)
- end.
-
-rizzo_test(Cipher, Config, Version, Mfa) ->
- {ClientOpts, ServerOpts} = client_server_opts(Cipher, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, Mfa},
- {options, [{active, true}, {ciphers, [Cipher]},
- {versions, [Version]}
- | ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, Mfa},
- {options, [{active, true}, {ciphers, [Cipher]}| ClientOpts]}]),
-
- Result = ssl_test_lib:check_result(Server, ok, Client, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client),
- case Result of
- ok ->
- [];
- Error ->
- [{Cipher, Error}]
- end.
-
-client_server_opts(#{key_exchange := KeyAlgo}, Config)
- when KeyAlgo == rsa orelse
- KeyAlgo == dhe_rsa orelse
- KeyAlgo == ecdhe_rsa orelse
- KeyAlgo == rsa_psk orelse
- KeyAlgo == srp_rsa ->
- {ssl_test_lib:ssl_options(client_opts, Config),
- ssl_test_lib:ssl_options(server_opts, Config)};
-client_server_opts(#{key_exchange := KeyAlgo}, Config) when KeyAlgo == dss orelse KeyAlgo == dhe_dss ->
- {ssl_test_lib:ssl_options(client_dsa_opts, Config),
- ssl_test_lib:ssl_options(server_dsa_opts, Config)};
-client_server_opts(#{key_exchange := KeyAlgo}, Config) when KeyAlgo == ecdh_ecdsa orelse KeyAlgo == ecdhe_ecdsa ->
- {ssl_test_lib:ssl_options(client_opts, Config),
- ssl_test_lib:ssl_options(server_ecdsa_opts, Config)};
-client_server_opts(#{key_exchange := KeyAlgo}, Config) when KeyAlgo == ecdh_rsa ->
- {ssl_test_lib:ssl_options(client_opts, Config),
- ssl_test_lib:ssl_options(server_ecdh_rsa_opts, Config)}.
-
-connection_information_result(Socket) ->
- {ok, Info = [_ | _]} = ssl:connection_information(Socket),
- case length(Info) > 3 of
- true ->
- %% Atleast one ssl_option() is set
- ct:log("Info ~p", [Info]),
- ok;
- false ->
- ct:fail(no_ssl_options_returned)
- end.
-
-connection_info_result(Socket) ->
- {ok, Info} = ssl:connection_information(Socket, [protocol, selected_cipher_suite]),
- {ok, {proplists:get_value(protocol, Info), proplists:get_value(selected_cipher_suite, Info)}}.
protocol_info_result(Socket) ->
{ok, [{protocol, PVersion}]} = ssl:connection_information(Socket, [protocol]),
@@ -6962,11 +475,7 @@ version_info_result(Socket) ->
{ok, [{version, Version}]} = ssl:connection_information(Socket, [version]),
{ok, Version}.
-secret_connection_info_result(Socket) ->
- {ok, [{client_random, ClientRand}, {server_random, ServerRand}, {master_secret, MasterSecret}]}
- = ssl:connection_information(Socket, [client_random, server_random, master_secret]),
- is_binary(ClientRand) andalso is_binary(ServerRand) andalso is_binary(MasterSecret).
-
+
connect_dist_s(S) ->
Msg = term_to_binary({erlang,term}),
ok = ssl:send(S, Msg).
@@ -6976,88 +485,11 @@ connect_dist_c(S) ->
{ok, Test} = ssl:recv(S, 0, 10000),
ok.
-tls_downgrade_result(Socket, Pid) ->
- ok = ssl_test_lib:send_recv_result(Socket),
- Pid ! {self(), ready},
- receive
- go ->
- ok
- end,
- case ssl:close(Socket, {self(), 10000}) of
- {ok, TCPSocket} ->
- inet:setopts(TCPSocket, [{active, true}]),
- gen_tcp:send(TCPSocket, "Downgraded"),
- receive
- {tcp, TCPSocket, <<"Downgraded">>} ->
- ok;
- {tcp_closed, TCPSocket} ->
- ct:fail("Peer timed out, downgrade aborted"),
- ok;
- Other ->
- {error, Other}
- end;
- {error, timeout} ->
- ct:fail("Timed out, downgrade aborted"),
- ok;
- Fail ->
- {error, Fail}
- end.
-
-tls_close(Socket) ->
- ok = ssl_test_lib:send_recv_result(Socket),
- case ssl:close(Socket, 5000) of
- ok ->
- ok;
- {error, closed} ->
- ok;
- Other ->
- ct:fail(Other)
- end.
-
- %% First two clauses handles 1/n-1 splitting countermeasure Rizzo/Duong-Beast
-treashold(N, {3,0}) ->
- (N div 2) + 1;
-treashold(N, {3,1}) ->
- (N div 2) + 1;
-treashold(N, _) ->
- N + 1.
-
-get_invalid_inet_option(Socket) ->
- {error, {options, {socket_options, foo, _}}} = ssl:getopts(Socket, [foo]),
- ok.
-
-tls_shutdown_result(Socket, server) ->
- ssl:send(Socket, "Hej"),
- ok = ssl:shutdown(Socket, write),
- {ok, "Hej hopp"} = ssl:recv(Socket, 8),
- ok;
-
-tls_shutdown_result(Socket, client) ->
- ssl:send(Socket, "Hej hopp"),
- ok = ssl:shutdown(Socket, write),
- {ok, "Hej"} = ssl:recv(Socket, 3),
- ok.
-
-tls_shutdown_write_result(Socket, server) ->
- ct:sleep(?SLEEP),
- ssl:shutdown(Socket, write);
-tls_shutdown_write_result(Socket, client) ->
- ssl:recv(Socket, 0).
-
dummy(_Socket) ->
%% Should not happen as the ssl connection will not be established
%% due to fatal handshake failiure
exit(kill).
-tls_shutdown_both_result(Socket, server) ->
- ct:sleep(?SLEEP),
- ssl:shutdown(Socket, read_write);
-tls_shutdown_both_result(Socket, client) ->
- ssl:recv(Socket, 0).
-
-peername_result(S) ->
- ssl:peername(S).
-
version_option_test(Config, Version) ->
ClientOpts = ssl_test_lib:ssl_options(client_opts, Config),
ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
@@ -7083,50 +515,3 @@ version_option_test(Config, Version) ->
ssl_test_lib:close(Server),
ssl_test_lib:close(Client).
-try_recv_active(Socket) ->
- ssl:send(Socket, "Hello world"),
- {error, einval} = ssl:recv(Socket, 11),
- ok.
-try_recv_active_once(Socket) ->
- {error, einval} = ssl:recv(Socket, 11),
- ok.
-
-
-wait_for_send(Socket) ->
- %% Make sure TLS process processed send message event
- _ = ssl:connection_information(Socket).
-
-tls_or_dtls('dtlsv1') ->
- dtls;
-tls_or_dtls('dtlsv1.2') ->
- dtls;
-tls_or_dtls(_) ->
- tls.
-
-hexstr2int(S) ->
- B = hexstr2bin(S),
- Bits = size(B) * 8,
- <<Integer:Bits/integer>> = B,
- Integer.
-
-hexstr2bin(S) when is_binary(S) ->
- hexstr2bin(S, <<>>);
-hexstr2bin(S) ->
- hexstr2bin(list_to_binary(S), <<>>).
-%%
-hexstr2bin(<<>>, Acc) ->
- Acc;
-hexstr2bin(<<C,T/binary>>, Acc) when C =:= 32; %% SPACE
- C =:= 10; %% LF
- C =:= 13 -> %% CR
- hexstr2bin(T, Acc);
-hexstr2bin(<<X,Y,T/binary>>, Acc) ->
- I = hex2int(X) * 16 + hex2int(Y),
- hexstr2bin(T, <<Acc/binary,I>>).
-
-hex2int(C) when $0 =< C, C =< $9 ->
- C - $0;
-hex2int(C) when $A =< C, C =< $F ->
- C - $A + 10;
-hex2int(C) when $a =< C, C =< $f ->
- C - $a + 10.
diff --git a/lib/ssl/test/ssl_bench_SUITE.erl b/lib/ssl/test/ssl_bench_SUITE.erl
index 35efa2b8a3..a297539c36 100644
--- a/lib/ssl/test/ssl_bench_SUITE.erl
+++ b/lib/ssl/test/ssl_bench_SUITE.erl
@@ -174,7 +174,12 @@ do_test(Type, TC, Loop, ParallellConnections, Server) ->
end,
{TimeInMicro, _} = timer:tc(Run),
TotalTests = ParallellConnections * Loop,
- TestPerSecond = 1000000 * TotalTests div TimeInMicro,
+ TestPerSecond = case TimeInMicro of
+ 0 ->
+ undefined;
+ _ ->
+ 1000000 * TotalTests div TimeInMicro
+ end,
io:format("TC ~p ~p ~p ~p 1/s~n", [TC, Type, ParallellConnections, TestPerSecond]),
unlink(SPid),
SPid ! quit,
diff --git a/lib/ssl/test/ssl_cert_SUITE.erl b/lib/ssl/test/ssl_cert_SUITE.erl
new file mode 100644
index 0000000000..d5ca9bcf02
--- /dev/null
+++ b/lib/ssl/test/ssl_cert_SUITE.erl
@@ -0,0 +1,922 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2019-2019. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(ssl_cert_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+-include_lib("common_test/include/ct.hrl").
+-include_lib("public_key/include/public_key.hrl").
+
+%%--------------------------------------------------------------------
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
+
+all() ->
+ [
+ {group, 'tlsv1.3'},
+ {group, 'tlsv1.2'},
+ {group, 'tlsv1.1'},
+ {group, 'tlsv1'},
+ {group, 'sslv3'},
+ {group, 'dtlsv1.2'},
+ {group, 'dtlsv1'}
+ ].
+
+groups() ->
+ [
+ {'tlsv1.3', [], tls_1_3_protocol_groups()},
+ {'tlsv1.2', [], tls_1_2_protocol_groups()},
+ {'tlsv1.1', [], ssl_protocol_groups()},
+ {'tlsv1', [], ssl_protocol_groups()},
+ {'sslv3', [], ssl_protocol_groups()},
+ {'dtlsv1.2', [], tls_1_2_protocol_groups()},
+ {'dtlsv1', [], ssl_protocol_groups()},
+ {rsa, [], all_version_tests() ++ rsa_tests() ++ pre_tls_1_3_rsa_tests()},
+ {ecdsa, [], all_version_tests()},
+ {dsa, [], all_version_tests()},
+ {rsa_1_3, [], all_version_tests() ++ rsa_tests() ++ tls_1_3_tests() ++ tls_1_3_rsa_tests()},
+ {ecdsa_1_3, [], all_version_tests() ++ tls_1_3_tests()}
+ ].
+
+ssl_protocol_groups() ->
+ [{group, rsa},
+ {group, dsa}].
+
+tls_1_2_protocol_groups() ->
+ [{group, rsa},
+ {group, ecdsa},
+ {group, dsa}].
+
+tls_1_3_protocol_groups() ->
+ [{group, rsa_1_3},
+ {group, ecdsa_1_3}].
+
+tls_1_3_tests() ->
+ [
+ hello_retry_request,
+ custom_groups,
+ hello_retry_client_auth,
+ hello_retry_client_auth_empty_cert_accepted,
+ hello_retry_client_auth_empty_cert_rejected
+ ].
+
+pre_tls_1_3_rsa_tests() ->
+ [
+ key_auth_ext_sign_only
+ ].
+
+rsa_tests() ->
+ [
+ longer_chain
+ ].
+
+tls_1_3_rsa_tests() ->
+ [
+ unsupported_sign_algo_client_auth,
+ unsupported_sign_algo_cert_client_auth
+ ].
+
+all_version_tests() ->
+ [
+ no_auth,
+ auth,
+ client_auth_empty_cert_accepted,
+ client_auth_empty_cert_rejected,
+ client_auth_partial_chain,
+ client_auth_allow_partial_chain,
+ client_auth_do_not_allow_partial_chain,
+ client_auth_partial_chain_fun_fail,
+ missing_root_cert_no_auth,
+ missing_root_cert_auth,
+ missing_root_cert_auth_user_verify_fun_accept,
+ missing_root_cert_auth_user_verify_fun_reject,
+ verify_fun_always_run_client,
+ verify_fun_always_run_server,
+ incomplete_chain_auth,
+ invalid_signature_client,
+ invalid_signature_server,
+ critical_extension_auth,
+ critical_extension_client_auth,
+ critical_extension_no_auth,
+ extended_key_usage_auth,
+ extended_key_usage_client_auth,
+ cert_expired,
+ client_auth_once,
+ no_auth_key_identifier_ext,
+ no_auth_key_identifier_ext_keyEncipherment
+ ].
+
+init_per_suite(Config) ->
+ catch crypto:stop(),
+ try crypto:start() of
+ ok ->
+ ssl_test_lib:clean_start(),
+ Config
+ catch _:_ ->
+ {skip, "Crypto did not start"}
+ end.
+
+end_per_suite(_Config) ->
+ ssl:stop(),
+ application:unload(ssl),
+ application:stop(crypto).
+
+init_per_group(Group, Config0) when Group == rsa;
+ Group == rsa_1_3 ->
+ Config = ssl_test_lib:make_rsa_cert(Config0),
+ COpts = proplists:get_value(client_rsa_opts, Config),
+ SOpts = proplists:get_value(server_rsa_opts, Config),
+ [{cert_key_alg, rsa} |
+ lists:delete(cert_key_alg,
+ [{client_cert_opts, COpts},
+ {server_cert_opts, SOpts} |
+ lists:delete(server_cert_opts,
+ lists:delete(client_cert_opts, Config))])];
+init_per_group(Group, Config0) when Group == ecdsa;
+ Group == ecdsa_1_3 ->
+
+ PKAlg = crypto:supports(public_keys),
+ case lists:member(ecdsa, PKAlg) andalso (lists:member(ecdh, PKAlg) orelse lists:member(dh, PKAlg)) of
+ true ->
+ Config = ssl_test_lib:make_ecdsa_cert(Config0),
+ COpts = proplists:get_value(client_ecdsa_opts, Config),
+ SOpts = proplists:get_value(server_ecdsa_opts, Config),
+ [{cert_key_alg, ecdsa} |
+ lists:delete(cert_key_alg,
+ [{client_cert_opts, COpts},
+ {server_cert_opts, SOpts} |
+ lists:delete(server_cert_opts,
+ lists:delete(client_cert_opts, Config))]
+ )];
+ false ->
+ {skip, "Missing EC crypto support"}
+ end;
+
+init_per_group(Group, Config0) when Group == dsa ->
+ PKAlg = crypto:supports(public_keys),
+ case lists:member(dss, PKAlg) andalso lists:member(dh, PKAlg) of
+ true ->
+ Config = ssl_test_lib:make_dsa_cert(Config0),
+ COpts = proplists:get_value(client_dsa_opts, Config),
+ SOpts = proplists:get_value(server_dsa_opts, Config),
+ [{cert_key_alg, dsa} |
+ lists:delete(cert_key_alg,
+ [{client_cert_opts, COpts},
+ {server_cert_opts, SOpts} |
+ lists:delete(server_cert_opts,
+ lists:delete(client_cert_opts, Config))])];
+ false ->
+ {skip, "Missing DSS crypto support"}
+ end;
+init_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ case ssl_test_lib:sufficient_crypto_support(GroupName) of
+ true ->
+ [{client_type, erlang},
+ {server_type, erlang}, {version, GroupName}
+ | ssl_test_lib:init_tls_version(GroupName, Config)];
+ false ->
+ {skip, "Missing crypto support"}
+ end;
+ _ ->
+ ssl:start(),
+ Config
+ end.
+
+end_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ ssl_test_lib:clean_tls_version(Config);
+ false ->
+ Config
+ end.
+
+init_per_testcase(_TestCase, Config) ->
+ ssl_test_lib:ct_log_supported_protocol_versions(Config),
+ ct:timetrap({seconds, 10}),
+ Config.
+
+end_per_testcase(_TestCase, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
+no_auth() ->
+ ssl_cert_tests:no_auth().
+
+no_auth(Config) ->
+ ssl_cert_tests:no_auth(Config).
+%%--------------------------------------------------------------------
+auth() ->
+ ssl_cert_tests:auth().
+auth(Config) ->
+ ssl_cert_tests:auth(Config).
+%%--------------------------------------------------------------------
+client_auth_empty_cert_accepted() ->
+ ssl_cert_tests:client_auth_empty_cert_accepted().
+client_auth_empty_cert_accepted(Config) ->
+ ssl_cert_tests:client_auth_empty_cert_accepted(Config).
+%%--------------------------------------------------------------------
+client_auth_empty_cert_rejected() ->
+ ssl_cert_tests:client_auth_empty_cert_rejected().
+client_auth_empty_cert_rejected(Config) ->
+ ssl_cert_tests:client_auth_empty_cert_rejected(Config).
+%%--------------------------------------------------------------------
+client_auth_partial_chain() ->
+ ssl_cert_tests:client_auth_partial_chain().
+client_auth_partial_chain(Config) when is_list(Config) ->
+ ssl_cert_tests:client_auth_partial_chain(Config).
+
+%%--------------------------------------------------------------------
+client_auth_allow_partial_chain() ->
+ ssl_cert_tests:client_auth_allow_partial_chain().
+client_auth_allow_partial_chain(Config) when is_list(Config) ->
+ ssl_cert_tests:client_auth_allow_partial_chain(Config).
+%%--------------------------------------------------------------------
+client_auth_do_not_allow_partial_chain() ->
+ ssl_cert_tests:client_auth_do_not_allow_partial_chain().
+client_auth_do_not_allow_partial_chain(Config) when is_list(Config) ->
+ ssl_cert_tests:client_auth_do_not_allow_partial_chain(Config).
+
+%%--------------------------------------------------------------------
+client_auth_partial_chain_fun_fail() ->
+ ssl_cert_tests:client_auth_partial_chain_fun_fail().
+client_auth_partial_chain_fun_fail(Config) when is_list(Config) ->
+ ssl_cert_tests:client_auth_partial_chain_fun_fail(Config).
+
+%%--------------------------------------------------------------------
+missing_root_cert_no_auth() ->
+ ssl_cert_tests:missing_root_cert_no_auth().
+missing_root_cert_no_auth(Config) when is_list(Config) ->
+ ssl_cert_tests:missing_root_cert_no_auth(Config).
+
+%%--------------------------------------------------------------------
+missing_root_cert_auth() ->
+ [{doc,"Must have ROOT certs to be able to verify verify peer"}].
+missing_root_cert_auth(Config) when is_list(Config) ->
+ ServerOpts = proplists:delete(cacertfile, ssl_test_lib:ssl_options(server_cert_opts, Config)),
+ {ClientNode, ServerNode, _} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {options, [{verify, verify_peer}
+ | ServerOpts]}]),
+
+ ssl_test_lib:check_result(Server, {error, {options, {cacertfile, ""}}}),
+
+ ClientOpts = proplists:delete(cacertfile, ssl_test_lib:ssl_options(client_cert_opts, Config)),
+ Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {options, [{verify, verify_peer}
+ | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client, {error, {options, {cacertfile, ""}}}).
+
+%%--------------------------------------------------------------------
+missing_root_cert_auth_user_verify_fun_accept() ->
+ [{doc, "Test that the client succeds if the ROOT CA is unknown in verify_peer mode"
+ " with a verify_fun that accepts the unknown CA error"}].
+
+missing_root_cert_auth_user_verify_fun_accept(Config) ->
+ ServerOpts = ssl_test_lib:ssl_options(server_cert_opts, Config),
+ FunAndState = {fun(_,{bad_cert, unknown_ca}, UserState) ->
+ {valid, UserState};
+ (_,{bad_cert, _} = Reason, _) ->
+ {fail, Reason};
+ (_,{extension, _}, UserState) ->
+ {unknown, UserState};
+ (_, valid, UserState) ->
+ {valid, UserState};
+ (_, valid_peer, UserState) ->
+ {valid, UserState}
+ end, []},
+ ClientOpts = ssl_test_lib:ssl_options([{verify, verify_peer},
+ {verify_fun, FunAndState}], Config),
+ ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config).
+
+%%--------------------------------------------------------------------
+missing_root_cert_auth_user_backwardscompatibility_verify_fun_accept() ->
+ [{doc, "Test old style verify fun"}].
+
+missing_root_cert_auth_user_backwardscompatibility_verify_fun_accept(Config) ->
+ ServerOpts = ssl_test_lib:ssl_options(server_cert_opts, Config),
+ AcceptBadCa = fun({bad_cert,unknown_ca}, Acc) -> Acc;
+ (Other, Acc) -> [Other | Acc]
+ end,
+ VerifyFun =
+ fun(ErrorList) ->
+ case lists:foldl(AcceptBadCa, [], ErrorList) of
+ [] -> true;
+ [_|_] -> false
+ end
+ end,
+
+ ClientOpts = ssl_test_lib:ssl_options([{verify, verify_peer},
+ {verify_fun, VerifyFun}], Config),
+ ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config).
+
+%%--------------------------------------------------------------------
+missing_root_cert_auth_user_verify_fun_reject() ->
+ [{doc, "Test that the client fails if the ROOT CA is unknown in verify_peer mode"
+ " with a verify_fun that rejects the unknown CA error"}].
+
+missing_root_cert_auth_user_verify_fun_reject(Config) ->
+ ServerOpts = ssl_test_lib:ssl_options(server_cert_opts, Config),
+ FunAndState = {fun(_,{bad_cert, unknown_ca} = Reason, _UserState) ->
+ {fail, Reason};
+ (_,{bad_cert, _} = Reason, _) ->
+ {fail, Reason};
+ (_,{extension, UserState}, _) ->
+ {unknown, UserState};
+ (_, valid, UserState) ->
+ {valid, UserState};
+ (_, valid_peer, UserState) ->
+ {valid, UserState}
+ end, []},
+ ClientOpts = ssl_test_lib:ssl_options([{verify, verify_peer},
+ {verify_fun, FunAndState}], Config),
+ ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, unknown_ca).
+%%--------------------------------------------------------------------
+incomplete_chain_auth() ->
+ [{doc,"Test that we can verify an incompleat chain when we have the certs to rebuild it"}].
+incomplete_chain_auth(Config) when is_list(Config) ->
+ DefaultCertConf = ssl_test_lib:default_cert_chain_conf(),
+ #{client_config := ClientOpts0,
+ server_config := ServerOpts0} = ssl_test_lib:make_cert_chains_der(proplists:get_value(cert_key_alg, Config),
+ [{server_chain, DefaultCertConf},
+ {client_chain, DefaultCertConf}]),
+ [ServerRoot| _] = ServerCas = proplists:get_value(cacerts, ServerOpts0),
+ ClientCas = proplists:get_value(cacerts, ClientOpts0),
+ ClientOpts = ssl_test_lib:ssl_options([{verify, verify_peer},
+ {cacerts, ServerCas ++ ClientCas} |
+ proplists:delete(cacerts, ClientOpts0)], Config),
+ ServerOpts = ssl_test_lib:ssl_options([{verify, verify_peer},
+ {cacerts, [ServerRoot]} |
+ proplists:delete(cacerts, ServerOpts0)], Config),
+ ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config).
+
+%%--------------------------------------------------------------------
+verify_fun_always_run_client() ->
+ [{doc,"Verify that user verify_fun is always run (for valid and "
+ "valid_peer not only unknown_extension)"}].
+
+verify_fun_always_run_client(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_cert_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_cert_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib,
+ no_result, []}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ %% If user verify fun is called correctly we fail the connection.
+ %% otherwise we cannot tell this case apart form where we miss
+ %% to call users verify fun
+ FunAndState = {fun(_,{extension, _}, UserState) ->
+ {unknown, UserState};
+ (_, valid, [ChainLen]) ->
+ {valid, [ChainLen + 1]};
+ (_, valid_peer, [1]) ->
+ {fail, "verify_fun_was_always_run"};
+ (_, valid_peer, UserState) ->
+ {valid, UserState}
+ end, [0]},
+
+ Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib,
+ no_result, []}},
+ {options,
+ [{verify, verify_peer},
+ {verify_fun, FunAndState}
+ | ClientOpts]}]),
+
+ ssl_test_lib:check_client_alert(Server, Client, handshake_failure).
+
+%%--------------------------------------------------------------------
+verify_fun_always_run_server() ->
+ [{doc,"Verify that user verify_fun is always run (for valid and "
+ "valid_peer not only unknown_extension)"}].
+verify_fun_always_run_server(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_cert_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_cert_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ %% If user verify fun is called correctly we fail the connection.
+ %% otherwise we cannot tell this case apart form where we miss
+ %% to call users verify fun
+ FunAndState = {fun(_,{extension, _}, UserState) ->
+ {unknown, UserState};
+ (_, valid, [ChainLen]) ->
+ {valid, [ChainLen + 1]};
+ (_, valid_peer, [1]) ->
+ {fail, "verify_fun_was_always_run"};
+ (_, valid_peer, UserState) ->
+ {valid, UserState}
+ end, [0]},
+
+ Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib,
+ no_result, []}},
+ {options,
+ [{verify, verify_peer},
+ {verify_fun, FunAndState} |
+ 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}]),
+
+ ssl_test_lib:check_client_alert(Server, Client, handshake_failure).
+
+%%--------------------------------------------------------------------
+invalid_signature_client() ->
+ ssl_cert_tests:invalid_signature_client().
+invalid_signature_client(Config) when is_list(Config) ->
+ ssl:clear_pem_cache(),
+ ssl_cert_tests:invalid_signature_client(Config).
+%%--------------------------------------------------------------------
+invalid_signature_server() ->
+ ssl_cert_tests:invalid_signature_server().
+invalid_signature_server(Config) when is_list(Config) ->
+ ssl:clear_pem_cache(),
+ ssl_cert_tests:invalid_signature_server(Config).
+
+%%--------------------------------------------------------------------
+critical_extension_auth() ->
+ [{doc,"Test cert that has a critical unknown extension in verify_peer mode"}].
+
+critical_extension_auth(Config) when is_list(Config) ->
+ DefaultCertConf = ssl_test_lib:default_cert_chain_conf(),
+ Ext = x509_test:extensions([{{2,16,840,1,113730,1,1}, <<3,2,6,192>>, true}]),
+ #{client_config := ClientOpts0,
+ server_config := ServerOpts0} = ssl_test_lib:make_cert_chains_der(proplists:get_value(cert_key_alg, Config),
+ [{server_chain,
+ [[],[],[{extensions, Ext}]]},
+ {client_chain, DefaultCertConf}]),
+ ClientOpts = ssl_test_lib:ssl_options(ClientOpts0, Config),
+ ServerOpts = ssl_test_lib:ssl_options(ServerOpts0, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server_error(
+ [{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {options, [{verify, verify_none} | 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, [{verify, verify_peer} | ClientOpts]}]),
+
+ ssl_test_lib:check_client_alert(Server, Client, unsupported_certificate).
+
+%%--------------------------------------------------------------------
+critical_extension_client_auth() ->
+ [{doc,"Test cert that has a critical unknown extension in verify_peer mode"}].
+
+critical_extension_client_auth(Config) when is_list(Config) ->
+ DefaultCertConf = ssl_test_lib:default_cert_chain_conf(),
+ Ext = x509_test:extensions([{{2,16,840,1,113730,1,1}, <<3,2,6,192>>, true}]),
+ #{client_config := ClientOpts0,
+ server_config := ServerOpts0} = ssl_test_lib:make_cert_chains_der(proplists:get_value(cert_key_alg, Config),
+ [{client_chain,
+ [[],[],[{extensions, Ext}]]},
+ {server_chain, DefaultCertConf}]),
+ ClientOpts = ssl_test_lib:ssl_options(ClientOpts0, Config),
+ ServerOpts = ssl_test_lib:ssl_options(ServerOpts0, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server_error(
+ [{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {options, [{verify, verify_peer} | 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, [{verify, verify_none} | ClientOpts]}]),
+
+ %% This certificate has a critical extension that we don't
+ %% understand. Therefore, verification should fail.
+ ssl_test_lib:check_server_alert(Server, Client, unsupported_certificate).
+
+%%--------------------------------------------------------------------
+critical_extension_no_auth() ->
+ [{doc,"Test cert that has a critical unknown extension in verify_none mode"}].
+
+critical_extension_no_auth(Config) when is_list(Config) ->
+ DefaultCertConf = ssl_test_lib:default_cert_chain_conf(),
+ Ext = x509_test:extensions([{{2,16,840,1,113730,1,1}, <<3,2,6,192>>, true}]),
+ #{client_config := ClientOpts0,
+ server_config := ServerOpts0} = ssl_test_lib:make_cert_chains_der(proplists:get_value(cert_key_alg, Config),
+ [{server_chain,
+ [[],[], [{extensions, Ext}]]},
+ {client_chain, DefaultCertConf}]),
+ ClientOpts = [{verify, verify_none} | ssl_test_lib:ssl_options(ClientOpts0, Config)],
+ ServerOpts = [{verify, verify_none} | ssl_test_lib:ssl_options(ServerOpts0, Config)],
+
+ ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config).
+
+
+%%--------------------------------------------------------------------
+extended_key_usage_auth() ->
+ [{doc,"Test cert that has a critical extended_key_usage extension in server cert"}].
+
+extended_key_usage_auth(Config) when is_list(Config) ->
+ DefaultCertConf = ssl_test_lib:default_cert_chain_conf(),
+ Ext = x509_test:extensions([{?'id-ce-extKeyUsage',
+ [?'id-kp-serverAuth'], true}]),
+ #{client_config := ClientOpts0,
+ server_config := ServerOpts0} = ssl_test_lib:make_cert_chains_der(proplists:get_value(cert_key_alg, Config),
+ [{server_chain,
+ [[],[], [{extensions, Ext}]]},
+ {client_chain, DefaultCertConf}
+ ]),
+ ClientOpts = ssl_test_lib:ssl_options(ClientOpts0, Config),
+ ServerOpts = ssl_test_lib:ssl_options(ServerOpts0, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, [{verify, verify_none} | 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, [{verify, verify_peer} |
+ ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+extended_key_usage_client_auth() ->
+ [{doc,"Test cert that has a critical extended_key_usage extension in client and server cert"}].
+
+extended_key_usage_client_auth(Config) when is_list(Config) ->
+ ServerExt = x509_test:extensions([{?'id-ce-extKeyUsage',
+ [?'id-kp-serverAuth'], true}]),
+ ClientExt = x509_test:extensions([{?'id-ce-extKeyUsage',
+ [?'id-kp-clientAuth'], true}]),
+ #{client_config := ClientOpts0,
+ server_config := ServerOpts0} = ssl_test_lib:make_cert_chains_der(proplists:get_value(cert_key_alg, Config),
+ [{client_chain, [[],[],[{extensions, ClientExt}]]},
+ {server_chain, [[],[],[{extensions, ServerExt}]]}]),
+ ClientOpts = ssl_test_lib:ssl_options(ClientOpts0, Config),
+ ServerOpts = ssl_test_lib:ssl_options(ServerOpts0, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, [{verify, verify_peer} | 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, [{verify, verify_peer} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+cert_expired() ->
+ [{doc,"Test server with expired certificate"}].
+
+cert_expired(Config) when is_list(Config) ->
+ DefaultCertConf = ssl_test_lib:default_cert_chain_conf(),
+ {Year, Month, Day} = date(),
+ #{client_config := ClientOpts0,
+ server_config := ServerOpts0} = ssl_test_lib:make_cert_chains_der(proplists:get_value(cert_key_alg, Config),
+ [{server_chain,
+ [[],
+ [{validity, {{Year-2, Month, Day},
+ {Year-1, Month, Day}}}],
+ []
+ ]},
+ {client_chain, DefaultCertConf}]),
+ ClientOpts = ssl_test_lib:ssl_options(ClientOpts0, Config),
+ ServerOpts = ssl_test_lib:ssl_options(ServerOpts0, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {options, [{verify, verify_peer} | ClientOpts]}]),
+
+ ssl_test_lib:check_client_alert(Server, Client, certificate_expired).
+
+%%--------------------------------------------------------------------
+client_auth_once() ->
+ [{doc,"Test server option verify_client_once"}].
+
+client_auth_once(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_cert_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_cert_opts, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, [{verify, verify_peer},
+ {verify_client_once, true}
+ | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client0 = 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, Client0, ok),
+ Server ! {listen, {mfa, {ssl_test_lib, send_recv_result_active, []}}},
+ ssl_test_lib:close(Client0),
+ Client1 = 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(Client1, ok, Server, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client1).
+
+%%--------------------------------------------------------------------
+no_auth_key_identifier_ext() ->
+ [{doc, "Test cert that does not have authorityKeyIdentifier extension"}].
+
+no_auth_key_identifier_ext(Config) when is_list(Config) ->
+ DefaultCertConf = ssl_test_lib:default_cert_chain_conf(),
+ #{client_config := ClientOpts0,
+ server_config := ServerOpts0} =
+ ssl_test_lib:make_cert_chains_der(proplists:get_value(cert_key_alg, Config),
+ [{client_chain, DefaultCertConf},
+ {server_chain, DefaultCertConf}]),
+ ClientOpts = [{verify, verify_peer} | ssl_test_lib:ssl_options(ClientOpts0, Config)],
+ ServerOpts = [{verify, verify_peer} | ssl_test_lib:ssl_options(ServerOpts0, Config)],
+
+ ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config).
+
+%%--------------------------------------------------------------------
+no_auth_key_identifier_ext_keyEncipherment() ->
+ [{doc, "Test cert with keyEncipherment key_usage an no"
+ " authorityKeyIdentifier extension"}].
+
+no_auth_key_identifier_ext_keyEncipherment(Config) when is_list(Config) ->
+ DefaultCertConf = ssl_test_lib:default_cert_chain_conf(),
+ ClientExt = x509_test:extensions([{key_usage, [digitalSignature, keyEncipherment]}]),
+ #{client_config := ClientOpts0,
+ server_config := ServerOpts0} =
+ ssl_test_lib:make_cert_chains_der(proplists:get_value(cert_key_alg, Config),
+ [{client_chain,
+ [[],[],[{extensions, ClientExt}]]},
+ {server_chain, DefaultCertConf}
+ ]),
+ ClientOpts = [{verify, verify_peer} | ssl_test_lib:ssl_options(ClientOpts0, Config)],
+ ServerOpts = [{verify, verify_peer} | ssl_test_lib:ssl_options(ServerOpts0, Config)],
+
+ ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config).
+
+%%--------------------------------------------------------------------
+key_auth_ext_sign_only() ->
+ [{doc, "Test that client with a certificate without keyEncipherment usage "
+ " extension can connect to a server with restricted cipher suites "}].
+key_auth_ext_sign_only(Config) when is_list(Config) ->
+ DefaultCertConf = ssl_test_lib:default_cert_chain_conf(),
+ ClientExt = x509_test:extensions([{key_usage, [digitalSignature]}]),
+ #{client_config := ClientOpts0,
+ server_config := ServerOpts0} =
+ ssl_test_lib:make_cert_chains_der(proplists:get_value(cert_key_alg, Config),
+ [{client_chain,
+ [[],[],[{extensions, ClientExt}]]},
+ {server_chain, DefaultCertConf}
+ ]),
+ Version = proplists:get_value(version, Config),
+ ClientOpts = [{verify, verify_peer} | ssl_test_lib:ssl_options(ClientOpts0, Config)],
+ ServerOpts = [{verify, verify_peer}, {ciphers,
+ ssl_test_lib:rsa_non_signed_suites(n_version(Version))}
+ | ssl_test_lib:ssl_options(ServerOpts0, Config)],
+
+ ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config).
+
+%%--------------------------------------------------------------------
+longer_chain() ->
+ [{doc,"Test depth option"}].
+longer_chain(Config) when is_list(Config) ->
+ #{server_config := ServerOpts0,
+ client_config := ClientOpts0} =
+ public_key:pkix_test_data(#{server_chain => #{root => [{key, ssl_test_lib:hardcode_rsa_key(1)}],
+ intermediates => [[{key, ssl_test_lib:hardcode_rsa_key(2)}],
+ [{key, ssl_test_lib:hardcode_rsa_key(3)}],
+ [{key, ssl_test_lib:hardcode_rsa_key(4)}]],
+ peer => [{key, ssl_test_lib:hardcode_rsa_key(5)}]},
+ client_chain => #{root => [{key, ssl_test_lib:hardcode_rsa_key(3)}],
+ intermediates => [[{key, ssl_test_lib:hardcode_rsa_key(2)}]],
+ peer => [{key, ssl_test_lib:hardcode_rsa_key(1)}]}}),
+ [ServerRoot| _] = ServerCas = proplists:get_value(cacerts, ServerOpts0),
+ ClientCas = proplists:get_value(cacerts, ClientOpts0),
+
+ ServerOpts = ssl_test_lib:ssl_options([{verify, verify_peer}, {cacerts, [ServerRoot]} |
+ proplists:delete(cacerts, ServerOpts0)], Config),
+ ClientOpts = ssl_test_lib:ssl_options([{verify, verify_peer},
+ {depth, 5},
+ {cacerts, ServerCas ++ ClientCas} |
+ proplists:delete(cacerts, ClientOpts0)], Config),
+ ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config).
+
+%%--------------------------------------------------------------------
+%% TLS 1.3 Test cases -----------------------------------------------
+%%--------------------------------------------------------------------
+hello_retry_request() ->
+ [{doc,"Test that ssl server can request a new group when the client's first key share"
+ "is not supported"}].
+
+hello_retry_request(Config) ->
+ ClientOpts0 = ssl_test_lib:ssl_options(client_cert_opts, Config),
+ ServerOpts0 = ssl_test_lib:ssl_options(server_cert_opts, Config),
+ ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']},
+ {supported_groups, [x448, x25519]}|ServerOpts0],
+ ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']},
+ {supported_groups, [secp256r1, x25519]} | ClientOpts0],
+ ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config).
+%%--------------------------------------------------------------------
+custom_groups() ->
+ [{doc,"Test that ssl server can select a common group for key-exchange"}].
+
+custom_groups(Config) ->
+ ClientOpts0 = ssl_test_lib:ssl_options(client_cert_opts, Config),
+ ServerOpts0 = ssl_test_lib:ssl_options(server_cert_opts, Config),
+
+ %% Set versions
+ ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']},
+ {supported_groups, [x448, secp256r1, secp384r1]}|ServerOpts0],
+ ClientOpts1 = [{versions, ['tlsv1.2','tlsv1.3']}|ClientOpts0],
+ ClientOpts = [{supported_groups,[secp384r1, secp256r1, x25519]}|ClientOpts1],
+ ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config).
+
+%%--------------------------------------------------------------------
+%% Triggers a Server Alert as ssl client does not have a certificate with a
+%% signature algorithm supported by the server (signature_algorithms_cert extension
+%% of CertificateRequest does not contain the algorithm of the client certificate).
+%% ssl client sends an empty certificate.
+unsupported_sign_algo_cert_client_auth() ->
+ [{doc,"TLS 1.3: Test client authentication with unsupported signature_algorithm_cert"}].
+
+unsupported_sign_algo_cert_client_auth(Config) ->
+ ClientOpts0 = ssl_test_lib:ssl_options(client_cert_opts, Config),
+ ServerOpts0 = ssl_test_lib:ssl_options(server_cert_opts, Config),
+ ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']},
+ {verify, verify_peer},
+ {signature_algs, [rsa_pkcs1_sha256, rsa_pkcs1_sha384, rsa_pss_rsae_sha256]},
+ %% Skip rsa_pkcs1_sha256!
+ {signature_algs_cert, [rsa_pkcs1_sha384, rsa_pkcs1_sha512]},
+ {fail_if_no_peer_cert, true}|ServerOpts0],
+ ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}|ClientOpts0],
+ ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, certificate_required).
+
+%%--------------------------------------------------------------------
+unsupported_sign_algo_client_auth() ->
+ [{doc,"TLS 1.3: Test client authentication with unsupported signature_algorithm"}].
+
+unsupported_sign_algo_client_auth(Config) ->
+ ClientOpts0 = ssl_test_lib:ssl_options(client_cert_opts, Config),
+ ServerOpts0 = ssl_test_lib:ssl_options(server_cert_opts, Config),
+ ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']},
+ {verify, verify_peer},
+ %% Skip rsa_pkcs1_sha256!
+ {signature_algs, [rsa_pkcs1_sha384, rsa_pkcs1_sha512]},
+ {fail_if_no_peer_cert, true}|ServerOpts0],
+ ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}|ClientOpts0],
+ ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, insufficient_security).
+%%--------------------------------------------------------------------
+hello_retry_client_auth() ->
+ [{doc, "TLS 1.3 (HelloRetryRequest): Test client authentication."}].
+
+hello_retry_client_auth(Config) ->
+ ClientOpts0 = ssl_test_lib:ssl_options(client_cert_opts, Config),
+ ServerOpts0 = ssl_test_lib:ssl_options(server_cert_opts, Config),
+ ServerOpts1 = [{versions, ['tlsv1.2','tlsv1.3']},
+ {supported_groups, [x448, x25519]}|ServerOpts0],
+ ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']},
+ {supported_groups, [secp256r1, x25519]}|ClientOpts0],
+ ServerOpts = [{verify, verify_peer},
+ {fail_if_no_peer_cert, true} | ServerOpts1],
+
+ ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config).
+%%--------------------------------------------------------------------
+hello_retry_client_auth_empty_cert_accepted() ->
+ [{doc,"TLS 1.3 (HelloRetryRequest): Test client authentication when client sends an empty "
+ "certificate and fail_if_no_peer_cert is set to true."}].
+
+hello_retry_client_auth_empty_cert_accepted(Config) ->
+ ClientOpts0 = ssl_test_lib:ssl_options(client_cert_opts, Config),
+ %% Delete Client Cert and Key
+ ClientOpts1 = proplists:delete(certfile, ClientOpts0),
+ ClientOpts2 = proplists:delete(keyfile, ClientOpts1),
+
+ ServerOpts0 = ssl_test_lib:ssl_options(server_cert_opts, Config),
+ %% Set versions
+ ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']},
+ {verify, verify_peer},
+ {fail_if_no_peer_cert, false},
+ {supported_groups, [x448, x25519]}|ServerOpts0],
+ ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']},
+ {supported_groups, [secp256r1, x25519]}|ClientOpts2],
+ ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config).
+%%--------------------------------------------------------------------
+hello_retry_client_auth_empty_cert_rejected() ->
+ [{doc,"TLS 1.3 (HelloRetryRequest): Test client authentication when client "
+ "sends an empty certificate and fail_if_no_peer_cert is set to true."}].
+
+hello_retry_client_auth_empty_cert_rejected(Config) ->
+ ClientOpts0 = ssl_test_lib:ssl_options(client_cert_opts, Config),
+ %% Delete Client Cert and Key
+ ClientOpts1 = proplists:delete(certfile, ClientOpts0),
+ ClientOpts2 = proplists:delete(keyfile, ClientOpts1),
+
+ ServerOpts0 = ssl_test_lib:ssl_options(server_cert_opts, Config),
+ %% Set versions
+ ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']},
+ {verify, verify_peer},
+ {fail_if_no_peer_cert, true},
+ {supported_groups, [x448, x25519]}|ServerOpts0],
+ ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']},
+ {supported_groups, [secp256r1, x25519]}|ClientOpts2],
+
+ ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, certificate_required).
+
+%%--------------------------------------------------------------------
+%% Internal functions -----------------------------------------------
+%%--------------------------------------------------------------------
+two_digits_str(N) when N < 10 ->
+ lists:flatten(io_lib:format("0~p", [N]));
+two_digits_str(N) ->
+ lists:flatten(io_lib:format("~p", [N])).
+
+delete_authority_key_extension([], Acc) ->
+ lists:reverse(Acc);
+delete_authority_key_extension([#'Extension'{extnID = ?'id-ce-authorityKeyIdentifier'} | Rest],
+ Acc) ->
+ delete_authority_key_extension(Rest, Acc);
+delete_authority_key_extension([Head | Rest], Acc) ->
+ delete_authority_key_extension(Rest, [Head | Acc]).
+
+n_version(Version) when Version == 'tlsv1.2';
+ Version == 'tlsv1.1';
+ Version == 'tlsv1';
+ Version == 'sslv3'
+ ->
+ tls_record:protocol_version(Version);
+n_version(Version) when Version == 'dtlsv1.2';
+ Version == 'dtlsv1' ->
+ dtls_record:protocol_version(Version).
diff --git a/lib/ssl/test/ssl_cert_tests.erl b/lib/ssl/test/ssl_cert_tests.erl
new file mode 100644
index 0000000000..c88daa2185
--- /dev/null
+++ b/lib/ssl/test/ssl_cert_tests.erl
@@ -0,0 +1,386 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2019-2019. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(ssl_cert_tests).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include_lib("public_key/include/public_key.hrl").
+
+%%--------------------------------------------------------------------
+%% Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
+
+no_auth() ->
+ [{doc,"Test connection without authentication"}].
+
+no_auth(Config) ->
+ ClientOpts = [{verify, verify_none} | ssl_test_lib:ssl_options(client_cert_opts, Config)],
+ ServerOpts = [{verify, verify_none} | ssl_test_lib:ssl_options(server_cert_opts, Config)],
+
+ ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config).
+%%--------------------------------------------------------------------
+auth() ->
+ [{doc,"Test connection with mutual authentication"}].
+
+auth(Config) ->
+ ClientOpts = [{verify, verify_peer} | ssl_test_lib:ssl_options(client_cert_opts, Config)],
+ ServerOpts = [{verify, verify_peer} | ssl_test_lib:ssl_options(server_cert_opts, Config)],
+
+ ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config).
+
+%%--------------------------------------------------------------------
+client_auth_empty_cert_accepted() ->
+ [{doc,"Test client authentication when client sends an empty certificate and "
+ "fail_if_no_peer_cert is set to false."}].
+
+client_auth_empty_cert_accepted(Config) ->
+ ClientOpts = proplists:delete(keyfile,
+ proplists:delete(certfile,
+ ssl_test_lib:ssl_options(client_cert_opts, Config))),
+ ServerOpts0 = ssl_test_lib:ssl_options(server_cert_opts, Config),
+ ServerOpts = [{verify, verify_peer},
+ {fail_if_no_peer_cert, false} | ServerOpts0],
+ ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config).
+%%--------------------------------------------------------------------
+client_auth_empty_cert_rejected() ->
+ [{doc,"Test client authentication when client sends an empty certificate and "
+ "fail_if_no_peer_cert is set to true."}].
+
+client_auth_empty_cert_rejected(Config) ->
+ ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true}
+ | ssl_test_lib:ssl_options(server_cert_opts, Config)],
+ ClientOpts0 = ssl_test_lib:ssl_options([], Config),
+ %% Delete Client Cert and Key
+ ClientOpts1 = proplists:delete(certfile, ClientOpts0),
+ ClientOpts = proplists:delete(keyfile, ClientOpts1),
+
+ Version = proplists:get_value(version,Config),
+ case Version of
+ 'tlsv1.3' ->
+ ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, certificate_required);
+ _ ->
+ ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, handshake_failure)
+ end.
+%%--------------------------------------------------------------------
+client_auth_partial_chain() ->
+ [{doc, "Client sends an incompleate chain, by default not acceptable."}].
+
+client_auth_partial_chain(Config) when is_list(Config) ->
+ ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true}
+ | ssl_test_lib:ssl_options(server_cert_opts, Config)],
+ ClientOpts0 = ssl_test_lib:ssl_options(client_cert_opts, Config),
+ {ok, ClientCAs} = file:read_file(proplists:get_value(cacertfile, ClientOpts0)),
+ [{_,RootCA,_} | _] = public_key:pem_decode(ClientCAs),
+ ClientOpts = [{cacerts, [RootCA]} |
+ proplists:delete(cacertfile, ClientOpts0)],
+ ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, unknown_ca).
+
+%%--------------------------------------------------------------------
+client_auth_allow_partial_chain() ->
+ [{doc, "Server trusts intermediat CA and accepts a partial chain. (partial_chain option)"}].
+
+client_auth_allow_partial_chain(Config) when is_list(Config) ->
+ ServerOpts0 = [{verify, verify_peer}, {fail_if_no_peer_cert, true}
+ | ssl_test_lib:ssl_options(server_cert_opts, Config)],
+ ClientOpts = ssl_test_lib:ssl_options(client_cert_opts, Config),
+ {ok, ClientCAs} = file:read_file(proplists:get_value(cacertfile, ClientOpts)),
+ [{_,_,_}, {_, IntermidiateCA, _} | _] = public_key:pem_decode(ClientCAs),
+
+ PartialChain = fun(CertChain) ->
+ case lists:member(IntermidiateCA, CertChain) of
+ true ->
+ {trusted_ca, IntermidiateCA};
+ false ->
+ unknown_ca
+ end
+ end,
+ ServerOpts = [{cacerts, [IntermidiateCA]},
+ {partial_chain, PartialChain} |
+ proplists:delete(cacertfile, ServerOpts0)],
+
+ ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config).
+
+ %%--------------------------------------------------------------------
+client_auth_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)"}].
+
+client_auth_do_not_allow_partial_chain(Config) when is_list(Config) ->
+ ServerOpts0 = [{verify, verify_peer}, {fail_if_no_peer_cert, true}
+ | ssl_test_lib:ssl_options(server_cert_opts, Config)],
+ ClientOpts = ssl_test_lib:ssl_options(client_cert_opts, Config),
+ {ok, ServerCAs} = file:read_file(proplists:get_value(cacertfile, ServerOpts0)),
+ [{_,_,_}, {_, IntermidiateCA, _} | _] = public_key:pem_decode(ServerCAs),
+
+ PartialChain = fun(_CertChain) ->
+ unknown_ca
+ end,
+ ServerOpts = [{cacerts, [IntermidiateCA]},
+ {partial_chain, PartialChain} |
+ proplists:delete(cacertfile, ServerOpts0)],
+ ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, unknown_ca).
+
+ %%--------------------------------------------------------------------
+client_auth_partial_chain_fun_fail() ->
+ [{doc, "If parial_chain fun crashes, treat it as if it returned unkown_ca"}].
+
+client_auth_partial_chain_fun_fail(Config) when is_list(Config) ->
+ ServerOpts0 = [{verify, verify_peer}, {fail_if_no_peer_cert, true}
+ | ssl_test_lib:ssl_options(server_cert_opts, Config)],
+ ClientOpts = ssl_test_lib:ssl_options(client_cert_opts, Config),
+
+ {ok, ServerCAs} = file:read_file(proplists:get_value(cacertfile, ServerOpts0)),
+ [{_,_,_}, {_, IntermidiateCA, _} | _] = public_key:pem_decode(ServerCAs),
+
+ PartialChain = fun(_CertChain) ->
+ true = false %% crash on purpose
+ end,
+ ServerOpts = [{cacerts, [IntermidiateCA]},
+ {partial_chain, PartialChain} |
+ proplists:delete(cacertfile, ServerOpts0)],
+
+ ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, unknown_ca).
+
+%%--------------------------------------------------------------------
+missing_root_cert_no_auth() ->
+ [{doc,"Test that the client succeds if the ROOT CA is unknown in verify_none mode"}].
+
+missing_root_cert_no_auth(Config) ->
+ ClientOpts = [{verify, verify_none} | ssl_test_lib:ssl_options(client_cert_opts, Config)],
+ ServerOpts = [{verify, verify_none} | ssl_test_lib:ssl_options(server_cert_opts, Config)],
+
+ ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config).
+
+%%--------------------------------------------------------------------
+invalid_signature_client() ->
+ [{doc,"Test server with invalid signature"}].
+
+invalid_signature_client(Config) when is_list(Config) ->
+ ClientOpts0 = ssl_test_lib:ssl_options(client_cert_opts, Config),
+ ServerOpts0 = ssl_test_lib:ssl_options(server_cert_opts, Config),
+ PrivDir = proplists:get_value(priv_dir, Config),
+
+ KeyFile = proplists:get_value(keyfile, ClientOpts0),
+ [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile),
+ Key = ssl_test_lib:public_key(public_key:pem_entry_decode(KeyEntry)),
+
+ ClientCertFile = proplists:get_value(certfile, ClientOpts0),
+ NewClientCertFile = filename:join(PrivDir, "client_invalid_cert.pem"),
+ [{'Certificate', ClientDerCert, _}] = ssl_test_lib:pem_to_der(ClientCertFile),
+ ClientOTPCert = public_key:pkix_decode_cert(ClientDerCert, otp),
+ ClientOTPTbsCert = ClientOTPCert#'OTPCertificate'.tbsCertificate,
+ NewClientDerCert = public_key:pkix_sign(ClientOTPTbsCert, Key),
+ ssl_test_lib:der_to_pem(NewClientCertFile, [{'Certificate', NewClientDerCert, not_encrypted}]),
+ ClientOpts = [{certfile, NewClientCertFile} | proplists:delete(certfile, ClientOpts0)],
+ ServerOpts = [{verify, verify_peer} | ServerOpts0],
+ ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, unknown_ca).
+
+%%--------------------------------------------------------------------
+invalid_signature_server() ->
+ [{doc,"Test client with invalid signature"}].
+
+invalid_signature_server(Config) when is_list(Config) ->
+ ClientOpts0 = ssl_test_lib:ssl_options(client_cert_opts, Config),
+ ServerOpts0 = ssl_test_lib:ssl_options(server_cert_opts, Config),
+ PrivDir = proplists:get_value(priv_dir, Config),
+
+ KeyFile = proplists:get_value(keyfile, ServerOpts0),
+ [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile),
+ Key = ssl_test_lib:public_key(public_key:pem_entry_decode(KeyEntry)),
+
+ ServerCertFile = proplists:get_value(certfile, ServerOpts0),
+ NewServerCertFile = filename:join(PrivDir, "server_invalid_cert.pem"),
+ [{'Certificate', ServerDerCert, _}] = ssl_test_lib:pem_to_der(ServerCertFile),
+ ServerOTPCert = public_key:pkix_decode_cert(ServerDerCert, otp),
+ ServerOTPTbsCert = ServerOTPCert#'OTPCertificate'.tbsCertificate,
+ NewServerDerCert = public_key:pkix_sign(ServerOTPTbsCert, Key),
+ ssl_test_lib:der_to_pem(NewServerCertFile, [{'Certificate', NewServerDerCert, not_encrypted}]),
+ ServerOpts = [{certfile, NewServerCertFile} | proplists:delete(certfile, ServerOpts0)],
+ ClientOpts = [{verify, verify_peer} | ClientOpts0],
+ ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, unknown_ca).
+
+%%--------------------------------------------------------------------
+%% TLS 1.3 Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
+hello_retry_request() ->
+ [{doc,"Test that ssl server can request a new group when the client's first key share"
+ "is not supported"}].
+
+hello_retry_request(Config) ->
+ ClientOpts0 = ssl_test_lib:ssl_options(client_cert_opts, Config),
+ ServerOpts0 = ssl_test_lib:ssl_options(server_cert_opts, Config),
+
+ {ServerOpts, ClientOpts} = group_config(Config,
+ [{versions, ['tlsv1.2','tlsv1.3']} | ServerOpts0],
+ [{versions, ['tlsv1.2','tlsv1.3']} | ClientOpts0]),
+
+ ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config).
+%%--------------------------------------------------------------------
+custom_groups() ->
+ [{doc,"Test that ssl server can select a common group for key-exchange"}].
+
+custom_groups(Config) ->
+ ClientOpts0 = ssl_test_lib:ssl_options(client_cert_opts, Config),
+ ServerOpts0 = ssl_test_lib:ssl_options(server_cert_opts, Config),
+
+ {ServerOpts, ClientOpts} = group_config_custom(Config,
+ [{versions, ['tlsv1.2','tlsv1.3']} | ServerOpts0],
+ [{versions, ['tlsv1.2','tlsv1.3']} | ClientOpts0]),
+
+ ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config).
+
+%%--------------------------------------------------------------------
+%% Triggers a Server Alert as ssl client does not have a certificate with a
+%% signature algorithm supported by the server (signature_algorithms_cert extension
+%% of CertificateRequest does not contain the algorithm of the client certificate).
+%% ssl client sends an empty certificate.
+unsupported_sign_algo_cert_client_auth() ->
+ [{doc,"TLS 1.3: Test client authentication with unsupported signature_algorithm_cert"}].
+
+unsupported_sign_algo_cert_client_auth(Config) ->
+ ClientOpts0 = ssl_test_lib:ssl_options(client_cert_opts, Config),
+ ServerOpts0 = ssl_test_lib:ssl_options(server_cert_opts, Config),
+ ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']},
+ {verify, verify_peer},
+ {signature_algs, [rsa_pkcs1_sha256, rsa_pkcs1_sha384, rsa_pss_rsae_sha256]},
+ %% Skip rsa_pkcs1_sha256!
+ {signature_algs_cert, [rsa_pkcs1_sha384, rsa_pkcs1_sha512]},
+ {fail_if_no_peer_cert, true}|ServerOpts0],
+ ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}|ClientOpts0],
+ ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, certificate_required).
+%%--------------------------------------------------------------------
+unsupported_sign_algo_client_auth() ->
+ [{doc,"TLS 1.3: Test client authentication with unsupported signature_algorithm"}].
+
+unsupported_sign_algo_client_auth(Config) ->
+ ClientOpts0 = ssl_test_lib:ssl_options(client_cert_opts, Config),
+ ServerOpts0 = ssl_test_lib:ssl_options(server_cert_opts, Config),
+ ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']},
+ {verify, verify_peer},
+ %% Skip rsa_pkcs1_sha256!
+ {signature_algs, [rsa_pkcs1_sha384, rsa_pkcs1_sha512]},
+ {fail_if_no_peer_cert, true}|ServerOpts0],
+ ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}|ClientOpts0],
+ ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, insufficient_security).
+%%--------------------------------------------------------------------
+hello_retry_client_auth() ->
+ [{doc, "TLS 1.3 (HelloRetryRequest): Test client authentication."}].
+
+hello_retry_client_auth(Config) ->
+ ClientOpts0 = ssl_test_lib:ssl_options(client_cert_opts, Config),
+ ServerOpts0 = ssl_test_lib:ssl_options(server_cert_opts, Config),
+
+ {ServerOpts, ClientOpts} = group_config(Config,
+ [{versions, ['tlsv1.2','tlsv1.3']},
+ {verify, verify_peer},
+ {fail_if_no_peer_cert, true} | ServerOpts0],
+ [{versions, ['tlsv1.2','tlsv1.3']}, {verify, verify_peer} | ClientOpts0]),
+
+ ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config).
+%%--------------------------------------------------------------------
+hello_retry_client_auth_empty_cert_accepted() ->
+ [{doc,"TLS 1.3 (HelloRetryRequest): Test client authentication when client sends an empty "
+ "certificate and fail_if_no_peer_cert is set to false."}].
+
+hello_retry_client_auth_empty_cert_accepted(Config) ->
+ ClientOpts0 = proplists:delete(keyfile,
+ proplists:delete(certfile,
+ ssl_test_lib:ssl_options(client_cert_opts, Config))),
+ ServerOpts0 = ssl_test_lib:ssl_options(server_cert_opts, Config),
+
+ {ServerOpts, ClientOpts} = group_config(Config,
+ [{versions, ['tlsv1.2','tlsv1.3']},
+ {verify, verify_peer},
+ {fail_if_no_peer_cert, false} | ServerOpts0],
+ [{versions, ['tlsv1.2','tlsv1.3']}, {verify, verify_peer} | ClientOpts0]),
+
+ ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config).
+%%--------------------------------------------------------------------
+hello_retry_client_auth_empty_cert_rejected() ->
+ [{doc,"TLS 1.3 (HelloRetryRequest): Test client authentication when client "
+ "sends an empty certificate and fail_if_no_peer_cert is set to true."}].
+
+hello_retry_client_auth_empty_cert_rejected(Config) ->
+ ClientOpts0 = proplists:delete(keyfile,
+ proplists:delete(certfile,
+ ssl_test_lib:ssl_options(client_cert_opts, Config))),
+ ServerOpts0 = ssl_test_lib:ssl_options(server_cert_opts, Config),
+
+ {ServerOpts, ClientOpts} = group_config(Config,
+ [{versions, ['tlsv1.2','tlsv1.3']},
+ {verify, verify_peer},
+ {fail_if_no_peer_cert, true} | ServerOpts0],
+ [{versions, ['tlsv1.2','tlsv1.3']}, {verify, verify_peer} | ClientOpts0]),
+
+ ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, certificate_required).
+
+
+%%--------------------------------------------------------------------
+%% Internal functions -----------------------------------------------
+%%--------------------------------------------------------------------
+
+group_config_custom(Config, ServerOpts, ClientOpts) ->
+ case proplists:get_value(client_type, Config) of
+ erlang ->
+ {[{groups,"X448:P-256:P-384"} | ServerOpts],
+ [{supported_groups, [secp384r1, secp256r1, x25519]} | ClientOpts]};
+ openssl ->
+ {[{supported_groups, [x448, secp256r1, secp384r1]} | ServerOpts],
+ [{groups,"P-384:P-256:X25519"} | ClientOpts]}
+ end.
+
+group_config(Config, ServerOpts, ClientOpts) ->
+ case proplists:get_value(client_type, Config) of
+ erlang ->
+ {[{groups,"X448:X25519"} | ServerOpts],
+ [{supported_groups, [secp256r1, x25519]} | ClientOpts]};
+ openssl ->
+ {[{supported_groups, [x448, x25519]} | ServerOpts],
+ [{groups,"P-256:X25519"} | ClientOpts]}
+ end.
+
+test_ciphers(_, 'tlsv1.3' = Version) ->
+ Ciphers = ssl:cipher_suites(default, Version),
+ ct:log("Version ~p Testing ~p~n", [Version, Ciphers]),
+ OpenSSLCiphers = openssl_ciphers(),
+ ct:log("OpenSSLCiphers ~p~n", [OpenSSLCiphers]),
+ lists:filter(fun(C) ->
+ ct:log("Cipher ~p~n", [C]),
+ lists:member(ssl_cipher_format:suite_map_to_openssl_str(C), OpenSSLCiphers)
+ end, Ciphers);
+test_ciphers(Kex, Version) ->
+ Ciphers = ssl:filter_cipher_suites(ssl:cipher_suites(default, Version),
+ [{key_exchange, Kex}]),
+ ct:log("Version ~p Testing ~p~n", [Version, Ciphers]),
+ OpenSSLCiphers = openssl_ciphers(),
+ ct:log("OpenSSLCiphers ~p~n", [OpenSSLCiphers]),
+ lists:filter(fun(C) ->
+ ct:log("Cipher ~p~n", [C]),
+ lists:member(ssl_cipher_format:suite_map_to_openssl_str(C), OpenSSLCiphers)
+ end, Ciphers).
+
+
+
+openssl_ciphers() ->
+ Str = os:cmd("openssl ciphers"),
+ string:split(string:strip(Str, right, $\n), ":", all).
diff --git a/lib/ssl/test/ssl_certificate_verify_SUITE.erl b/lib/ssl/test/ssl_certificate_verify_SUITE.erl
deleted file mode 100644
index 4de4a35e59..0000000000
--- a/lib/ssl/test/ssl_certificate_verify_SUITE.erl
+++ /dev/null
@@ -1,1212 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2012-2018. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
-
-%%
--module(ssl_certificate_verify_SUITE).
-
-%% Note: This directive should only be used in test suites.
--compile(export_all).
-
--include_lib("common_test/include/ct.hrl").
--include_lib("public_key/include/public_key.hrl").
-
--include("ssl_internal.hrl").
--include("ssl_alert.hrl").
--include("ssl_internal.hrl").
--include("tls_record.hrl").
--include("tls_handshake.hrl").
-
--define(LONG_TIMEOUT, 600000).
-
-%%--------------------------------------------------------------------
-%% Common Test interface functions -----------------------------------
-%%--------------------------------------------------------------------
-all() ->
- [
- {group, 'tlsv1.3'},
- {group, 'tlsv1.2'},
- {group, 'tlsv1.1'},
- {group, 'tlsv1'},
- {group, 'sslv3'},
- {group, 'dtlsv1.2'},
- {group, 'dtlsv1'}
- ].
-
-groups() ->
- [
- {'tlsv1.3', [], all_protocol_groups()},
- {'tlsv1.2', [], all_protocol_groups()},
- {'tlsv1.1', [], all_protocol_groups()},
- {'tlsv1', [], all_protocol_groups()},
- {'sslv3', [], all_protocol_groups()},
- {'dtlsv1.2', [], all_protocol_groups()},
- {'dtlsv1', [], all_protocol_groups()},
- {active, [], tests()},
- {active_once, [], tests()},
- {passive, [], tests()},
- {error_handling, [],error_handling_tests()}
- ].
-
-all_protocol_groups() ->
- [{group, active},
- {group, passive},
- {group, active_once},
- {group, error_handling}].
-
-tests() ->
- [verify_peer,
- verify_none,
- server_require_peer_cert_ok,
- server_require_peer_cert_fail,
- server_require_peer_cert_empty_ok,
- 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,
- invalid_signature_client,
- invalid_signature_server,
- extended_key_usage_verify_both,
- extended_key_usage_verify_server,
- critical_extension_verify_client,
- critical_extension_verify_server,
- critical_extension_verify_none,
- customize_hostname_check,
- incomplete_chain,
- long_chain
- ].
-
-error_handling_tests()->
- [client_with_cert_cipher_suites_handshake,
- server_verify_no_cacerts,
- unknown_server_ca_fail,
- unknown_server_ca_accept_verify_none,
- unknown_server_ca_accept_verify_peer,
- unknown_server_ca_accept_backwardscompatibility,
- no_authority_key_identifier,
- no_authority_key_identifier_keyEncipherment].
-
-init_per_suite(Config) ->
- catch crypto:stop(),
- try crypto:start() of
- ok ->
- ssl_test_lib:clean_start(),
- ssl_test_lib:make_rsa_cert(Config)
- catch _:_ ->
- {skip, "Crypto did not start"}
- end.
-
-end_per_suite(_Config) ->
- ssl:stop(),
- application:stop(crypto).
-
-init_per_group(active, Config) ->
- [{active, true}, {receive_function, send_recv_result_active} | Config];
-init_per_group(active_once, Config) ->
- [{active, once}, {receive_function, send_recv_result_active_once} | Config];
-init_per_group(passive, Config) ->
- [{active, false}, {receive_function, send_recv_result} | Config];
-init_per_group(error_handling, Config) ->
- [{active, false}, {receive_function, send_recv_result} | Config];
-init_per_group(GroupName, Config) ->
- case ssl_test_lib:is_tls_version(GroupName) of
- true ->
- case ssl_test_lib:sufficient_crypto_support(GroupName) of
- true ->
- [{version, GroupName} | ssl_test_lib:init_tls_version(GroupName, Config)];
- false ->
- {skip, "Missing crypto support"}
- end
- end.
-
-end_per_group(GroupName, Config) ->
- case ssl_test_lib:is_tls_version(GroupName) of
- true ->
- ssl_test_lib:clean_tls_version(Config);
- false ->
- Config
- end.
-
-init_per_testcase(_TestCase, Config) ->
- ssl:stop(),
- ssl:start(),
- ssl_test_lib:ct_log_supported_protocol_versions(Config),
- ct:pal(" ~p", [ dtls_record:supported_protocol_versions()]),
- ct:timetrap({seconds, 10}),
- Config.
-
-end_per_testcase(_TestCase, Config) ->
- Config.
-
-%%--------------------------------------------------------------------
-%% Test Cases --------------------------------------------------------
-%%--------------------------------------------------------------------
-
-verify_peer() ->
- [{doc,"Test option verify_peer"}].
-verify_peer(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
- Active = proplists:get_value(active, Config),
- ReceiveFunction = proplists:get_value(receive_function, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, ReceiveFunction, []}},
- {options, [{active, Active}, {verify, verify_peer}
- | 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, ReceiveFunction, []}},
- {options, [{active, Active}, {verify, verify_peer} | ClientOpts]}]),
-
- ssl_test_lib:check_result(Server, ok, Client, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-%%--------------------------------------------------------------------
-verify_none() ->
- [{doc,"Test option verify_none"}].
-
-verify_none(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
- Active = proplists:get_value(active, Config),
- ReceiveFunction = proplists:get_value(receive_function, Config),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, ReceiveFunction, []}},
- {options, [{active, Active}, {verify, verify_none}
- | 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, ReceiveFunction, []}},
- {options, [{active, Active},
- {verify, verify_none} | ClientOpts]}]),
-
- ssl_test_lib:check_result(Server, ok, Client, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-%%--------------------------------------------------------------------
-
-server_verify_client_once() ->
- [{doc,"Test server option verify_client_once"}].
-
-server_verify_client_once(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, []),
- ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
- Active = proplists:get_value(active, Config),
- ReceiveFunction = proplists:get_value(receive_function, Config),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, ReceiveFunction, []}},
- {options, [{active, Active}, {verify, verify_peer},
- {verify_client_once, true}
- | ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
- Client0 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {ssl_test_lib, ReceiveFunction, []}},
- {options, [{active, Active} | ClientOpts]}]),
-
- ssl_test_lib:check_result(Server, ok, Client0, ok),
- Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}},
- ssl_test_lib:close(Client0),
- Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, result_ok, []}},
- {options, [{active, Active} | ClientOpts]}]),
-
- ssl_test_lib:check_result(Client1, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client1).
-
-%%--------------------------------------------------------------------
-
-server_require_peer_cert_ok() ->
- [{doc,"Test server option fail_if_no_peer_cert when peer sends cert"}].
-
-server_require_peer_cert_ok(Config) when is_list(Config) ->
- ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true}
- | ssl_test_lib:ssl_options(server_rsa_opts, Config)],
- ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
- Active = proplists:get_value(active, Config),
- ReceiveFunction = proplists:get_value(receive_function, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, ReceiveFunction, []}},
- {options, [{active, Active} | 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, ReceiveFunction, []}},
- {options, [{active, Active} | ClientOpts]}]),
-
- ssl_test_lib:check_result(Server, ok, Client, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-%%--------------------------------------------------------------------
-
-server_require_peer_cert_fail() ->
- [{doc,"Test server option fail_if_no_peer_cert when peer doesn't send cert"}].
-
-server_require_peer_cert_fail(Config) when is_list(Config) ->
- ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true}
- | ssl_test_lib:ssl_options(server_rsa_opts, Config)],
- BadClientOpts = ssl_test_lib:ssl_options(empty_client_opts, Config),
- Active = proplists:get_value(active, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
- {from, self()},
- {options, [{active, Active} | ServerOpts]}]),
-
- Port = ssl_test_lib:inet_port(Server),
-
- Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {options, [{active, Active} | BadClientOpts]}]),
-
- Version = proplists:get_value(version,Config),
- case Version of
- 'tlsv1.3' ->
- ssl_test_lib:check_server_alert(Server, Client, certificate_required);
- _ ->
- ssl_test_lib:check_server_alert(Server, Client, handshake_failure)
- end.
-
-%%--------------------------------------------------------------------
-server_require_peer_cert_empty_ok() ->
- [{doc,"Test server option fail_if_no_peer_cert when peer sends cert"}].
-
-server_require_peer_cert_empty_ok(Config) when is_list(Config) ->
- ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, false}
- | ssl_test_lib:ssl_options(server_rsa_opts, Config)],
- ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config),
- Active = proplists:get_value(active, Config),
- ReceiveFunction = proplists:get_value(receive_function, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- ClientOpts = proplists:delete(keyfile, proplists:delete(certfile, ClientOpts0)),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, ReceiveFunction, []}},
- {options, [{active, Active} | 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, ReceiveFunction, []}},
- {options, [{active, Active} | ClientOpts]}]),
-
- ssl_test_lib:check_result(Server, ok, Client, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-%%--------------------------------------------------------------------
-
-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}
- | ssl_test_lib:ssl_options(server_rsa_opts, Config)],
- ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
- Active = proplists:get_value(active, 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, Active} | 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, Active},
- {cacerts, [RootCA]} |
- proplists:delete(cacertfile, ClientOpts)]}]),
- ssl_test_lib:check_server_alert(Server, Client, unknown_ca).
-
-%%--------------------------------------------------------------------
-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}
- | ssl_test_lib:ssl_options(server_rsa_opts, Config)],
- ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Active = proplists:get_value(active, Config),
- ReceiveFunction = proplists:get_value(receive_function, Config),
-
- {ok, ClientCAs} = file:read_file(proplists:get_value(cacertfile, ClientOpts)),
- [{_,_,_}, {_, IntermidiateCA, _} | _] = public_key:pem_decode(ClientCAs),
-
- 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, ReceiveFunction, []}},
- {options, [{active, Active},
- {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, ReceiveFunction, []}},
- {options, [{active, Active} | 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}
- | ssl_test_lib:ssl_options(server_rsa_opts, Config)],
- ClientOpts = ssl_test_lib:ssl_options(client_rsa_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}]),
- ssl_test_lib:check_server_alert(Server, Client, unknown_ca).
- %%--------------------------------------------------------------------
-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}
- | ssl_test_lib:ssl_options(server_rsa_opts, Config)],
- ClientOpts = ssl_test_lib:ssl_options(client_rsa_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) ->
- true = 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}]),
- ssl_test_lib:check_server_alert(Server, Client, unknown_ca).
-
-%%--------------------------------------------------------------------
-verify_fun_always_run_client() ->
- [{doc,"Verify that user verify_fun is always run (for valid and valid_peer not only unknown_extension)"}].
-
-verify_fun_always_run_client(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib,
- no_result, []}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
-
- %% If user verify fun is called correctly we fail the connection.
- %% otherwise we cannot tell this case apart form where we miss
- %% to call users verify fun
- FunAndState = {fun(_,{extension, _}, UserState) ->
- {unknown, UserState};
- (_, valid, [ChainLen]) ->
- {valid, [ChainLen + 1]};
- (_, valid_peer, [1]) ->
- {fail, "verify_fun_was_always_run"};
- (_, valid_peer, UserState) ->
- {valid, UserState}
- end, [0]},
-
- Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {ssl_test_lib,
- no_result, []}},
- {options,
- [{verify, verify_peer},
- {verify_fun, FunAndState}
- | ClientOpts]}]),
-
- ssl_test_lib:check_client_alert(Server, Client, handshake_failure).
-
-%%--------------------------------------------------------------------
-verify_fun_always_run_server() ->
- [{doc,"Verify that user verify_fun is always run (for valid and valid_peer not only unknown_extension)"}].
-verify_fun_always_run_server(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- %% If user verify fun is called correctly we fail the connection.
- %% otherwise we cannot tell this case apart form where we miss
- %% to call users verify fun
- FunAndState = {fun(_,{extension, _}, UserState) ->
- {unknown, UserState};
- (_, valid, [ChainLen]) ->
- {valid, [ChainLen + 1]};
- (_, valid_peer, [1]) ->
- {fail, "verify_fun_was_always_run"};
- (_, valid_peer, UserState) ->
- {valid, UserState}
- end, [0]},
-
- Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib,
- no_result, []}},
- {options,
- [{verify, verify_peer},
- {verify_fun, FunAndState} |
- 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}]),
-
- ssl_test_lib:check_client_alert(Server, Client, handshake_failure).
-%%--------------------------------------------------------------------
-
-cert_expired() ->
- [{doc,"Test server with expired certificate"}].
-
-cert_expired(Config) when is_list(Config) ->
- {Year, Month, Day} = date(),
- Active = proplists:get_value(active, Config),
- {ClientOpts0, ServerOpts0} = ssl_test_lib:make_rsa_cert_chains([{server_chain,
- [[],
- [{validity, {{Year-2, Month, Day},
- {Year-1, Month, Day}}}],
- []
- ]}],
- Config, "_expired"),
- ClientOpts = ssl_test_lib:ssl_options(ClientOpts0, Config),
- ServerOpts = ssl_test_lib:ssl_options(ServerOpts0, Config),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
- {from, self()},
- {options, [{active, Active}| ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {options, [{verify, verify_peer}, {active, Active} | ClientOpts]}]),
-
- ssl_test_lib:check_client_alert(Server, Client, certificate_expired).
-
-two_digits_str(N) when N < 10 ->
- lists:flatten(io_lib:format("0~p", [N]));
-two_digits_str(N) ->
- lists:flatten(io_lib:format("~p", [N])).
-
-%%--------------------------------------------------------------------
-extended_key_usage_verify_server() ->
- [{doc,"Test cert that has a critical extended_key_usage extension in server cert"}].
-
-extended_key_usage_verify_server(Config) when is_list(Config) ->
- Ext = x509_test:extensions([{?'id-ce-extKeyUsage',
- [?'id-kp-serverAuth'], true}]),
- {ClientOpts0, ServerOpts0} = ssl_test_lib:make_rsa_cert_chains([{server_chain,
- [[],[], [{extensions, Ext}]]}], Config,
- "_keyusage_server"),
- ClientOpts = ssl_test_lib:ssl_options(ClientOpts0, Config),
- ServerOpts = ssl_test_lib:ssl_options(ServerOpts0, Config),
- Active = proplists:get_value(active, Config),
- ReceiveFunction = proplists:get_value(receive_function, Config),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, ReceiveFunction, []}},
- {options, [{verify, verify_none}, {active, Active} | 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, ReceiveFunction, []}},
- {options, [{verify, verify_peer}, {active, Active} |
- ClientOpts]}]),
-
- ssl_test_lib:check_result(Server, ok, Client, ok),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-%%--------------------------------------------------------------------
-extended_key_usage_verify_both() ->
- [{doc,"Test cert that has a critical extended_key_usage extension in client verify_peer mode"}].
-
-extended_key_usage_verify_both(Config) when is_list(Config) ->
- ServerExt = x509_test:extensions([{?'id-ce-extKeyUsage',
- [?'id-kp-serverAuth'], true}]),
- ClientExt = x509_test:extensions([{?'id-ce-extKeyUsage',
- [?'id-kp-clientAuth'], true}]),
- {ClientOpts0, ServerOpts0} = ssl_test_lib:make_rsa_cert_chains([{client_chain, [[],[],[{extensions, ClientExt}]]},
- {server_chain, [[],[],[{extensions, ServerExt}]]}],
- Config, "_keyusage_both"),
- ClientOpts = ssl_test_lib:ssl_options(ClientOpts0, Config),
- ServerOpts = ssl_test_lib:ssl_options(ServerOpts0, Config),
- Active = proplists:get_value(active, Config),
- ReceiveFunction = proplists:get_value(receive_function, Config),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, ReceiveFunction, []}},
- {options, [{verify, verify_peer}, {active, Active} | 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, ReceiveFunction, []}},
- {options, [{verify, verify_peer}, {active, Active} | ClientOpts]}]),
-
- ssl_test_lib:check_result(Server, ok, Client, ok),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-%%--------------------------------------------------------------------
-critical_extension_verify_server() ->
- [{doc,"Test cert that has a critical unknown extension in verify_peer mode"}].
-
-critical_extension_verify_server(Config) when is_list(Config) ->
- Ext = x509_test:extensions([{{2,16,840,1,113730,1,1}, <<3,2,6,192>>, true}]),
- {ClientOpts0, ServerOpts0} = ssl_test_lib:make_rsa_cert_chains([{client_chain,
- [[],[], [{extensions, Ext}]]}],
- Config, "_client_unknown_extension"),
- ClientOpts = ssl_test_lib:ssl_options(ClientOpts0, Config),
- ServerOpts = ssl_test_lib:ssl_options(ServerOpts0, Config),
- Active = proplists:get_value(active, Config),
- ReceiveFunction = proplists:get_value(receive_function, Config),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server_error(
- [{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, ReceiveFunction, []}},
- {options, [{verify, verify_peer}, {active, Active} | 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, ReceiveFunction, []}},
- {options, [{verify, verify_none}, {active, Active} | ClientOpts]}]),
-
- %% This certificate has a critical extension that we don't
- %% understand. Therefore, verification should fail.
- ssl_test_lib:check_server_alert(Server, Client, unsupported_certificate).
-%%--------------------------------------------------------------------
-
-critical_extension_verify_client() ->
- [{doc,"Test cert that has a critical unknown extension in verify_peer mode"}].
-
-critical_extension_verify_client(Config) when is_list(Config) ->
- Ext = x509_test:extensions([{{2,16,840,1,113730,1,1}, <<3,2,6,192>>, true}]),
- {ClientOpts0, ServerOpts0} = ssl_test_lib:make_rsa_cert_chains([{server_chain,
- [[],[],[{extensions, Ext}]]}],
- Config, "_server_unknown_extensions"),
- ClientOpts = ssl_test_lib:ssl_options(ClientOpts0, Config),
- ServerOpts = ssl_test_lib:ssl_options(ServerOpts0, Config),
- Active = proplists:get_value(active, Config),
- ReceiveFunction = proplists:get_value(receive_function, Config),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server_error(
- [{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, ReceiveFunction, []}},
- {options, [{verify, verify_none}, {active, Active} | 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, ReceiveFunction, []}},
- {options, [{verify, verify_peer}, {active, Active} | ClientOpts]}]),
-
- ssl_test_lib:check_client_alert(Server, Client, unsupported_certificate).
-
-%%--------------------------------------------------------------------
-critical_extension_verify_none() ->
- [{doc,"Test cert that has a critical unknown extension in verify_none mode"}].
-
-critical_extension_verify_none(Config) when is_list(Config) ->
- Ext = x509_test:extensions([{{2,16,840,1,113730,1,1}, <<3,2,6,192>>, true}]),
- {ClientOpts0, ServerOpts0} = ssl_test_lib:make_rsa_cert_chains([{server_chain,
- [[],[], [{extensions, Ext}]]}],
- Config, "_unknown_extensions"),
- ClientOpts = ssl_test_lib:ssl_options(ClientOpts0, Config),
- ServerOpts = ssl_test_lib:ssl_options(ServerOpts0, Config),
- Active = proplists:get_value(active, Config),
- ReceiveFunction = proplists:get_value(receive_function, Config),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server(
- [{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, ReceiveFunction, []}},
- {options, [{verify, verify_none}, {active, Active} | 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, ReceiveFunction, []}},
- {options, [{verify, verify_none}, {active, Active} | ClientOpts]}]),
-
- %% This certificate has a critical extension that we don't
- %% understand. But we're using `verify_none', so verification
- %% shouldn't fail.
- ssl_test_lib:check_result(Server, ok, Client, ok),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-%%--------------------------------------------------------------------
-no_authority_key_identifier() ->
- [{doc, "Test cert that does not have authorityKeyIdentifier extension"
- " but are present in trusted certs db."}].
-
-no_authority_key_identifier(Config) when is_list(Config) ->
- {ClientOpts0, ServerOpts0} = ssl_test_lib:make_rsa_cert_chains([], Config, "_peer_no_auth_key_id"),
- ClientOpts = ssl_test_lib:ssl_options(ClientOpts0, Config),
- ServerOpts = ssl_test_lib:ssl_options(ServerOpts0, Config),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, 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, [{verify, verify_peer} | ClientOpts]}]),
-
- ssl_test_lib:check_result(Server, ok, Client, ok),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-delete_authority_key_extension([], Acc) ->
- lists:reverse(Acc);
-delete_authority_key_extension([#'Extension'{extnID = ?'id-ce-authorityKeyIdentifier'} | Rest],
- Acc) ->
- delete_authority_key_extension(Rest, Acc);
-delete_authority_key_extension([Head | Rest], Acc) ->
- delete_authority_key_extension(Rest, [Head | Acc]).
-
-%%--------------------------------------------------------------------
-
-no_authority_key_identifier_keyEncipherment() ->
- [{doc, "Test cert with keyEncipherment key_usage an no"
- " authorityKeyIdentifier extension, but are present in trusted certs db."}].
-
-no_authority_key_identifier_keyEncipherment(Config) when is_list(Config) ->
- ClientExt = x509_test:extensions([{key_usage, [digitalSignature, keyEncipherment]}]),
- {ClientOpts0, ServerOpts0} = ssl_test_lib:make_rsa_cert_chains([{client_chain,
- [[],[],[{extensions, ClientExt}]]}],
- Config, "_peer_keyEncipherment"),
- ClientOpts = ssl_test_lib:ssl_options(ClientOpts0, Config),
- ServerOpts = ssl_test_lib:ssl_options(ServerOpts0, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib,
- send_recv_result_active, []}},
- {options, [{active, true} | 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, [{verify, verify_peer} | ClientOpts]}]),
- ssl_test_lib:check_result(Server, ok, Client, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-
-%%--------------------------------------------------------------------
-
-invalid_signature_server() ->
- [{doc,"Test client with invalid signature"}].
-
-invalid_signature_server(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
- PrivDir = proplists:get_value(priv_dir, Config),
-
- KeyFile = proplists:get_value(keyfile, ServerOpts),
- [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile),
- Key = ssl_test_lib:public_key(public_key:pem_entry_decode(KeyEntry)),
-
- ServerCertFile = proplists:get_value(certfile, ServerOpts),
- NewServerCertFile = filename:join(PrivDir, "server_invalid_cert.pem"),
- [{'Certificate', ServerDerCert, _}] = ssl_test_lib:pem_to_der(ServerCertFile),
- ServerOTPCert = public_key:pkix_decode_cert(ServerDerCert, otp),
- ServerOTPTbsCert = ServerOTPCert#'OTPCertificate'.tbsCertificate,
- NewServerDerCert = public_key:pkix_sign(ServerOTPTbsCert, Key),
- ssl_test_lib:der_to_pem(NewServerCertFile, [{'Certificate', NewServerDerCert, not_encrypted}]),
- NewServerOpts = [{certfile, NewServerCertFile} | proplists:delete(certfile, ServerOpts)],
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
- {from, self()},
- {options, NewServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {options, [{verify, verify_peer} | ClientOpts]}]),
- ssl_test_lib:check_server_alert(Server, Client, unknown_ca).
-
-%%--------------------------------------------------------------------
-
-invalid_signature_client() ->
- [{doc,"Test server with invalid signature"}].
-
-invalid_signature_client(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
- PrivDir = proplists:get_value(priv_dir, Config),
-
- KeyFile = proplists:get_value(keyfile, ClientOpts),
- [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile),
- Key = ssl_test_lib:public_key(public_key:pem_entry_decode(KeyEntry)),
-
- ClientCertFile = proplists:get_value(certfile, ClientOpts),
- NewClientCertFile = filename:join(PrivDir, "client_invalid_cert.pem"),
- [{'Certificate', ClientDerCert, _}] = ssl_test_lib:pem_to_der(ClientCertFile),
- ClientOTPCert = public_key:pkix_decode_cert(ClientDerCert, otp),
- ClientOTPTbsCert = ClientOTPCert#'OTPCertificate'.tbsCertificate,
- NewClientDerCert = public_key:pkix_sign(ClientOTPTbsCert, Key),
- ssl_test_lib:der_to_pem(NewClientCertFile, [{'Certificate', NewClientDerCert, not_encrypted}]),
- NewClientOpts = [{certfile, NewClientCertFile} | proplists:delete(certfile, ClientOpts)],
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
- {from, self()},
- {options, [{verify, verify_peer} | ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {options, NewClientOpts}]),
-
- ssl_test_lib:check_client_alert(Server, Client, unknown_ca).
-
-%%--------------------------------------------------------------------
-
-client_with_cert_cipher_suites_handshake() ->
- [{doc, "Test that client with a certificate without keyEncipherment usage "
- " extension can connect to a server with restricted cipher suites "}].
-client_with_cert_cipher_suites_handshake(Config) when is_list(Config) ->
- Ext = x509_test:extensions([{key_usage, [digitalSignature]}]),
- {ClientOpts0, ServerOpts0} = ssl_test_lib:make_rsa_cert_chains([{client_chain,
- [[], [], [{extensions, Ext}]]}],
- Config, "_sign_only_extensions"),
- ClientOpts = ssl_test_lib:ssl_options(ClientOpts0, Config),
- ServerOpts = ssl_test_lib:ssl_options(ServerOpts0, Config),
- TLSVersion = ssl_test_lib:protocol_version(Config, tuple),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib,
- send_recv_result_active, []}},
- {options, [{active, true},
- {ciphers,
- ssl_test_lib:rsa_non_signed_suites(TLSVersion)}
- | 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, [{active, true}
- | ClientOpts]}]),
-
- ssl_test_lib:check_result(Server, ok, Client, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-%%--------------------------------------------------------------------
-
-server_verify_no_cacerts() ->
- [{doc,"Test server must have cacerts if it wants to verify client"}].
-server_verify_no_cacerts(Config) when is_list(Config) ->
- ServerOpts = proplists:delete(cacertfile, ssl_test_lib:ssl_options(server_rsa_opts, Config)),
- {_, ServerNode, _} = ssl_test_lib:run_where(Config),
- Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
- {from, self()},
- {options, [{verify, verify_peer}
- | ServerOpts]}]),
-
- ssl_test_lib:check_result(Server, {error, {options, {cacertfile, ""}}}).
-
-
-%%--------------------------------------------------------------------
-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 = ssl_test_lib:ssl_options(empty_client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib,
- no_result, []}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
-
- FunAndState = {fun(_,{bad_cert, unknown_ca} = Reason, _) ->
- {fail, Reason};
- (_,{extension, _}, UserState) ->
- {unknown, UserState};
- (_, valid, UserState) ->
- {valid, [test_to_update_user_state | UserState]};
- (_, valid_peer, UserState) ->
- {valid, UserState}
- end, []},
-
- Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {ssl_test_lib,
- no_result, []}},
- {options,
- [{verify, verify_peer},
- {verify_fun, FunAndState}
- | ClientOpts]}]),
- ssl_test_lib:check_client_alert(Server, Client, unknown_ca).
-
-%%--------------------------------------------------------------------
-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 = ssl_test_lib:ssl_options(empty_client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib,
- send_recv_result_active, []}},
- {options, 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,
- [{verify, verify_none}| ClientOpts]}]),
-
- ssl_test_lib:check_result(Server, ok, Client, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-%%--------------------------------------------------------------------
-unknown_server_ca_accept_verify_peer() ->
- [{doc, "Test that the client succeds if the ca is unknown in verify_peer mode"
- " with a verify_fun that accepts the unknown ca error"}].
-unknown_server_ca_accept_verify_peer(Config) when is_list(Config) ->
- ClientOpts = ssl_test_lib:ssl_options(empty_client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib,
- send_recv_result_active, []}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
-
- FunAndState = {fun(_,{bad_cert, unknown_ca}, UserState) ->
- {valid, UserState};
- (_,{bad_cert, _} = Reason, _) ->
- {fail, Reason};
- (_,{extension, _}, UserState) ->
- {unknown, UserState};
- (_, valid, UserState) ->
- {valid, UserState};
- (_, valid_peer, UserState) ->
- {valid, UserState}
- end, []},
-
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {ssl_test_lib,
- send_recv_result_active, []}},
- {options,
- [{verify, verify_peer},
- {verify_fun, FunAndState}| ClientOpts]}]),
-
- ssl_test_lib:check_result(Server, ok, Client, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-%%--------------------------------------------------------------------
-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 = ssl_test_lib:ssl_options(empty_client_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib,
- send_recv_result_active, []}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
-
- AcceptBadCa = fun({bad_cert,unknown_ca}, Acc) -> Acc;
- (Other, Acc) -> [Other | Acc]
- end,
- VerifyFun =
- fun(ErrorList) ->
- case lists:foldl(AcceptBadCa, [], ErrorList) of
- [] -> true;
- [_|_] -> false
- end
- end,
-
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {ssl_test_lib,
- send_recv_result_active, []}},
- {options,
- [{verify, verify_peer},
- {verify_fun, VerifyFun}| ClientOpts]}]),
-
- ssl_test_lib:check_result(Server, ok, Client, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-%%--------------------------------------------------------------------
-
-customize_hostname_check() ->
- [{doc,"Test option customize_hostname_check."}].
-customize_hostname_check(Config) when is_list(Config) ->
- Ext = [#'Extension'{extnID = ?'id-ce-subjectAltName',
- extnValue = [{dNSName, "*.example.org"}],
- critical = false}
- ],
- {ClientOpts0, ServerOpts0} = ssl_test_lib:make_rsa_cert_chains([{server_chain,
- [[],
- [],
- [{extensions, Ext}]
- ]}],
- Config, "https_hostname_convention"),
- ClientOpts = ssl_test_lib:ssl_options(ClientOpts0, Config),
- ServerOpts = ssl_test_lib:ssl_options(ServerOpts0, Config),
-
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
-
- CustomFun = public_key:pkix_verify_hostname_match_fun(https),
-
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {ssl_test_lib, send_recv_result_active, []}},
- {options,
- [{server_name_indication, "other.example.org"},
- {customize_hostname_check,
- [{match_fun, CustomFun}]} | ClientOpts]
- }]),
- ssl_test_lib:check_result(Server, ok, Client, ok),
-
- Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}},
-
- Client1 = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {ssl_test_lib, no_result, []}},
- {options, ClientOpts}
- ]),
- ssl_test_lib:check_client_alert(Server, Client1, handshake_failure).
-
-incomplete_chain() ->
- [{doc,"Test option verify_peer"}].
-incomplete_chain(Config) when is_list(Config) ->
- DefConf = ssl_test_lib:default_cert_chain_conf(),
- CertChainConf = ssl_test_lib:gen_conf(rsa, rsa, DefConf, DefConf),
- #{server_config := ServerConf,
- client_config := ClientConf} = public_key:pkix_test_data(CertChainConf),
- [ServerRoot| _] = ServerCas = proplists:get_value(cacerts, ServerConf),
- ClientCas = proplists:get_value(cacerts, ClientConf),
-
- Active = proplists:get_value(active, Config),
- ReceiveFunction = proplists:get_value(receive_function, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, ReceiveFunction, []}},
- {options, [{active, Active}, {verify, verify_peer},
- {cacerts, [ServerRoot]} |
- proplists:delete(cacerts, ServerConf)]}]),
- 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, ReceiveFunction, []}},
- {options, [{active, Active},
- {verify, verify_peer},
- {cacerts, ServerCas ++ ClientCas} |
- proplists:delete(cacerts, ClientConf)]}]),
- ssl_test_lib:check_result(Server, ok, Client, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-long_chain() ->
- [{doc,"Test option verify_peer"}].
-long_chain(Config) when is_list(Config) ->
- #{server_config := ServerConf,
- client_config := ClientConf} = public_key:pkix_test_data(#{server_chain => #{root => [{key, ssl_test_lib:hardcode_rsa_key(1)}],
- intermediates => [[{key, ssl_test_lib:hardcode_rsa_key(2)}],
- [{key, ssl_test_lib:hardcode_rsa_key(3)}],
- [{key, ssl_test_lib:hardcode_rsa_key(4)}]],
- peer => [{key, ssl_test_lib:hardcode_rsa_key(5)}]},
- client_chain => #{root => [{key, ssl_test_lib:hardcode_rsa_key(3)}],
- intermediates => [[{key, ssl_test_lib:hardcode_rsa_key(2)}]],
- peer => [{key, ssl_test_lib:hardcode_rsa_key(1)}]}}),
- [ServerRoot| _] = ServerCas = proplists:get_value(cacerts, ServerConf),
- ClientCas = proplists:get_value(cacerts, ClientConf),
-
- Active = proplists:get_value(active, Config),
- ReceiveFunction = proplists:get_value(receive_function, Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib, ReceiveFunction, []}},
- {options, [{active, Active}, {verify, verify_peer},
- {cacerts, [ServerRoot]} |
- proplists:delete(cacerts, ServerConf)]}]),
- 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, ReceiveFunction, []}},
- {options, [{active, Active},
- {verify, verify_peer},
- {depth, 5},
- {cacerts, ServerCas ++ ClientCas} |
- proplists:delete(cacerts, ClientConf)]}]),
- ssl_test_lib:check_result(Server, ok, Client, ok),
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client).
-
-
-%%--------------------------------------------------------------------
-%% Internal functions ------------------------------------------------
-%%--------------------------------------------------------------------
-
diff --git a/lib/ssl/test/ssl_cipher_suite_SUITE.erl b/lib/ssl/test/ssl_cipher_suite_SUITE.erl
index 51788c29e7..e598d662e9 100644
--- a/lib/ssl/test/ssl_cipher_suite_SUITE.erl
+++ b/lib/ssl/test/ssl_cipher_suite_SUITE.erl
@@ -45,7 +45,7 @@ groups() ->
{'tlsv1.2', [], kex()},
{'tlsv1.1', [], kex()},
{'tlsv1', [], kex()},
- {'sslv3', [], kex()},
+ {'sslv3', [], ssl3_kex()},
{'dtlsv1.2', [], kex()},
{'dtlsv1', [], kex()},
{dhe_rsa, [],[dhe_rsa_3des_ede_cbc,
@@ -130,6 +130,11 @@ groups() ->
kex() ->
rsa() ++ ecdsa() ++ dss() ++ anonymous().
+
+ssl3_kex() ->
+ ssl3_rsa() ++ ssl3_dss() ++ ssl3_anonymous().
+
+
rsa() ->
[{group, dhe_rsa},
{group, ecdhe_rsa},
@@ -138,6 +143,11 @@ rsa() ->
{group, rsa_psk}
].
+ssl3_rsa() ->
+ [{group, dhe_rsa},
+ {group, rsa}
+ ].
+
ecdsa() ->
[{group, ecdhe_ecdsa}].
@@ -145,6 +155,10 @@ dss() ->
[{group, dhe_dss},
{group, srp_dss}].
+ssl3_dss() ->
+ [{group, dhe_dss}
+ ].
+
anonymous() ->
[{group, dh_anon},
{group, ecdh_anon},
@@ -154,6 +168,10 @@ anonymous() ->
{group, srp_anon}
].
+ssl3_anonymous() ->
+ [{group, dh_anon}].
+
+
init_per_suite(Config) ->
catch crypto:stop(),
try crypto:start() of
diff --git a/lib/ssl/test/ssl_dist_SUITE.erl b/lib/ssl/test/ssl_dist_SUITE.erl
index 003e1fc448..7cfb2ac0c5 100644
--- a/lib/ssl/test/ssl_dist_SUITE.erl
+++ b/lib/ssl/test/ssl_dist_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2018. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -311,9 +311,11 @@ listen_port_options(Config) when is_list(Config) ->
catch
_:Reason ->
stop_ssl_node(NH2),
+ stop_ssl_node(NH1),
ct:fail(Reason)
end,
stop_ssl_node(NH2),
+ stop_ssl_node(NH1),
success(Config).
%%--------------------------------------------------------------------
diff --git a/lib/ssl/test/ssl_handshake_SUITE.erl b/lib/ssl/test/ssl_handshake_SUITE.erl
index 1b432970b6..e4a2ebb583 100644
--- a/lib/ssl/test/ssl_handshake_SUITE.erl
+++ b/lib/ssl/test/ssl_handshake_SUITE.erl
@@ -106,7 +106,7 @@ decode_hello_handshake(_Config) ->
Version = {3, 0},
{Records, _Buffer} = tls_handshake:get_tls_handshake(Version, HelloPacket, <<>>,
- #ssl_options{}),
+ ssl:options_to_map(#ssl_options{})),
{Hello, _Data} = hd(Records),
Extensions = Hello#server_hello.extensions,
@@ -214,7 +214,7 @@ encode_decode_srp(_Config) ->
0,3, % HostNameLength
98,97,114>>, % hostname = "bar"
EncodedExts0 = <<?UINT16(_),EncodedExts/binary>> =
- ssl_handshake:encode_hello_extensions(Exts),
+ ssl_handshake:encode_hello_extensions(Exts, {3,3}),
Exts = ssl_handshake:decode_hello_extensions(EncodedExts, {3,3}, {3,3}, client).
signature_algorithms(Config) ->
diff --git a/lib/ssl/test/ssl_npn_handshake_SUITE.erl b/lib/ssl/test/ssl_npn_SUITE.erl
index 878e983bb9..8a76a3a82b 100644
--- a/lib/ssl/test/ssl_npn_handshake_SUITE.erl
+++ b/lib/ssl/test/ssl_npn_SUITE.erl
@@ -19,7 +19,7 @@
%%
%%
--module(ssl_npn_handshake_SUITE).
+-module(ssl_npn_SUITE).
%% Note: This directive should only be used in test suites.
-compile(export_all).
diff --git a/lib/ssl/test/ssl_npn_hello_SUITE.erl b/lib/ssl/test/ssl_npn_hello_SUITE.erl
index 46734ba180..e098190ece 100644
--- a/lib/ssl/test/ssl_npn_hello_SUITE.erl
+++ b/lib/ssl/test/ssl_npn_hello_SUITE.erl
@@ -24,10 +24,11 @@
%% Note: This directive should only be used in test suites.
-compile(export_all).
--include("ssl_cipher.hrl").
--include("ssl_internal.hrl").
--include("tls_handshake.hrl").
--include("tls_record.hrl").
+
+-include_lib("ssl/src/tls_record.hrl").
+-include_lib("ssl/src/tls_handshake.hrl").
+-include_lib("ssl/src/ssl_cipher.hrl").
+-include_lib("ssl/src/ssl_internal.hrl").
-include_lib("common_test/include/ct.hrl").
%%--------------------------------------------------------------------
@@ -70,7 +71,8 @@ encode_and_decode_client_hello_test(Config) ->
HandShakeData = create_client_handshake(undefined),
Version = ssl_test_lib:protocol_version(Config),
{[{DecodedHandshakeMessage, _Raw}], _} =
- tls_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>, #ssl_options{}),
+ tls_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>,
+ ssl:options_to_map(#ssl_options{})),
Extensions = DecodedHandshakeMessage#client_hello.extensions,
#{next_protocol_negotiation := undefined} = Extensions.
%%--------------------------------------------------------------------
@@ -78,7 +80,8 @@ encode_and_decode_npn_client_hello_test(Config) ->
HandShakeData = create_client_handshake(#next_protocol_negotiation{extension_data = <<>>}),
Version = ssl_test_lib:protocol_version(Config),
{[{DecodedHandshakeMessage, _Raw}], _} =
- tls_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>, #ssl_options{}),
+ tls_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>,
+ ssl:options_to_map(#ssl_options{})),
Extensions = DecodedHandshakeMessage#client_hello.extensions,
#{next_protocol_negotiation := #next_protocol_negotiation{extension_data = <<>>}} = Extensions.
%%--------------------------------------------------------------------
@@ -86,7 +89,8 @@ encode_and_decode_server_hello_test(Config) ->
HandShakeData = create_server_handshake(undefined),
Version = ssl_test_lib:protocol_version(Config),
{[{DecodedHandshakeMessage, _Raw}], _} =
- tls_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>, #ssl_options{}),
+ tls_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>,
+ ssl:options_to_map(#ssl_options{})),
Extensions = DecodedHandshakeMessage#server_hello.extensions,
#{next_protocol_negotiation := undefined} = Extensions.
@@ -95,7 +99,8 @@ encode_and_decode_npn_server_hello_test(Config) ->
HandShakeData = create_server_handshake(#next_protocol_negotiation{extension_data = <<6, "spdy/2">>}),
Version = ssl_test_lib:protocol_version(Config),
{[{DecodedHandshakeMessage, _Raw}], _} =
- tls_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>, #ssl_options{}),
+ tls_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>,
+ ssl:options_to_map(#ssl_options{})),
Extensions = DecodedHandshakeMessage#server_hello.extensions,
ct:log("~p ~n", [Extensions]),
#{next_protocol_negotiation := #next_protocol_negotiation{extension_data = <<6, "spdy/2">>}} = Extensions.
diff --git a/lib/ssl/test/ssl_pem_cache_SUITE.erl b/lib/ssl/test/ssl_pem_cache_SUITE.erl
index 6f11e2bbe8..3c7f6ab20f 100644
--- a/lib/ssl/test/ssl_pem_cache_SUITE.erl
+++ b/lib/ssl/test/ssl_pem_cache_SUITE.erl
@@ -34,7 +34,10 @@
%% Common Test interface functions -----------------------------------
%%--------------------------------------------------------------------
all() ->
- [pem_cleanup, invalid_insert].
+ [
+ pem_cleanup,
+ clear_pem_cache,
+ invalid_insert].
groups() ->
[].
@@ -110,6 +113,37 @@ pem_cleanup(Config)when is_list(Config) ->
ssl_test_lib:close(Client),
false = Size == Size1.
+clear_pem_cache() ->
+ [{doc,"Test that internal reference tabel is cleaned properly even when "
+ " the PEM cache is cleared" }].
+clear_pem_cache(Config) when is_list(Config) ->
+ {status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)),
+ [_, _,_, _, Prop] = StatusInfo,
+ State = ssl_test_lib:state(Prop),
+ [_,{FilRefDb, _} |_] = element(6, State),
+ {Server, Client} = basic_verify_test_no_close(Config),
+ CountReferencedFiles = fun({_, -1}, Acc) ->
+ Acc;
+ ({_, N}, Acc) ->
+ N + Acc
+ end,
+
+ 2 = ets:foldl(CountReferencedFiles, 0, FilRefDb),
+ ssl:clear_pem_cache(),
+ _ = sys:get_status(whereis(ssl_manager)),
+ {Server1, Client1} = basic_verify_test_no_close(Config),
+ 4 = ets:foldl(CountReferencedFiles, 0, FilRefDb),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client),
+ ct:sleep(2000),
+ _ = sys:get_status(whereis(ssl_manager)),
+ 2 = ets:foldl(CountReferencedFiles, 0, FilRefDb),
+ ssl_test_lib:close(Server1),
+ ssl_test_lib:close(Client1),
+ ct:sleep(2000),
+ _ = sys:get_status(whereis(ssl_manager)),
+ 0 = ets:foldl(CountReferencedFiles, 0, FilRefDb).
+
invalid_insert() ->
[{doc, "Test that insert of invalid pem does not cause empty cache entry"}].
invalid_insert(Config)when is_list(Config) ->
@@ -163,3 +197,22 @@ later()->
Gregorian = calendar:datetime_to_gregorian_seconds(DateTime),
calendar:gregorian_seconds_to_datetime(Gregorian + (2 * ?CLEANUP_INTERVAL)).
+basic_verify_test_no_close(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, 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),
+ {Server, Client}.
diff --git a/lib/ssl/test/ssl_renegotiate_SUITE.erl b/lib/ssl/test/ssl_renegotiate_SUITE.erl
new file mode 100644
index 0000000000..ef3f9ebb52
--- /dev/null
+++ b/lib/ssl/test/ssl_renegotiate_SUITE.erl
@@ -0,0 +1,499 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2019-2019. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+
+-module(ssl_renegotiate_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("public_key/include/public_key.hrl").
+
+-define(SLEEP, 500).
+-define(RENEGOTIATION_DISABLE_TIME, 12000).
+
+%%--------------------------------------------------------------------
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
+all() ->
+ [
+ {group, 'tlsv1.2'},
+ {group, 'tlsv1.1'},
+ {group, 'tlsv1'},
+ {group, 'sslv3'},
+ {group, 'dtlsv1.2'},
+ {group, 'dtlsv1'}
+ ].
+
+groups() ->
+ [{'dtlsv1.2', [], renegotiate_tests()},
+ {'dtlsv1', [], renegotiate_tests()},
+ {'tlsv1.3', [], renegotiate_tests()},
+ {'tlsv1.2', [], renegotiate_tests()},
+ {'tlsv1.1', [], renegotiate_tests()},
+ {'tlsv1', [], renegotiate_tests()},
+ {'sslv3', [], ssl3_renegotiate_tests()}
+ ].
+
+renegotiate_tests() ->
+ [client_renegotiate,
+ server_renegotiate,
+ client_secure_renegotiate,
+ client_secure_renegotiate_fallback,
+ client_renegotiate_reused_session,
+ server_renegotiate_reused_session,
+ client_no_wrap_sequence_number,
+ server_no_wrap_sequence_number,
+ renegotiate_dos_mitigate_active,
+ renegotiate_dos_mitigate_passive,
+ renegotiate_dos_mitigate_absolute].
+
+ssl3_renegotiate_tests() ->
+ [client_renegotiate,
+ server_renegotiate,
+ client_renegotiate_reused_session,
+ server_renegotiate_reused_session,
+ client_no_wrap_sequence_number,
+ server_no_wrap_sequence_number,
+ renegotiate_dos_mitigate_active,
+ renegotiate_dos_mitigate_passive,
+ renegotiate_dos_mitigate_absolute].
+
+init_per_suite(Config) ->
+ catch crypto:stop(),
+ try crypto:start() of
+ ok ->
+ ssl_test_lib:clean_start(),
+ ssl_test_lib:make_rsa_cert(Config)
+ catch _:_ ->
+ {skip, "Crypto did not start"}
+ end.
+
+end_per_suite(_Config) ->
+ ssl:stop(),
+ application:stop(crypto).
+
+init_per_group(GroupName, Config) ->
+ ssl_test_lib:clean_tls_version(Config),
+ case ssl_test_lib:is_tls_version(GroupName) andalso ssl_test_lib:sufficient_crypto_support(GroupName) of
+ true ->
+ ssl_test_lib:init_tls_version(GroupName, Config);
+ _ ->
+ case ssl_test_lib:sufficient_crypto_support(GroupName) of
+ true ->
+ ssl:start(),
+ Config;
+ false ->
+ {skip, "Missing crypto support"}
+ end
+ end.
+
+end_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ ssl_test_lib:clean_tls_version(Config);
+ false ->
+ Config
+ end.
+
+%%--------------------------------------------------------------------
+%% Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
+client_renegotiate() ->
+ [{doc,"Test ssl:renegotiate/1 on client."}].
+client_renegotiate(Config) when is_list(Config) ->
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "From erlang to erlang",
+
+ Server =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ renegotiate, [Data]}},
+ {options, [{reuse_sessions, false} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client, ok, Server, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+client_secure_renegotiate() ->
+ [{doc,"Test ssl:renegotiate/1 on client."}].
+client_secure_renegotiate(Config) when is_list(Config) ->
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "From erlang to erlang",
+
+ Server =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
+ {options, [{secure_renegotiate, true} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ renegotiate, [Data]}},
+ {options, [{reuse_sessions, false},
+ {secure_renegotiate, true}| ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client, ok, Server, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+client_secure_renegotiate_fallback() ->
+ [{doc,"Test that we can set secure_renegotiate to false that is "
+ "fallback option, we however do not have a insecure server to test against!"}].
+client_secure_renegotiate_fallback(Config) when is_list(Config) ->
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "From erlang to erlang",
+
+ Server =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
+ {options, [{secure_renegotiate, false} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ renegotiate, [Data]}},
+ {options, [{reuse_sessions, false},
+ {secure_renegotiate, false}| ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client, ok, Server, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+server_renegotiate() ->
+ [{doc,"Test ssl:renegotiate/1 on server."}].
+server_renegotiate(Config) when is_list(Config) ->
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "From erlang to erlang",
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE,
+ renegotiate, [Data]}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
+ {options, [{reuse_sessions, false} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+client_renegotiate_reused_session() ->
+ [{doc,"Test ssl:renegotiate/1 on client when the ssl session will be reused."}].
+client_renegotiate_reused_session(Config) when is_list(Config) ->
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "From erlang to erlang",
+
+ Server =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ renegotiate_reuse_session, [Data]}},
+ {options, [{reuse_sessions, true} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client, ok, Server, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+%%--------------------------------------------------------------------
+server_renegotiate_reused_session() ->
+ [{doc,"Test ssl:renegotiate/1 on server when the ssl session will be reused."}].
+server_renegotiate_reused_session(Config) when is_list(Config) ->
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "From erlang to erlang",
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE,
+ renegotiate_reuse_session, [Data]}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
+ {options, [{reuse_sessions, true} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+%%--------------------------------------------------------------------
+client_no_wrap_sequence_number() ->
+ [{doc,"Test that erlang client will renegotiate session when",
+ "max sequence number celing is about to be reached. Although"
+ "in the testcase we use the test option renegotiate_at"
+ " to lower treashold substantially."}].
+
+client_no_wrap_sequence_number(Config) when is_list(Config) ->
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ ErlData = "From erlang to erlang",
+ N = 12,
+
+ Server =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ Version = ssl_test_lib:protocol_version(Config, tuple),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib,
+ trigger_renegotiate, [[ErlData, treashold(N, Version)]]}},
+ {options, [{reuse_sessions, false},
+ {renegotiate_at, N} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+server_no_wrap_sequence_number() ->
+ [{doc, "Test that erlang server will renegotiate session when",
+ "max sequence number celing is about to be reached. Although"
+ "in the testcase we use the test option renegotiate_at"
+ " to lower treashold substantially."}].
+
+server_no_wrap_sequence_number(Config) when is_list(Config) ->
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "From erlang to erlang",
+ N = 12,
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib,
+ trigger_renegotiate, [[Data, N+2]]}},
+ {options, [{renegotiate_at, N} | 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, no_result, []}},
+ {options, [{reuse_sessions, false} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+renegotiate_dos_mitigate_active() ->
+ [{doc, "Mitigate DOS computational attack by not allowing client to renegotiate many times in a row",
+ "immediately after each other"}].
+renegotiate_dos_mitigate_active(Config) when is_list(Config) ->
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ renegotiate_immediately, []}},
+ {options, ClientOpts}]),
+
+ ssl_test_lib:check_result(Client, ok, Server, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+renegotiate_dos_mitigate_passive() ->
+ [{doc, "Mitigate DOS computational attack by not allowing client to renegotiate many times in a row",
+ "immediately after each other"}].
+renegotiate_dos_mitigate_passive(Config) when is_list(Config) ->
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result, []}},
+ {options, [{active, false} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ renegotiate_immediately, []}},
+ {options, ClientOpts}]),
+
+ ssl_test_lib:check_result(Client, ok, Server, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+renegotiate_dos_mitigate_absolute() ->
+ [{doc, "Mitigate DOS computational attack by not allowing client to initiate renegotiation"}].
+renegotiate_dos_mitigate_absolute(Config) when is_list(Config) ->
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, [{client_renegotiation, false} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ renegotiate_rejected,
+ []}},
+ {options, ClientOpts}]),
+
+ ssl_test_lib:check_result(Client, ok, Server, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+%% Internal functions ------------------------------------------------
+%%--------------------------------------------------------------------
+renegotiate(Socket, Data) ->
+ ct:log("Renegotiating ~n", []),
+ Result = ssl:renegotiate(Socket),
+ ct:log("Result ~p~n", [Result]),
+ ssl:send(Socket, Data),
+ case Result of
+ ok ->
+ ok;
+ Other ->
+ Other
+ end.
+
+renegotiate_reuse_session(Socket, Data) ->
+ %% Make sure session is registered
+ ct:sleep(?SLEEP),
+ renegotiate(Socket, Data).
+
+renegotiate_immediately(Socket) ->
+ _ = ssl_test_lib:active_recv(Socket, 11),
+ ok = ssl:renegotiate(Socket),
+ {error, renegotiation_rejected} = ssl:renegotiate(Socket),
+ ct:sleep(?RENEGOTIATION_DISABLE_TIME + ?SLEEP),
+ ok = ssl:renegotiate(Socket),
+ ct:log("Renegotiated again"),
+ ssl:send(Socket, "Hello world"),
+ ok.
+
+renegotiate_rejected(Socket) ->
+ _ = ssl_test_lib:active_recv(Socket, 11),
+ {error, renegotiation_rejected} = ssl:renegotiate(Socket),
+ {error, renegotiation_rejected} = ssl:renegotiate(Socket),
+ ct:sleep(?RENEGOTIATION_DISABLE_TIME +1),
+ {error, renegotiation_rejected} = ssl:renegotiate(Socket),
+ ct:log("Failed to renegotiate again"),
+ ssl:send(Socket, "Hello world"),
+ ok.
+
+%% First two clauses handles 1/n-1 splitting countermeasure Rizzo/Duong-Beast
+treashold(N, {3,0}) ->
+ (N div 2) + 1;
+treashold(N, {3,1}) ->
+ (N div 2) + 1;
+treashold(N, _) ->
+ N + 1.
+
+erlang_ssl_receive(Socket, Data) ->
+ case ssl_test_lib:active_recv(Socket, length(Data)) of
+ Data ->
+ ok;
+ Other ->
+ ct:fail({{expected, Data}, {got, Other}})
+ end.
diff --git a/lib/ssl/test/ssl_session_SUITE.erl b/lib/ssl/test/ssl_session_SUITE.erl
new file mode 100644
index 0000000000..aa79698a72
--- /dev/null
+++ b/lib/ssl/test/ssl_session_SUITE.erl
@@ -0,0 +1,377 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2007-2019. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(ssl_session_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include("tls_handshake.hrl").
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("public_key/include/public_key.hrl").
+
+-define(SLEEP, 500).
+-define(EXPIRE, 10).
+
+%%--------------------------------------------------------------------
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
+all() ->
+ [
+ {group, 'tlsv1.2'},
+ {group, 'tlsv1.1'},
+ {group, 'tlsv1'},
+ {group, 'sslv3'},
+ {group, 'dtlsv1.2'},
+ {group, 'dtlsv1'}
+ ].
+
+groups() ->
+ [{'dtlsv1.2', [], session_tests()},
+ {'dtlsv1', [], session_tests()},
+ {'tlsv1.3', [], session_tests()},
+ {'tlsv1.2', [], session_tests()},
+ {'tlsv1.1', [], session_tests()},
+ {'tlsv1', [], session_tests()},
+ {'sslv3', [], session_tests()}
+ ].
+
+session_tests() ->
+ [reuse_session,
+ reuse_session_expired,
+ server_does_not_want_to_reuse_session,
+ no_reuses_session_server_restart_new_cert,
+ no_reuses_session_server_restart_new_cert_file].
+
+
+init_per_suite(Config0) ->
+ catch crypto:stop(),
+ try crypto:start() of
+ ok ->
+ ssl_test_lib:clean_start(),
+ Config = ssl_test_lib:make_rsa_cert(Config0),
+ ssl_test_lib:make_dsa_cert(Config)
+ catch _:_ ->
+ {skip, "Crypto did not start"}
+ end.
+
+end_per_suite(_Config) ->
+ ssl:stop(),
+ application:stop(crypto).
+
+init_per_group(GroupName, Config) ->
+ ssl_test_lib:clean_tls_version(Config),
+ case ssl_test_lib:is_tls_version(GroupName) andalso ssl_test_lib:sufficient_crypto_support(GroupName) of
+ true ->
+ ssl_test_lib:init_tls_version(GroupName, Config);
+ _ ->
+ case ssl_test_lib:sufficient_crypto_support(GroupName) of
+ true ->
+ ssl:start(),
+ Config;
+ false ->
+ {skip, "Missing crypto support"}
+ end
+ end.
+
+end_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ ssl_test_lib:clean_tls_version(Config);
+ false ->
+ Config
+ end.
+
+init_per_testcase(reuse_session_expired, Config) ->
+ ssl:stop(),
+ application:load(ssl),
+ ssl_test_lib:clean_env(),
+ application:set_env(ssl, session_lifetime, ?EXPIRE),
+ application:set_env(ssl, session_delay_cleanup_time, 500),
+ ssl:start(),
+ ct:timetrap({seconds, 30}),
+ Config;
+init_per_testcase(_, Config) ->
+ ct:timetrap({seconds, 15}),
+ Config.
+
+end_per_testcase(_TestCase, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
+
+reuse_session() ->
+ [{doc,"Test reuse of sessions (short handshake)"}].
+reuse_session(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
+
+ ssl_test_lib:reuse_session(ClientOpts, ServerOpts, Config).
+%%--------------------------------------------------------------------
+reuse_session_expired() ->
+ [{doc,"Test sessions is not reused when it has expired"}].
+reuse_session_expired(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server0 =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {tcp_options, [{active, false}]},
+ {options, ServerOpts}]),
+ Port0 = ssl_test_lib:inet_port(Server0),
+
+ Client0 = ssl_test_lib:start_client([{node, ClientNode},
+ {port, Port0}, {host, Hostname},
+ {mfa, {ssl_test_lib, session_id, []}},
+ {from, self()}, {options, [{reuse_sessions, save} | ClientOpts]}]),
+ Server0 ! listen,
+
+ Client1 = ssl_test_lib:start_client([{node, ClientNode},
+ {port, Port0}, {host, Hostname},
+ {mfa, {ssl_test_lib, session_id, []}},
+ {from, self()}, {options, ClientOpts}]),
+
+ SID = receive
+ {Client0, Id0} ->
+ Id0
+ end,
+
+ receive
+ {Client1, SID} ->
+ ok
+ after ?SLEEP ->
+ ct:fail(session_not_reused)
+ end,
+
+ Server0 ! listen,
+
+ %% Make sure session is unregistered due to expiration
+ ct:sleep((?EXPIRE*2)),
+
+ make_sure_expired(Hostname, Port0, SID),
+
+ Client2 =
+ ssl_test_lib:start_client([{node, ClientNode},
+ {port, Port0}, {host, Hostname},
+ {mfa, {ssl_test_lib, session_id, []}},
+ {from, self()}, {options, ClientOpts}]),
+ receive
+ {Client2, SID} ->
+ ct:fail(session_reused_when_session_expired);
+ {Client2, _} ->
+ ok
+ end,
+ process_flag(trap_exit, false),
+ ssl_test_lib:close(Server0),
+ ssl_test_lib:close(Client0),
+ ssl_test_lib:close(Client1),
+ ssl_test_lib:close(Client2).
+
+make_sure_expired(Host, Port, Id) ->
+ {status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)),
+ [_, _,_, _, Prop] = StatusInfo,
+ State = ssl_test_lib:state(Prop),
+ ClientCache = element(2, State),
+
+ case ssl_session_cache:lookup(ClientCache, {{Host, Port}, Id}) of
+ undefined ->
+ ok;
+ #session{is_resumable = false} ->
+ ok;
+ _ ->
+ ct:sleep(?SLEEP),
+ make_sure_expired(Host, Port, Id)
+ end.
+
+%%--------------------------------------------------------------------
+server_does_not_want_to_reuse_session() ->
+ [{doc,"Test reuse of sessions (short handshake)"}].
+server_does_not_want_to_reuse_session(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, session_info_result, []}},
+ {options, [{reuse_session, fun(_,_,_,_) ->
+ false
+ end} |
+ ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client0 =
+ ssl_test_lib:start_client([{node, ClientNode},
+ {port, Port}, {host, Hostname},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {from, self()}, {options, ClientOpts}]),
+ SessionInfo =
+ receive
+ {Server, Info} ->
+ Info
+ end,
+
+ Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}},
+
+ %% Make sure session is registered
+ ct:sleep(?SLEEP),
+
+ Client1 =
+ ssl_test_lib:start_client([{node, ClientNode},
+ {port, Port}, {host, Hostname},
+ {mfa, {ssl_test_lib, session_info_result, []}},
+ {from, self()}, {options, ClientOpts}]),
+ receive
+ {Client1, SessionInfo} ->
+ ct:fail(session_reused_when_server_does_not_want_to);
+ {Client1, _Other} ->
+ ok
+ end,
+ ssl_test_lib:close(Client0),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client1).
+
+no_reuses_session_server_restart_new_cert() ->
+ [{doc,"Check that a session is not reused if the server is restarted with a new cert."}].
+no_reuses_session_server_restart_new_cert(Config) when is_list(Config) ->
+
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
+ DsaServerOpts = ssl_test_lib:ssl_options(server_dsa_verify_opts, Config),
+ DsaClientOpts = ssl_test_lib:ssl_options(client_dsa_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, session_info_result, []}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client0 =
+ ssl_test_lib:start_client([{node, ClientNode},
+ {port, Port}, {host, Hostname},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {from, self()}, {options, ClientOpts}]),
+ SessionInfo =
+ receive
+ {Server, Info} ->
+ Info
+ end,
+
+ %% Make sure session is registered
+ ct:sleep(?SLEEP),
+ Monitor = erlang:monitor(process, Server),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client0),
+ receive
+ {'DOWN', Monitor, _, _, _} ->
+ ok
+ end,
+
+ Server1 =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, Port},
+ {from, self()},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {options, [{reuseaddr, true} | DsaServerOpts]}]),
+
+ Client1 =
+ ssl_test_lib:start_client([{node, ClientNode},
+ {port, Port}, {host, Hostname},
+ {mfa, {ssl_test_lib, session_info_result, []}},
+ {from, self()}, {options, DsaClientOpts}]),
+ receive
+ {Client1, SessionInfo} ->
+ ct:fail(session_reused_when_server_has_new_cert);
+ {Client1, _Other} ->
+ ok
+ end,
+ ssl_test_lib:close(Server1),
+ ssl_test_lib:close(Client1).
+
+%%--------------------------------------------------------------------
+no_reuses_session_server_restart_new_cert_file() ->
+ [{doc,"Check that a session is not reused if a server is restarted with a new "
+ "cert contained in a file with the same name as the old cert."}].
+
+no_reuses_session_server_restart_new_cert_file(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
+ DsaServerOpts = ssl_test_lib:ssl_options(server_dsa_verify_opts, Config),
+ PrivDir = proplists:get_value(priv_dir, Config),
+
+ NewServerOpts0 = ssl_test_lib:new_config(PrivDir, ServerOpts),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, session_info_result, []}},
+ {options, NewServerOpts0}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client0 =
+ ssl_test_lib:start_client([{node, ClientNode},
+ {port, Port}, {host, Hostname},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {from, self()}, {options, ClientOpts}]),
+ SessionInfo =
+ receive
+ {Server, Info} ->
+ Info
+ end,
+
+ %% Make sure session is registered and we get
+ %% new file time stamp when calling new_config!
+ ct:sleep(?SLEEP* 2),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client0),
+
+ ssl:clear_pem_cache(),
+
+ NewServerOpts1 = ssl_test_lib:new_config(PrivDir, DsaServerOpts),
+
+ Server1 =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, Port},
+ {from, self()},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {options, [{reuseaddr, true} | NewServerOpts1]}]),
+ Client1 =
+ ssl_test_lib:start_client([{node, ClientNode},
+ {port, Port}, {host, Hostname},
+ {mfa, {ssl_test_lib, session_info_result, []}},
+ {from, self()}, {options, ClientOpts}]),
+ receive
+ {Client1, SessionInfo} ->
+ ct:fail(session_reused_when_server_has_new_cert);
+ {Client1, _Other} ->
+ ok
+ end,
+ ssl_test_lib:close(Server1),
+ ssl_test_lib:close(Client1).
+
+%%--------------------------------------------------------------------
+%% Internal functions ------------------------------------------------
+%%--------------------------------------------------------------------
diff --git a/lib/ssl/test/ssl_session_cache_SUITE.erl b/lib/ssl/test/ssl_session_cache_SUITE.erl
index b71b15b028..553c2d247b 100644
--- a/lib/ssl/test/ssl_session_cache_SUITE.erl
+++ b/lib/ssl/test/ssl_session_cache_SUITE.erl
@@ -28,7 +28,7 @@
-include_lib("common_test/include/ct.hrl").
-define(DELAY, 500).
--define(SLEEP, 500).
+-define(SLEEP, 1000).
-define(TIMEOUT, 60000).
-define(LONG_TIMEOUT, 600000).
-define(MAX_TABLE_SIZE, 5).
@@ -207,7 +207,7 @@ session_cleanup(Config) when is_list(Config) ->
end,
%% Make sure session is registered
- ct:sleep(?SLEEP),
+ ct:sleep(?SLEEP*2),
{status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)),
[_, _,_, _, Prop] = StatusInfo,
diff --git a/lib/ssl/test/ssl_sni_SUITE.erl b/lib/ssl/test/ssl_sni_SUITE.erl
index 7629d75100..e68ea2c99d 100644
--- a/lib/ssl/test/ssl_sni_SUITE.erl
+++ b/lib/ssl/test/ssl_sni_SUITE.erl
@@ -36,7 +36,6 @@ all() ->
[{group, 'tlsv1.2'},
{group, 'tlsv1.1'},
{group, 'tlsv1'},
- {group, 'sslv3'},
{group, 'dtlsv1.2'},
{group, 'dtlsv1'}
].
@@ -46,7 +45,6 @@ groups() ->
{'tlsv1.2', [], sni_tests()},
{'tlsv1.1', [], sni_tests()},
{'tlsv1', [], sni_tests()},
- {'sslv3', [], sni_tests()},
{'dtlsv1.2', [], sni_tests()},
{'dtlsv1', [], sni_tests()}
].
@@ -61,7 +59,8 @@ sni_tests() ->
dns_name,
ip_fallback,
no_ip_fallback,
- dns_name_reuse].
+ dns_name_reuse,
+ customize_hostname_check].
init_per_suite(Config0) ->
catch crypto:stop(),
@@ -88,12 +87,10 @@ end_per_suite(_) ->
ssl:stop(),
application:stop(crypto).
-init_per_testcase(TestCase, Config) when TestCase == ip_fallback;
- TestCase == no_ip_fallback;
- TestCase == dns_name_reuse ->
+init_per_testcase(customize_hostname_check, Config) ->
ssl_test_lib:ct_log_supported_protocol_versions(Config),
- ct:log("Ciphers: ~p~n ", [ ssl:cipher_suites()]),
- ct:timetrap({seconds, 20}),
+ ssl_test_lib:clean_start(),
+ ct:timetrap({seconds, 5}),
Config;
init_per_testcase(_TestCase, Config) ->
ssl_test_lib:ct_log_supported_protocol_versions(Config),
@@ -236,7 +233,60 @@ dns_name_reuse(Config) ->
{mfa, {ssl_test_lib, session_info_result, []}},
{from, self()}, {options, [{verify, verify_peer} | ClientConf]}]),
- ssl_test_lib:check_client_alert(Client1, handshake_failure).
+ ssl_test_lib:check_client_alert(Client1, handshake_failure),
+ ssl_test_lib:close(Client0).
+
+
+customize_hostname_check() ->
+ [{doc,"Test option customize_hostname_check."}].
+customize_hostname_check(Config) when is_list(Config) ->
+ Ext = [#'Extension'{extnID = ?'id-ce-subjectAltName',
+ extnValue = [{dNSName, "*.example.org"}],
+ critical = false}
+ ],
+ #{server_config := ServerOpts0,
+ client_config := ClientOpts0} = ssl_test_lib:make_cert_chains_pem(rsa, [{server_chain,
+ [[],
+ [],
+ [{extensions, Ext}]
+ ]}],
+ Config, "https_hostname_convention"),
+ ClientOpts = ssl_test_lib:ssl_options(ClientOpts0, Config),
+ ServerOpts = ssl_test_lib:ssl_options(ServerOpts0, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ CustomFun = public_key:pkix_verify_hostname_match_fun(https),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options,
+ [{verify, verify_peer},
+ {server_name_indication, "other.example.org"},
+ {customize_hostname_check,
+ [{match_fun, CustomFun}]} | ClientOpts]
+ }]),
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}},
+
+ Client1 = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {options, [{verify, verify_peer},
+ {server_name_indication, "other.example.org"} | ClientOpts]}
+ ]),
+ ssl_test_lib:check_client_alert(Server, Client1, handshake_failure).
%%--------------------------------------------------------------------
%% Internal Functions ------------------------------------------------
diff --git a/lib/ssl/test/ssl_socket_SUITE.erl b/lib/ssl/test/ssl_socket_SUITE.erl
new file mode 100644
index 0000000000..d648f2f9e1
--- /dev/null
+++ b/lib/ssl/test/ssl_socket_SUITE.erl
@@ -0,0 +1,437 @@
+%%
+%% Copyright Ericsson AB 2019-2019. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+
+-module(ssl_socket_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("public_key/include/public_key.hrl").
+
+-define(SLEEP, 500).
+%%--------------------------------------------------------------------
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
+all() ->
+ [
+ {group, tls},
+ {group, dtls}
+ ].
+
+groups() ->
+ [
+ {tls,[], socket_tests() ++ raw_inet_opt()},
+ {dtls,[], socket_tests()}
+ ].
+
+socket_tests() ->
+ [
+ getstat,
+ socket_options,
+ invalid_inet_get_option,
+ invalid_inet_get_option_not_list,
+ invalid_inet_get_option_improper_list,
+ invalid_inet_set_option,
+ invalid_inet_set_option_not_list,
+ invalid_inet_set_option_improper_list
+ ].
+
+raw_inet_opt() ->
+ [
+ raw_inet_option
+ ].
+
+
+init_per_suite(Config0) ->
+ catch crypto:stop(),
+ try crypto:start() of
+ ok ->
+ ssl_test_lib:clean_start(),
+ ssl_test_lib:make_rsa_cert(Config0)
+ catch _:_ ->
+ {skip, "Crypto did not start"}
+ end.
+
+end_per_suite(_Config) ->
+ ssl:stop(),
+ application:unload(ssl),
+ application:stop(crypto).
+
+init_per_group(dtls, Config) ->
+ [{protocol_opts, [{protocol, dtls}]} | proplists:delete(protocol_opts, Config)];
+init_per_group(tls, Config) ->
+ [{protocol_opts, [{protocol, tls}]} | proplists:delete(protocol_opts, Config)];
+init_per_group(_GroupName, Config) ->
+ [{client_type, erlang},
+ {server_type, erlang} | Config].
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+init_per_testcase(raw_inet_option, Config) ->
+ ct:timetrap({seconds, 5}),
+ case os:type() of
+ {unix,linux} ->
+ Config;
+ _ ->
+ {skip, "Raw options are platform-specific"}
+ end;
+init_per_testcase(_TestCase, Config) ->
+ ct:timetrap({seconds, 5}),
+ Config.
+
+end_per_testcase(_TestCase, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
+
+getstat() ->
+ [{doc,"Test API function getstat/2"}].
+
+getstat(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_rsa_opts, Config),
+ ServerOpts = ?config(server_rsa_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server1 =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result, []}},
+ {options, [{active, false} | ServerOpts]}]),
+ Port1 = ssl_test_lib:inet_port(Server1),
+ Server2 =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result, []}},
+ {options, [{active, false} | ServerOpts]}]),
+ Port2 = ssl_test_lib:inet_port(Server2),
+ {ok, ActiveC} = rpc:call(ClientNode, ssl, connect,
+ [Hostname,Port1,[{active, once}|ClientOpts]]),
+ {ok, PassiveC} = rpc:call(ClientNode, ssl, connect,
+ [Hostname,Port2,[{active, false}|ClientOpts]]),
+
+ ct:log("Testcase ~p, Client ~p Servers ~p, ~p ~n",
+ [self(), self(), Server1, Server2]),
+
+ %% We only check that the values are non-zero initially
+ %% (due to the handshake), and that sending more changes the values.
+
+ %% Passive socket.
+
+ {ok, InitialStats} = ssl:getstat(PassiveC),
+ ct:pal("InitialStats ~p~n", [InitialStats]),
+ [true] = lists:usort([0 =/= proplists:get_value(Name, InitialStats)
+ || Name <- [recv_cnt, recv_oct, recv_avg, recv_max, send_cnt, send_oct, send_avg, send_max]]),
+
+ ok = ssl:send(PassiveC, "Hello world"),
+ wait_for_send(PassiveC),
+ {ok, SStats} = ssl:getstat(PassiveC, [send_cnt, send_oct]),
+ ct:pal("SStats ~p~n", [SStats]),
+ [true] = lists:usort([proplists:get_value(Name, SStats) =/= proplists:get_value(Name, InitialStats)
+ || Name <- [send_cnt, send_oct]]),
+
+ %% Active socket.
+
+ {ok, InitialAStats} = ssl:getstat(ActiveC),
+ ct:pal("InitialAStats ~p~n", [InitialAStats]),
+ [true] = lists:usort([0 =/= proplists:get_value(Name, InitialAStats)
+ || Name <- [recv_cnt, recv_oct, recv_avg, recv_max, send_cnt, send_oct, send_avg, send_max]]),
+
+ _ = receive
+ {ssl, ActiveC, _} ->
+ ok
+ after
+ ?SLEEP ->
+ exit(timeout)
+ end,
+
+ ok = ssl:send(ActiveC, "Hello world"),
+ wait_for_send(ActiveC),
+ {ok, ASStats} = ssl:getstat(ActiveC, [send_cnt, send_oct]),
+ ct:pal("ASStats ~p~n", [ASStats]),
+ [true] = lists:usort([proplists:get_value(Name, ASStats) =/= proplists:get_value(Name, InitialAStats)
+ || Name <- [send_cnt, send_oct]]),
+
+ ok.
+%%--------------------------------------------------------------------
+socket_options() ->
+ [{doc,"Test API function getopts/2 and setopts/2"}].
+
+socket_options(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Values = [{mode, list}, {active, true}],
+ %% Shall be the reverse order of Values!
+ Options = [active, mode],
+
+ NewValues = [{mode, binary}, {active, once}],
+ %% Shall be the reverse order of NewValues!
+ NewOptions = [active, mode],
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, socket_options_result,
+ [Options, Values, NewOptions, NewValues]}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, socket_options_result,
+ [Options, Values, NewOptions, NewValues]}},
+ {options, ClientOpts}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+
+ {ok, Listen} = ssl:listen(0, ServerOpts),
+ {ok,[{mode,list}]} = ssl:getopts(Listen, [mode]),
+ ok = ssl:setopts(Listen, [{mode, binary}]),
+ {ok,[{mode, binary}]} = ssl:getopts(Listen, [mode]),
+ {ok,[{recbuf, _}]} = ssl:getopts(Listen, [recbuf]),
+ ssl:close(Listen).
+
+%%--------------------------------------------------------------------
+raw_inet_option() ->
+ [{doc,"Ensure that a single 'raw' option is passed to ssl:listen correctly."}].
+
+raw_inet_option(Config) when is_list(Config) ->
+ % 'raw' option values are platform-specific; these are the Linux values:
+ IpProtoTcp = 6,
+ % Use TCP_KEEPIDLE, because (e.g.) TCP_MAXSEG can't be read back reliably.
+ TcpKeepIdle = 4,
+ KeepAliveTimeSecs = 55,
+ LOptions = [{raw, IpProtoTcp, TcpKeepIdle, <<KeepAliveTimeSecs:32/native>>}],
+ {ok, LSocket} = ssl:listen(0, LOptions),
+ % Per http://www.erlang.org/doc/man/inet.html#getopts-2, we have to specify
+ % exactly which raw option we want, and the size of the buffer.
+ {ok, [{raw, IpProtoTcp, TcpKeepIdle, <<KeepAliveTimeSecs:32/native>>}]} =
+ ssl:getopts(LSocket, [{raw, IpProtoTcp, TcpKeepIdle, 4}]).
+
+%%--------------------------------------------------------------------
+
+invalid_inet_get_option() ->
+ [{doc,"Test handling of invalid inet options in getopts"}].
+
+invalid_inet_get_option(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, get_invalid_inet_option, []}},
+ {options, 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, no_result, []}},
+ {options, ClientOpts}]),
+
+ ct:log("Testcase ~p, Client ~p Server ~p ~n",
+ [self(), Client, Server]),
+
+ ssl_test_lib:check_result(Server, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+invalid_inet_get_option_not_list() ->
+ [{doc,"Test handling of invalid type in getopts"}].
+
+invalid_inet_get_option_not_list(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, get_invalid_inet_option_not_list, []}},
+ {options, 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, no_result, []}},
+ {options, ClientOpts}]),
+
+ ct:log("Testcase ~p, Client ~p Server ~p ~n",
+ [self(), Client, Server]),
+
+ ssl_test_lib:check_result(Server, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+invalid_inet_get_option_improper_list() ->
+ [{doc,"Test handling of invalid type in getopts"}].
+
+invalid_inet_get_option_improper_list(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, get_invalid_inet_option_improper_list, []}},
+ {options, 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, no_result, []}},
+ {options, ClientOpts}]),
+
+ ct:log("Testcase ~p, Client ~p Server ~p ~n",
+ [self(), Client, Server]),
+
+ ssl_test_lib:check_result(Server, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+invalid_inet_set_option() ->
+ [{doc,"Test handling of invalid inet options in setopts"}].
+
+invalid_inet_set_option(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, set_invalid_inet_option, []}},
+ {options, 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, no_result, []}},
+ {options, ClientOpts}]),
+
+ ct:log("Testcase ~p, Client ~p Server ~p ~n",
+ [self(), Client, Server]),
+
+ ssl_test_lib:check_result(Server, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+invalid_inet_set_option_not_list() ->
+ [{doc,"Test handling of invalid type in setopts"}].
+
+invalid_inet_set_option_not_list(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, set_invalid_inet_option_not_list, []}},
+ {options, 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, no_result, []}},
+ {options, ClientOpts}]),
+
+ ct:log("Testcase ~p, Client ~p Server ~p ~n",
+ [self(), Client, Server]),
+
+ ssl_test_lib:check_result(Server, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+invalid_inet_set_option_improper_list() ->
+ [{doc,"Test handling of invalid tye in setopts"}].
+
+invalid_inet_set_option_improper_list(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, set_invalid_inet_option_improper_list, []}},
+ {options, 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, no_result, []}},
+ {options, ClientOpts}]),
+
+ ct:log("Testcase ~p, Client ~p Server ~p ~n",
+ [self(), Client, Server]),
+
+ ssl_test_lib:check_result(Server, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+
+%%--------------------------------------------------------------------
+%% Internal functions ------------------------------------------------
+%%--------------------------------------------------------------------
+socket_options_result(Socket, Options, DefaultValues, NewOptions, NewValues) ->
+ %% Test get/set emulated opts
+ {ok, DefaultValues} = ssl:getopts(Socket, Options),
+ ssl:setopts(Socket, NewValues),
+ {ok, NewValues} = ssl:getopts(Socket, NewOptions),
+ %% Test get/set inet opts
+ {ok,[{reuseaddr, _}]} = ssl:getopts(Socket, [reuseaddr]),
+ {ok, All} = ssl:getopts(Socket, []),
+ ct:log("All opts ~p~n", [All]),
+ ok.
+
+get_invalid_inet_option(Socket) ->
+ {error, {options, {socket_options, foo, _}}} = ssl:getopts(Socket, [foo]),
+ ok.
+
+get_invalid_inet_option_not_list(Socket) ->
+ {error, {options, {socket_options, some_invalid_atom_here}}}
+ = ssl:getopts(Socket, some_invalid_atom_here),
+ ok.
+
+get_invalid_inet_option_improper_list(Socket) ->
+ {error, {options, {socket_options, foo,_}}} = ssl:getopts(Socket, [packet | foo]),
+ ok.
+
+set_invalid_inet_option(Socket) ->
+ {error, {options, {socket_options, {packet, foo}}}} = ssl:setopts(Socket, [{packet, foo}]),
+ {error, {options, {socket_options, {header, foo}}}} = ssl:setopts(Socket, [{header, foo}]),
+ {error, {options, {socket_options, {active, foo}}}} = ssl:setopts(Socket, [{active, foo}]),
+ {error, {options, {socket_options, {mode, foo}}}} = ssl:setopts(Socket, [{mode, foo}]),
+ ok.
+
+set_invalid_inet_option_not_list(Socket) ->
+ {error, {options, {not_a_proplist, some_invalid_atom_here}}}
+ = ssl:setopts(Socket, some_invalid_atom_here),
+ ok.
+
+set_invalid_inet_option_improper_list(Socket) ->
+ {error, {options, {not_a_proplist, [{packet, 0} | {foo, 2}]}}} =
+ ssl:setopts(Socket, [{packet, 0} | {foo, 2}]),
+ ok.
+
+wait_for_send(Socket) ->
+ %% Make sure TLS process processed send message event
+ _ = ssl:connection_information(Socket).
+
diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl
index c791f438d0..cb528f185a 100644
--- a/lib/ssl/test/ssl_test_lib.erl
+++ b/lib/ssl/test/ssl_test_lib.erl
@@ -218,6 +218,55 @@ start_server_transport_control(Args) ->
Result
end.
+start_erlang_client_and_openssl_server_with_opts(Config, ErlangClientOpts, OpensslServerOpts, Data, Callback) ->
+ process_flag(trap_exit, true),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
+ ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
+ ClientOpts = ErlangClientOpts ++ ClientOpts0,
+
+ {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "From openssl to erlang",
+
+ Port = ssl_test_lib:inet_port(node()),
+ CaCertFile = proplists:get_value(cacertfile, ServerOpts),
+ CertFile = proplists:get_value(certfile, ServerOpts),
+ KeyFile = proplists:get_value(keyfile, ServerOpts),
+ Version = ssl_test_lib:protocol_version(Config),
+
+ Exe = "openssl",
+ Args = case OpensslServerOpts of
+ [] ->
+ ["s_server", "-accept",
+ integer_to_list(Port), ssl_test_lib:version_flag(Version),
+ "-CAfile", CaCertFile,
+ "-cert", CertFile,"-key", KeyFile];
+ [Opt, Value] ->
+ ["s_server", Opt, Value, "-accept",
+ integer_to_list(Port), ssl_test_lib:version_flag(Version),
+ "-CAfile", CaCertFile,
+ "-cert", CertFile,"-key", KeyFile]
+ end,
+
+ OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
+
+ ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ active_recv, [length(Data)]}},
+ {options, ClientOpts}]),
+
+ Callback(Client, OpensslPort),
+
+ %% Clean close down! Server needs to be closed first !!
+ ssl_test_lib:close_port(OpensslPort),
+
+ ssl_test_lib:close(Client),
+ process_flag(trap_exit, false).
+
transport_accept_abuse(Opts) ->
Port = proplists:get_value(port, Opts),
@@ -233,6 +282,34 @@ transport_accept_abuse(Opts) ->
_ = ssl:handshake(AcceptSocket, infinity),
Pid ! {self(), ok}.
+start_erlang_server_and_openssl_client_with_opts(Config, ErlangServerOpts, OpenSSLClientOpts, Data, Callback) ->
+ process_flag(trap_exit, true),
+ ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ ServerOpts = ErlangServerOpts ++ ServerOpts0,
+
+ {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, active_recv, [length(Data)]}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Version = ssl_test_lib:protocol_version(Config),
+
+ Exe = "openssl",
+ Args = ["s_client"] ++ OpenSSLClientOpts ++ ["-msg", "-connect",
+ hostname_format(Hostname) ++ ":" ++ integer_to_list(Port),
+ ssl_test_lib:version_flag(Version)],
+
+ OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
+
+ Callback(Server, OpenSslPort),
+
+ ssl_test_lib:close(Server),
+
+ ssl_test_lib:close_port(OpenSslPort),
+ process_flag(trap_exit, false).
transport_switch_control(Opts) ->
Port = proplists:get_value(port, Opts),
@@ -416,7 +493,10 @@ check_result(Server, ServerMsg, Client, ClientMsg) ->
ct:log("~p:~p~n Openssl ~s~n",[?MODULE,?LINE, Debug]),
check_result(Server, ServerMsg, Client, ClientMsg);
{Port,closed} when is_port(Port) ->
- ct:log("~p:~p~n Openssl port ~n",[?MODULE,?LINE]),
+ ct:log("~p:~p~n Openssl port closed ~n",[?MODULE,?LINE]),
+ check_result(Server, ServerMsg, Client, ClientMsg);
+ {'EXIT', epipe} ->
+ ct:log("~p:~p~n Openssl port died ~n",[?MODULE,?LINE]),
check_result(Server, ServerMsg, Client, ClientMsg);
Unexpected ->
Reason = {{expected, {Client, ClientMsg}},
@@ -571,8 +651,7 @@ cert_options(Config) ->
"badcert.pem"]),
BadKeyFile = filename:join([proplists:get_value(priv_dir, Config),
"badkey.pem"]),
- PskSharedSecret = <<1,2,3,4,5,6,7,8,9,10,11,12,13,14,15>>,
-
+
[{client_opts, [{cacertfile, ClientCaCertFile},
{certfile, ClientCertFile},
{keyfile, ClientKeyFile}]},
@@ -586,30 +665,6 @@ cert_options(Config) ->
{ssl_imp, new}]},
{server_opts, [{ssl_imp, new},{reuseaddr, true}, {cacertfile, ServerCaCertFile},
{certfile, ServerCertFile}, {keyfile, ServerKeyFile}]},
- {client_psk, [{ssl_imp, new},
- {psk_identity, "Test-User"},
- {user_lookup_fun, {fun user_lookup/3, PskSharedSecret}}]},
- {server_psk, [{ssl_imp, new},{reuseaddr, true},
- {certfile, ServerCertFile}, {keyfile, ServerKeyFile},
- {user_lookup_fun, {fun user_lookup/3, PskSharedSecret}}]},
- {server_psk_hint, [{ssl_imp, new},{reuseaddr, true},
- {certfile, ServerCertFile}, {keyfile, ServerKeyFile},
- {psk_identity, "HINT"},
- {user_lookup_fun, {fun user_lookup/3, PskSharedSecret}}]},
- {server_psk_anon, [{ssl_imp, new},{reuseaddr, true},
- {user_lookup_fun, {fun user_lookup/3, PskSharedSecret}}]},
- {server_psk_anon_hint, [{ssl_imp, new},{reuseaddr, true},
- {psk_identity, "HINT"},
- {user_lookup_fun, {fun user_lookup/3, PskSharedSecret}}]},
- {client_srp, [{ssl_imp, new},
- {srp_identity, {"Test-User", "secret"}}]},
- {server_srp, [{ssl_imp, new},{reuseaddr, true},
- {certfile, ServerCertFile}, {keyfile, ServerKeyFile},
- {user_lookup_fun, {fun user_lookup/3, undefined}},
- {ciphers, srp_suites()}]},
- {server_srp_anon, [{ssl_imp, new},{reuseaddr, true},
- {user_lookup_fun, {fun user_lookup/3, undefined}},
- {ciphers, srp_anon_suites()}]},
{server_verification_opts, [{ssl_imp, new},{reuseaddr, true},
{cacertfile, ClientCaCertFile},
{certfile, ServerCertFile}, {keyfile, ServerKeyFile}]},
@@ -650,15 +705,32 @@ make_dsa_cert(Config) ->
[{server_dsa_opts, ServerConf},
{server_dsa_verify_opts, [{verify, verify_peer} | ServerConf]},
- {client_dsa_opts, ClientConf},
- {server_srp_dsa, [{user_lookup_fun, {fun user_lookup/3, undefined}},
- {ciphers, srp_dss_suites()} | ServerConf]},
- {client_srp_dsa, [{srp_identity, {"Test-User", "secret"}}
- | ClientConf]}
+ {client_dsa_opts, ClientConf}
| Config];
false ->
Config
end.
+
+
+make_cert_chains_der(Alg, UserConf) ->
+ ClientChain = proplists:get_value(client_chain, UserConf, default_cert_chain_conf()),
+ ServerChain = proplists:get_value(server_chain, UserConf, default_cert_chain_conf()),
+ CertChainConf = gen_conf(Alg, Alg, ClientChain, ServerChain),
+ public_key:pkix_test_data(CertChainConf).
+
+make_cert_chains_pem(Alg, UserConf, Config, Suffix) ->
+ ClientChain = proplists:get_value(client_chain, UserConf, default_cert_chain_conf()),
+ ServerChain = proplists:get_value(server_chain, UserConf, default_cert_chain_conf()),
+ CertChainConf = gen_conf(Alg, Alg, ClientChain, ServerChain),
+ ClientFileBase = filename:join([proplists:get_value(priv_dir, Config), atom_to_list(Alg) ++ Suffix]),
+ ServerFileBase = filename:join([proplists:get_value(priv_dir, Config), atom_to_list(Alg) ++ Suffix]),
+ GenCertData = public_key:pkix_test_data(CertChainConf),
+ Conf = x509_test:gen_pem_config_files(GenCertData, ClientFileBase, ServerFileBase),
+ CConf = proplists:get_value(client_config, Conf),
+ SConf = proplists:get_value(server_config, Conf),
+ #{server_config => SConf,
+ client_config => CConf}.
+
make_rsa_cert_chains(UserConf, Config, Suffix) ->
ClientChain = proplists:get_value(client_chain, UserConf, default_cert_chain_conf()),
ServerChain = proplists:get_value(server_chain, UserConf, default_cert_chain_conf()),
@@ -1156,6 +1228,52 @@ basic_test(COpts, SOpts, Config) ->
gen_check_result(Server, SType, Client, CType),
stop(Server, Client).
+basic_alert(ClientOpts, ServerOpts, Config, Alert) ->
+ SType = proplists:get_value(server_type, Config),
+ CType = proplists:get_value(client_type, Config),
+ run_basic_alert(SType, CType, ClientOpts, ServerOpts, Config, Alert).
+
+run_basic_alert(erlang, erlang, ClientOpts, ServerOpts, Config, Alert) ->
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = start_server_error([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {options, ServerOpts}]),
+
+ Port = inet_port(Server),
+
+ Client = start_client_error([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {options, ClientOpts}]),
+
+ check_server_alert(Server, Client, Alert);
+run_basic_alert(openssl = SType, erlang, ClientOpts, ServerOpts, Config, Alert) ->
+ {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
+ {_Server, Port} = start_server(SType, ClientOpts, ServerOpts, Config),
+ ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)),
+ Client = start_client_error([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {options, ClientOpts}]),
+
+ check_client_alert(Client, Alert);
+run_basic_alert(erlang, openssl = CType, ClientOpts, ServerOpts, Config, Alert) ->
+ {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = start_server_error([{node, ServerNode}, {port, 0},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {options, ServerOpts}]),
+ Port = inet_port(Server),
+ start_client(CType, Port, ClientOpts, Config),
+
+ check_server_alert(Server, Alert).
+
+
ecc_test(Expect, COpts, SOpts, CECCOpts, SECCOpts, Config) ->
{Server, Port} = start_server_ecc(erlang, SOpts, Expect, SECCOpts, Config),
Client = start_client_ecc(erlang, Port, COpts, Expect, CECCOpts, Config),
@@ -1197,15 +1315,22 @@ start_basic_client(openssl, Version, Port, ClientOpts) ->
OpenSslPort.
start_client(openssl, Port, ClientOpts, Config) ->
- Cert = proplists:get_value(certfile, ClientOpts),
- Key = proplists:get_value(keyfile, ClientOpts),
- CA = proplists:get_value(cacertfile, ClientOpts),
Version = ssl_test_lib:protocol_version(Config),
Exe = "openssl",
- Args0 = ["s_client", "-verify", "2", "-port", integer_to_list(Port),
- ssl_test_lib:version_flag(Version),
- "-cert", Cert, "-CAfile", CA,
- "-key", Key, "-host","localhost", "-msg", "-debug"],
+ Ciphers = proplists:get_value(ciphers, ClientOpts, ssl:cipher_suites(default,Version)),
+ Groups0 = proplists:get_value(groups, ClientOpts),
+ CertArgs = openssl_cert_options(ClientOpts, client),
+ Exe = "openssl",
+ Args0 = case Groups0 of
+ undefined ->
+ ["s_client", "-verify", "2", "-port", integer_to_list(Port), cipher_flag(Version),
+ ciphers(Ciphers, Version),
+ ssl_test_lib:version_flag(Version)] ++ CertArgs ++ ["-msg", "-debug"];
+ Group ->
+ ["s_client", "-verify", "2", "-port", integer_to_list(Port), cipher_flag(Version),
+ ciphers(Ciphers, Version), "-groups", Group,
+ ssl_test_lib:version_flag(Version)] ++ CertArgs ++ ["-msg", "-debug"]
+ end,
Args = maybe_force_ipv4(Args0),
OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
true = port_command(OpenSslPort, "Hello world"),
@@ -1257,24 +1382,54 @@ start_server(openssl, ClientOpts, ServerOpts, Config) ->
Port = inet_port(node()),
Version = protocol_version(Config),
Exe = "openssl",
- CertArgs = openssl_cert_options(ServerOpts),
- [Cipher|_] = proplists:get_value(ciphers, ClientOpts, ssl:cipher_suites(default,Version)),
- Args = ["s_server", "-accept", integer_to_list(Port), "-cipher",
- ssl_cipher_format:suite_map_to_openssl_str(Cipher),
- ssl_test_lib:version_flag(Version)] ++ CertArgs ++ ["-msg", "-debug"],
+ CertArgs = openssl_cert_options(ServerOpts, server),
+ Ciphers = proplists:get_value(ciphers, ClientOpts, ssl:cipher_suites(default,Version)),
+ Groups0 = proplists:get_value(groups, ServerOpts),
+ Args = case Groups0 of
+ undefined ->
+ ["s_server", "-accept", integer_to_list(Port), cipher_flag(Version),
+ ciphers(Ciphers, Version),
+ ssl_test_lib:version_flag(Version)] ++ CertArgs ++ ["-msg", "-debug"];
+ Group ->
+ ["s_server", "-accept", integer_to_list(Port), cipher_flag(Version),
+ ciphers(Ciphers, Version), "-groups", Group,
+ ssl_test_lib:version_flag(Version)] ++ CertArgs ++ ["-msg", "-debug"]
+ end,
OpenSslPort = portable_open_port(Exe, Args),
true = port_command(OpenSslPort, "Hello world"),
{OpenSslPort, Port};
start_server(erlang, _, ServerOpts, Config) ->
{_, ServerNode, _} = ssl_test_lib:run_where(Config),
KeyEx = proplists:get_value(check_keyex, Config, false),
+ Versions = protocol_versions(Config),
Server = start_server([{node, ServerNode}, {port, 0},
{from, self()},
{mfa, {ssl_test_lib,
check_key_exchange_send_active,
[KeyEx]}},
- {options, [{verify, verify_peer} | ServerOpts]}]),
+ {options, [{verify, verify_peer}, {versions, Versions} | ServerOpts]}]),
{Server, inet_port(Server)}.
+
+cipher_flag('tlsv1.3') ->
+ "-ciphersuites";
+cipher_flag(_) ->
+ "-cipher".
+
+ciphers(Ciphers, Version) ->
+ Strs = [ssl_cipher_format:suite_map_to_openssl_str(Cipher) || Cipher <- Ciphers],
+ ciphers_concat(Version, Strs, "").
+
+ciphers_concat(_, [], [":" | Acc]) ->
+ lists:append(lists:reverse(Acc));
+ciphers_concat('tlsv1.3' = Version, [Head| Tail], Acc) ->
+ case Head of
+ "TLS" ++ _ ->
+ ciphers_concat(Version, Tail, [":", Head | Acc]);
+ _ ->
+ ciphers_concat(Version, Tail, Acc)
+ end;
+ciphers_concat(Version, [Head| Tail], Acc) ->
+ ciphers_concat(Version, Tail, [":", Head | Acc]).
start_server_with_raw_key(erlang, ServerOpts, Config) ->
{_, ServerNode, _} = ssl_test_lib:run_where(Config),
@@ -1329,23 +1484,31 @@ stop(Client, Server) ->
close(Client).
-openssl_cert_options(ServerOpts) ->
- Cert = proplists:get_value(certfile, ServerOpts, undefined),
- Key = proplists:get_value(keyfile, ServerOpts, undefined),
- CA = proplists:get_value(cacertfile, ServerOpts, undefined),
+openssl_cert_options(Opts, Role) ->
+ Cert = proplists:get_value(certfile, Opts, undefined),
+ Key = proplists:get_value(keyfile, Opts, undefined),
+ CA = proplists:get_value(cacertfile, Opts, undefined),
case CA of
undefined ->
case cert_option("-cert", Cert) ++ cert_option("-key", Key) of
- [] ->
+ [] when Role == server ->
["-nocert"];
Other ->
Other
end;
_ ->
cert_option("-cert", Cert) ++ cert_option("-CAfile", CA) ++
- cert_option("-key", Key) ++ ["-verify", "2"]
+ cert_option("-key", Key) ++ openssl_verify(Opts) ++ ["2"]
end.
+openssl_verify(Opts) ->
+ case proplists:get_value(fail_if_no_peer_cert, Opts, undefined) of
+ true ->
+ ["-Verify"];
+ _ ->
+ ["-verify"]
+ end.
+
cert_option(_, undefined) ->
[];
cert_option(Opt, Value) ->
@@ -2005,12 +2168,81 @@ check_sane_openssl_version(Version) ->
false;
{'tlsv1.1', "OpenSSL 0" ++ _} ->
false;
+ {'tlsv1', "OpenSSL 0" ++ _} ->
+ false;
{_, _} ->
true
end;
false ->
false
end.
+check_sane_openssl_renegotaite(Config, Version) when Version == 'tlsv1.1';
+ Version == 'tlsv1.2' ->
+ case os:cmd("openssl version") of
+ "OpenSSL 1.0.1c" ++ _ ->
+ {skip, "Known renegotiation bug in OpenSSL"};
+ "OpenSSL 1.0.1b" ++ _ ->
+ {skip, "Known renegotiation bug in OpenSSL"};
+ "OpenSSL 1.0.1a" ++ _ ->
+ {skip, "Known renegotiation bug in OpenSSL"};
+ "OpenSSL 1.0.1 " ++ _ ->
+ {skip, "Known renegotiation bug in OpenSSL"};
+ _ ->
+ check_sane_openssl_renegotaite(Config)
+ end;
+check_sane_openssl_renegotaite(Config, 'sslv3') ->
+ case os:cmd("openssl version") of
+ "OpenSSL 1" ++ _ ->
+ {skip, "Known renegotiation bug with sslv3 in OpenSSL"};
+ _ ->
+ check_sane_openssl_renegotaite(Config)
+ end;
+check_sane_openssl_renegotaite(Config, _) ->
+ check_sane_openssl_renegotaite(Config).
+
+check_sane_openssl_renegotaite(Config) ->
+ case os:cmd("openssl version") of
+ "OpenSSL 1.0.0" ++ _ ->
+ {skip, "Known renegotiation bug in OpenSSL"};
+ "OpenSSL 0.9.8" ++ _ ->
+ {skip, "Known renegotiation bug in OpenSSL"};
+ "OpenSSL 0.9.7" ++ _ ->
+ {skip, "Known renegotiation bug in OpenSSL"};
+ "LibreSSL 2." ++ _ ->
+ {skip, "Known renegotiation bug in LibreSSL"};
+
+ _ ->
+ Config
+ end.
+
+openssl_allows_client_renegotaite(Config) ->
+ case os:cmd("openssl version") of
+ "OpenSSL 1.1" ++ _ ->
+ {skip, "OpenSSL does not allow client renegotiation"};
+ "LibreSSL 2" ++ _ ->
+ {skip, "LibreSSL does not allow client renegotiation"};
+ _ ->
+ Config
+ end.
+
+workaround_openssl_s_clinent() ->
+ %% http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=683159
+ %% https://bugs.archlinux.org/task/33919
+ %% Bug seems to manifests it self if TLS version is not
+ %% explicitly specified
+ case os:cmd("openssl version") of
+ "OpenSSL 1.0.1c" ++ _ ->
+ ["-no_tls1_2"];
+ "OpenSSL 1.0.1d" ++ _ ->
+ ["-no_tls1_2"];
+ "OpenSSL 1.0.1e" ++ _ ->
+ ["-no_tls1_2"];
+ "OpenSSL 1.0.1f" ++ _ ->
+ ["-no_tls1_2"];
+ _ ->
+ []
+ end.
+
enough_openssl_crl_support("OpenSSL 0." ++ _) -> false;
enough_openssl_crl_support(_) -> true.
@@ -2060,8 +2292,8 @@ filter_suites(Ciphers0, AtomVersion) ->
++ ssl_cipher:anonymous_suites(Version)
++ ssl_cipher:psk_suites(Version)
++ ssl_cipher:psk_suites_anon(Version)
- ++ ssl_cipher:srp_suites()
- ++ ssl_cipher:srp_suites_anon()
+ ++ ssl_cipher:srp_suites(Version)
+ ++ ssl_cipher:srp_suites_anon(Version)
++ ssl_cipher:rc4_suites(Version),
Supported1 = ssl_cipher:filter_suites(Supported0),
Supported2 = [ssl_cipher_format:suite_bin_to_map(S) || S <- Supported1],
@@ -2178,6 +2410,14 @@ protocol_version(Config, atom) ->
tls_record:protocol_version(protocol_version(Config, tuple))
end.
+protocol_versions(Config) ->
+ Version = protocol_version(Config),
+ case Version of
+ 'tlsv1.3' -> %% TLS-1.3 servers shall also support 1.2
+ ['tlsv1.3', 'tlsv1.2'];
+ _ ->
+ [Version]
+ end.
protocol_options(Config, Options) ->
Protocol = proplists:get_value(protocol, Config, tls),
{Protocol, Opts} = lists:keyfind(Protocol, 1, Options),
@@ -2504,3 +2744,59 @@ kill_openssl() ->
{win32, _} ->
os:cmd("cmd.exe /C \"taskkill /IM openssl.exe /F\"")
end.
+
+hostname_format(Hostname) ->
+ case lists:member($., Hostname) of
+ true ->
+ Hostname;
+ false ->
+ "localhost"
+ end.
+
+erlang_ssl_receive_and_assert_negotiated_protocol(Socket, Protocol, Data) ->
+ case ssl:negotiated_protocol(Socket) of
+ {ok, Protocol} ->
+ active_recv(Socket, length(Data));
+ Result ->
+ {error, {{expected, Protocol}, {got, Result}}}
+ end.
+
+check_openssl_npn_support(Config) ->
+ HelpText = os:cmd("openssl s_client --help"),
+ case string:str(HelpText, "nextprotoneg") of
+ 0 ->
+ {skip, "Openssl not compiled with nextprotoneg support"};
+ _ ->
+ Config
+ end.
+
+new_config(PrivDir, ServerOpts0) ->
+ CaCertFile = proplists:get_value(cacertfile, ServerOpts0),
+ CertFile = proplists:get_value(certfile, ServerOpts0),
+ KeyFile = proplists:get_value(keyfile, ServerOpts0),
+ NewCaCertFile = filename:join(PrivDir, "new_ca.pem"),
+ NewCertFile = filename:join(PrivDir, "new_cert.pem"),
+ NewKeyFile = filename:join(PrivDir, "new_key.pem"),
+ file:copy(CaCertFile, NewCaCertFile),
+ file:copy(CertFile, NewCertFile),
+ file:copy(KeyFile, NewKeyFile),
+ ServerOpts1 = proplists:delete(cacertfile, ServerOpts0),
+ ServerOpts2 = proplists:delete(certfile, ServerOpts1),
+ ServerOpts = proplists:delete(keyfile, ServerOpts2),
+
+ {ok, PEM} = file:read_file(NewCaCertFile),
+ ct:log("CA file content: ~p~n", [public_key:pem_decode(PEM)]),
+
+ [{cacertfile, NewCaCertFile}, {certfile, NewCertFile},
+ {keyfile, NewKeyFile} | ServerOpts].
+
+
+openssl_sane_dtls_alpn() ->
+ case os:cmd("openssl version") of
+ "OpenSSL 1.1.0g" ++ _ ->
+ false;
+ "OpenSSL 1.1.1a" ++ _ ->
+ false;
+ _->
+ openssl_sane_dtls()
+ end.
diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl
deleted file mode 100644
index 31d9af5298..0000000000
--- a/lib/ssl/test/ssl_to_openssl_SUITE.erl
+++ /dev/null
@@ -1,2021 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2008-2018. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
-%%
-
--module(ssl_to_openssl_SUITE).
-
-%% Note: This directive should only be used in test suites.
--compile(export_all).
-
--include_lib("common_test/include/ct.hrl").
-
--define(SLEEP, 1000).
--define(OPENSSL_RENEGOTIATE, "R\n").
--define(OPENSSL_QUIT, "Q\n").
--define(OPENSSL_GARBAGE, "P\n").
--define(EXPIRE, 10).
-
-%%--------------------------------------------------------------------
-%% Common Test interface functions -----------------------------------
-%%--------------------------------------------------------------------
-
-all() ->
- case ssl_test_lib:openssl_sane_dtls() of
- true ->
- [{group, 'tlsv1.2'},
- {group, 'tlsv1.1'},
- {group, 'tlsv1'},
- {group, 'sslv3'},
- {group, 'dtlsv1.2'},
- {group, 'dtlsv1'}];
- false ->
- [{group, 'tlsv1.2'},
- {group, 'tlsv1.1'},
- {group, 'tlsv1'},
- {group, 'sslv3'}]
- end.
-
-groups() ->
- case ssl_test_lib:openssl_sane_dtls() of
- true ->
- [{'tlsv1.2', [], all_versions_tests() ++ alpn_tests() ++ npn_tests() ++ sni_server_tests()},
- {'tlsv1.1', [], all_versions_tests() ++ alpn_tests() ++ npn_tests() ++ sni_server_tests()},
- {'tlsv1', [], all_versions_tests()++ alpn_tests() ++ npn_tests() ++ sni_server_tests()},
- {'sslv3', [], all_versions_tests()},
- {'dtlsv1.2', [], dtls_all_versions_tests()},
- {'dtlsv1', [], dtls_all_versions_tests()}
- ];
- false ->
- [{'tlsv1.2', [], all_versions_tests() ++ alpn_tests() ++ npn_tests() ++ sni_server_tests()},
- {'tlsv1.1', [], all_versions_tests() ++ alpn_tests() ++ npn_tests() ++ sni_server_tests()},
- {'tlsv1', [], all_versions_tests()++ alpn_tests() ++ npn_tests() ++ sni_server_tests()},
- {'sslv3', [], all_versions_tests()}
- ]
- end.
-
-all_versions_tests() ->
- [
- erlang_client_openssl_server,
- erlang_server_openssl_client,
- erlang_client_openssl_server_dsa_cert,
- erlang_server_openssl_client_dsa_cert,
- erlang_client_openssl_server_anon,
- erlang_server_openssl_client_anon,
- erlang_server_openssl_client_anon_with_cert,
- erlang_server_openssl_client_reuse_session,
- erlang_client_openssl_server_renegotiate,
- erlang_client_openssl_server_renegotiate_after_client_data,
- erlang_client_openssl_server_nowrap_seqnum,
- erlang_server_openssl_client_nowrap_seqnum,
- erlang_client_openssl_server_no_server_ca_cert,
- erlang_client_openssl_server_client_cert,
- erlang_server_openssl_client_client_cert,
- ciphers_rsa_signed_certs,
- ciphers_dsa_signed_certs,
- erlang_client_bad_openssl_server,
- expired_session,
- ssl2_erlang_server_openssl_client
- ].
-
-dtls_all_versions_tests() ->
- case ssl_test_lib:openssl_sane_client_cert() of
- true ->
- [erlang_server_openssl_client_client_cert,
- erlang_client_openssl_server_no_server_ca_cert,
- erlang_client_openssl_server_client_cert
- | dtls_all_versions_tests_2()];
- false ->
- dtls_all_versions_tests_2()
- end.
-
-dtls_all_versions_tests_2() ->
- [erlang_client_openssl_server,
- erlang_server_openssl_client,
- erlang_client_openssl_server_dsa_cert,
- erlang_server_openssl_client_dsa_cert,
- erlang_client_openssl_server_anon,
- erlang_server_openssl_client_anon,
- erlang_server_openssl_client_anon_with_cert,
- erlang_server_openssl_client_reuse_session,
- erlang_client_openssl_server_renegotiate,
- erlang_client_openssl_server_nowrap_seqnum,
- erlang_server_openssl_client_nowrap_seqnum,
- ciphers_rsa_signed_certs,
- ciphers_dsa_signed_certs
- %%expired_session
- ].
-
-alpn_tests() ->
- [erlang_client_alpn_openssl_server_alpn,
- erlang_server_alpn_openssl_client_alpn,
- erlang_client_alpn_openssl_server,
- erlang_client_openssl_server_alpn,
- erlang_server_alpn_openssl_client,
- erlang_server_openssl_client_alpn,
- erlang_client_alpn_openssl_server_alpn_renegotiate,
- erlang_server_alpn_openssl_client_alpn_renegotiate,
- erlang_client_alpn_npn_openssl_server_alpn_npn,
- erlang_server_alpn_npn_openssl_client_alpn_npn].
-
-npn_tests() ->
- [erlang_client_openssl_server_npn,
- erlang_server_openssl_client_npn,
- erlang_server_openssl_client_npn_renegotiate,
- erlang_client_openssl_server_npn_renegotiate,
- erlang_server_openssl_client_npn_only_client,
- erlang_server_openssl_client_npn_only_server,
- erlang_client_openssl_server_npn_only_client,
- erlang_client_openssl_server_npn_only_server].
-
-sni_server_tests() ->
- [erlang_server_openssl_client_sni_match,
- erlang_server_openssl_client_sni_match_fun,
- erlang_server_openssl_client_sni_no_match,
- erlang_server_openssl_client_sni_no_match_fun,
- erlang_server_openssl_client_sni_no_header,
- erlang_server_openssl_client_sni_no_header_fun].
-
-
-init_per_suite(Config0) ->
- case os:find_executable("openssl") of
- false ->
- {skip, "Openssl not found"};
- _ ->
- ct:pal("Version: ~p", [os:cmd("openssl version")]),
- catch crypto:stop(),
- try crypto:start() of
- ok ->
- ssl_test_lib:clean_start(),
- Config =
- case ssl_test_lib:openssl_dsa_support() of
- true ->
- Config1 = ssl_test_lib:make_rsa_cert(Config0),
- ssl_test_lib:make_dsa_cert(Config1);
- false ->
- ssl_test_lib:make_rsa_cert(Config0)
- end,
- ssl_test_lib:cipher_restriction(Config)
- catch _:_ ->
- {skip, "Crypto did not start"}
- end
- end.
-
-end_per_suite(_Config) ->
- ssl:stop(),
- application:stop(crypto),
- ssl_test_lib:kill_openssl().
-
-init_per_group(GroupName, Config) ->
- case ssl_test_lib:is_tls_version(GroupName) of
- true ->
- case ssl_test_lib:supports_ssl_tls_version(GroupName) of
- true ->
- case ssl_test_lib:check_sane_openssl_version(GroupName) of
- true ->
- ssl_test_lib:init_tls_version(GroupName, Config);
- false ->
- {skip, openssl_does_not_support_version}
- end;
- false ->
- {skip, openssl_does_not_support_version}
- end;
- _ ->
- ssl:start(),
- Config
- end.
-
-end_per_group(GroupName, Config) ->
- case ssl_test_lib:is_tls_version(GroupName) of
- true ->
- ssl_test_lib:clean_tls_version(Config);
- false ->
- Config
- end.
-
-init_per_testcase(expired_session, Config) ->
- ct:timetrap(?EXPIRE * 1000 * 5),
- ssl:stop(),
- application:load(ssl),
- application:set_env(ssl, session_lifetime, ?EXPIRE),
- ssl:start(),
- Config;
-
-init_per_testcase(TestCase, Config) when
- TestCase == ciphers_dsa_signed_certs;
- TestCase == erlang_client_openssl_server_dsa_cert;
- TestCase == erlang_server_openssl_client_dsa_cert;
- TestCase == erlang_client_openssl_server_dsa_cert;
- TestCase == erlang_server_openssl_client_dsa_cert ->
- case ssl_test_lib:openssl_dsa_support() andalso ssl_test_lib:is_sane_oppenssl_client() of
- true ->
- special_init(TestCase, Config);
- false ->
- {skip, "DSA not supported by OpenSSL"}
- end;
-init_per_testcase(TestCase, Config) ->
- ct:timetrap({seconds, 35}),
- special_init(TestCase, Config).
-
-special_init(TestCase, Config) when
- TestCase == ciphers_rsa_signed_certs;
- TestCase == ciphers_dsa_signed_certs->
- ct:timetrap({seconds, 90}),
- Config;
-special_init(TestCase, Config)
- when TestCase == erlang_client_openssl_server_renegotiate;
- TestCase == erlang_client_openssl_server_nowrap_seqnum;
- TestCase == erlang_server_openssl_client_nowrap_seqnum;
- TestCase == erlang_client_openssl_server_renegotiate_after_client_data
- ->
- {ok, Version} = application:get_env(ssl, protocol_version),
- check_sane_openssl_renegotaite(Config, Version);
-
-special_init(ssl2_erlang_server_openssl_client, Config) ->
- case ssl_test_lib:supports_ssl_tls_version(sslv2) of
- true ->
- Config;
- false ->
- {skip, "sslv2 not supported by openssl"}
- end;
-
-special_init(TestCase, Config)
- when TestCase == erlang_client_alpn_openssl_server_alpn;
- TestCase == erlang_server_alpn_openssl_client_alpn;
- TestCase == erlang_client_alpn_openssl_server;
- TestCase == erlang_client_openssl_server_alpn;
- TestCase == erlang_server_alpn_openssl_client;
- TestCase == erlang_server_openssl_client_alpn ->
- check_openssl_alpn_support(Config);
-
-special_init(TestCase, Config)
- when TestCase == erlang_client_alpn_openssl_server_alpn_renegotiate;
- TestCase == erlang_server_alpn_openssl_client_alpn_renegotiate ->
- {ok, Version} = application:get_env(ssl, protocol_version),
- case check_sane_openssl_renegotaite(Config, Version) of
- {skip, _} = Skip ->
- Skip;
- _ ->
- check_openssl_alpn_support(Config)
- end;
-
-special_init(TestCase, Config)
- when TestCase == erlang_client_alpn_npn_openssl_server_alpn_npn;
- TestCase == erlang_server_alpn_npn_openssl_client_alpn_npn ->
- case check_openssl_alpn_support(Config) of
- {skip, _} = Skip ->
- Skip;
- _ ->
- check_openssl_npn_support(Config)
- end;
-
-special_init(TestCase, Config)
- when TestCase == erlang_client_openssl_server_npn;
- TestCase == erlang_server_openssl_client_npn;
- TestCase == erlang_server_openssl_client_npn_only_server;
- TestCase == erlang_server_openssl_client_npn_only_client;
- TestCase == erlang_client_openssl_server_npn_only_client;
- TestCase == erlang_client_openssl_server_npn_only_server ->
- check_openssl_npn_support(Config);
-
-special_init(TestCase, Config)
- when TestCase == erlang_server_openssl_client_npn_renegotiate;
- TestCase == erlang_client_openssl_server_npn_renegotiate ->
- {ok, Version} = application:get_env(ssl, protocol_version),
- case check_sane_openssl_renegotaite(Config, Version) of
- {skip, _} = Skip ->
- Skip;
- _ ->
- check_openssl_npn_support(Config)
- end;
-
-special_init(TestCase, Config0)
- when TestCase == erlang_server_openssl_client_sni_match;
- TestCase == erlang_server_openssl_client_sni_no_match;
- TestCase == erlang_server_openssl_client_sni_no_header;
- TestCase == erlang_server_openssl_client_sni_match_fun;
- TestCase == erlang_server_openssl_client_sni_no_match_fun;
- TestCase == erlang_server_openssl_client_sni_no_header_fun ->
- RsaOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config0),
- Config = [{sni_server_opts, [{sni_hosts,
- [{"a.server", [
- {certfile, proplists:get_value(certfile, RsaOpts)},
- {keyfile, proplists:get_value(keyfile, RsaOpts)}
- ]},
- {"b.server", [
- {certfile, proplists:get_value(certfile, RsaOpts)},
- {keyfile, proplists:get_value(keyfile, RsaOpts)}
- ]}
- ]}]} | Config0],
- check_openssl_sni_support(Config);
-special_init(TestCase, Config)
- when TestCase == erlang_server_openssl_client;
- TestCase == erlang_server_openssl_client_client_cert;
- TestCase == erlang_server_openssl_client_reuse_session ->
- case ssl_test_lib:is_sane_oppenssl_client() of
- true ->
- Config;
- false ->
- {skip, "Broken OpenSSL client"}
- end;
-special_init(_, Config) ->
- Config.
-
-end_per_testcase(reuse_session_expired, Config) ->
- application:unset_env(ssl, session_lifetime),
- Config;
-end_per_testcase(_, Config) ->
- Config.
-
-%%--------------------------------------------------------------------
-%% Test Cases --------------------------------------------------------
-%%--------------------------------------------------------------------
-
-erlang_client_openssl_server() ->
- [{doc,"Test erlang client with openssl server"}].
-erlang_client_openssl_server(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
- ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
-
- {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
-
- Data = "From openssl to erlang",
-
- Port = ssl_test_lib:inet_port(node()),
- CertFile = proplists:get_value(certfile, ServerOpts),
- KeyFile = proplists:get_value(keyfile, ServerOpts),
- Version = ssl_test_lib:protocol_version(Config),
- Exe = "openssl",
- Args = ["s_server", "-accept", integer_to_list(Port),
- ssl_test_lib:version_flag(Version),
- "-cert", CertFile, "-key", KeyFile],
-
- OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
-
- ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)),
-
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE,
- erlang_ssl_receive, [Data]}},
- {options, ClientOpts}]),
- true = port_command(OpensslPort, Data),
-
- ssl_test_lib:check_result(Client, ok),
-
- %% Clean close down! Server needs to be closed first !!
- ssl_test_lib:close_port(OpensslPort),
- ssl_test_lib:close(Client),
- process_flag(trap_exit, false).
-
-%%--------------------------------------------------------------------
-erlang_server_openssl_client() ->
- [{doc,"Test erlang server with openssl client"}].
-erlang_server_openssl_client(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
-
- {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Data = "From openssl to erlang",
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
- Version = ssl_test_lib:protocol_version(Config),
-
- Exe = "openssl",
- Args = ["s_client", "-connect", hostname_format(Hostname) ++":" ++ integer_to_list(Port),
- ssl_test_lib:version_flag(Version)],
-
- OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
-
- true = port_command(OpenSslPort, Data),
-
- ssl_test_lib:check_result(Server, ok),
-
- %% Clean close down! Server needs to be closed first !!
- ssl_test_lib:close(Server),
- ssl_test_lib:close_port(OpenSslPort),
- process_flag(trap_exit, false).
-
-erlang_client_openssl_server_dsa_cert() ->
- [{doc,"Test erlang server with openssl client"}].
-erlang_client_openssl_server_dsa_cert(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- ClientOpts = ssl_test_lib:ssl_options(client_dsa_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_dsa_verify_opts, Config),
-
- {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
-
- Data = "From openssl to erlang",
-
- Port = ssl_test_lib:inet_port(node()),
- CaCertFile = proplists:get_value(cacertfile, ServerOpts),
- CertFile = proplists:get_value(certfile, ServerOpts),
- KeyFile = proplists:get_value(keyfile, ServerOpts),
- Version = ssl_test_lib:protocol_version(Config),
- Exe = "openssl",
- Args = ["s_server", "-accept", integer_to_list(Port),
- ssl_test_lib:version_flag(Version),
- "-cert", CertFile, "-CAfile", CaCertFile,
- "-key", KeyFile, "-Verify", "2", "-msg"],
-
- OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
-
- ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)),
-
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE,
- erlang_ssl_receive, [Data]}},
- {options, ClientOpts}]),
-
- true = port_command(OpensslPort, Data),
-
- ssl_test_lib:check_result(Client, ok),
-
- %% Clean close down! Server needs to be closed first !!
- ssl_test_lib:close_port(OpensslPort),
- ssl_test_lib:close(Client),
- process_flag(trap_exit, false),
- ok.
-%%--------------------------------------------------------------------
-erlang_server_openssl_client_dsa_cert() ->
- [{doc,"Test erlang server with openssl client"}].
-erlang_server_openssl_client_dsa_cert(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- ClientOpts = ssl_test_lib:ssl_options(client_dsa_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_dsa_verify_opts, Config),
-
- {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Data = "From openssl to erlang",
- CaCertFile = proplists:get_value(cacertfile, ClientOpts),
- CertFile = proplists:get_value(certfile, ClientOpts),
- KeyFile = proplists:get_value(keyfile, ClientOpts),
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
- Version = ssl_test_lib:protocol_version(Config),
- Exe = "openssl",
- Args = ["s_client", "-connect", hostname_format(Hostname) ++ ":" ++ integer_to_list(Port),
- ssl_test_lib:version_flag(Version),
- "-cert", CertFile,
- "-CAfile", CaCertFile,
- "-key", KeyFile, "-msg"],
-
- OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
- true = port_command(OpenSslPort, Data),
-
- ssl_test_lib:check_result(Server, ok),
-
- %% Clean close down! Server needs to be closed first !!
- ssl_test_lib:close(Server),
- ssl_test_lib:close_port(OpenSslPort),
- process_flag(trap_exit, false).
-
- %%--------------------------------------------------------------------
-erlang_client_openssl_server_anon() ->
- [{doc,"Test erlang client with openssl server, anonymous"}].
-erlang_client_openssl_server_anon(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- %% OpenSSL expects a certificate and key, even if the cipher spec
- %% is restructed to aNULL, so we use 'server_rsa_opts' here
- ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
- ClientOpts = ssl_test_lib:ssl_options(client_anon_opts, Config),
- VersionTuple = ssl_test_lib:protocol_version(Config, tuple),
- Ciphers = ssl_test_lib:ecdh_dh_anonymous_suites(VersionTuple),
-
- case openssl_has_common_ciphers(Ciphers) of
- false ->
- {skip, not_supported_by_openssl};
- true ->
-
- {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
-
- Data = "From openssl to erlang",
-
- Port = ssl_test_lib:inet_port(node()),
- CertFile = proplists:get_value(certfile, ServerOpts),
- KeyFile = proplists:get_value(keyfile, ServerOpts),
- Version = ssl_test_lib:protocol_version(Config),
- Exe = "openssl",
- Args = ["s_server", "-accept", integer_to_list(Port),
- ssl_test_lib:version_flag(Version),
- "-cert", CertFile, "-key", KeyFile,
- "-cipher", "aNULL", "-msg"],
-
- OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
-
- ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)),
-
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE,
- erlang_ssl_receive, [Data]}},
- {options, [{ciphers, Ciphers} | ClientOpts]}]),
-
- true = port_command(OpensslPort, Data),
-
- ssl_test_lib:check_result(Client, ok),
-
- %% Clean close down! Server needs to be closed first !!
- ssl_test_lib:close_port(OpensslPort),
- ssl_test_lib:close(Client),
- process_flag(trap_exit, false)
- end.
-%%--------------------------------------------------------------------
-erlang_server_openssl_client_anon() ->
- [{doc,"Test erlang server with openssl client, anonymous"}].
-erlang_server_openssl_client_anon(Config) when is_list(Config) ->
-
- process_flag(trap_exit, true),
- ServerOpts = ssl_test_lib:ssl_options(server_anon_opts, Config),
- VersionTuple = ssl_test_lib:protocol_version(Config, tuple),
- Ciphers = ssl_test_lib:ecdh_dh_anonymous_suites(VersionTuple),
-
- case openssl_has_common_ciphers(Ciphers) of
- false ->
- {skip, not_supported_by_openssl};
- true ->
- {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Data = "From openssl to erlang",
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
- {options, [{ciphers, Ciphers} | ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
- Version = ssl_test_lib:protocol_version(Config),
- Exe = "openssl",
- Args = ["s_client", "-connect", hostname_format(Hostname) ++ ":" ++ integer_to_list(Port),
- ssl_test_lib:version_flag(Version),
- "-cipher", "aNULL", "-msg"],
-
- OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
- true = port_command(OpenSslPort, Data),
-
- ssl_test_lib:check_result(Server, ok),
-
- %% Clean close down! Server needs to be closed first !!
- ssl_test_lib:close(Server),
- ssl_test_lib:close_port(OpenSslPort),
- process_flag(trap_exit, false)
- end.
-
-%%--------------------------------------------------------------------
-erlang_server_openssl_client_anon_with_cert() ->
- [{doc,"Test erlang server with openssl client, anonymous (with cert)"}].
-erlang_server_openssl_client_anon_with_cert(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
- VersionTuple = ssl_test_lib:protocol_version(Config, tuple),
- Ciphers = ssl_test_lib:ecdh_dh_anonymous_suites(VersionTuple),
-
- case openssl_has_common_ciphers(Ciphers) of
- false ->
- {skip, not_supported_by_openssl};
- true ->
- {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Data = "From openssl to erlang",
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
- {options, [{ciphers, Ciphers} | ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
- Version = ssl_test_lib:protocol_version(Config),
- Exe = "openssl",
- Args = ["s_client", "-connect", hostname_format(Hostname) ++ ":" ++ integer_to_list(Port),
- ssl_test_lib:version_flag(Version),
- "-cipher", "aNULL", "-msg"],
-
- OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
- true = port_command(OpenSslPort, Data),
-
- ssl_test_lib:check_result(Server, ok),
-
- %% Clean close down! Server needs to be closed first !!
- ssl_test_lib:close(Server),
- ssl_test_lib:close_port(OpenSslPort),
- process_flag(trap_exit, false)
- end.
-
- %%--------------------------------------------------------------------
-erlang_server_openssl_client_reuse_session() ->
- [{doc, "Test erlang server with openssl client that reconnects with the"
- "same session id, to test reusing of sessions."}].
-erlang_server_openssl_client_reuse_session(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
-
- {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Data = "From openssl to erlang",
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
- {reconnect_times, 5},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
- Version = ssl_test_lib:protocol_version(Config),
-
- Exe = "openssl",
- Args = ["s_client", "-connect", hostname_format(Hostname)
- ++ ":" ++ integer_to_list(Port),
- ssl_test_lib:version_flag(Version),
- "-reconnect"],
-
- OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
-
- true = port_command(OpenSslPort, Data),
-
- ssl_test_lib:check_result(Server, ok),
-
- %% Clean close down! Server needs to be closed first !!
- ssl_test_lib:close(Server),
- ssl_test_lib:close_port(OpenSslPort),
- process_flag(trap_exit, false),
- ok.
-
-%%--------------------------------------------------------------------
-
-erlang_client_openssl_server_renegotiate() ->
- [{doc,"Test erlang client when openssl server issuses a renegotiate"}].
-erlang_client_openssl_server_renegotiate(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
- ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
-
- {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
-
- ErlData = "From erlang to openssl",
- OpenSslData = "From openssl to erlang",
-
- Port = ssl_test_lib:inet_port(node()),
- CertFile = proplists:get_value(certfile, ServerOpts),
- CaCertFile = proplists:get_value(cacertfile, ServerOpts),
- KeyFile = proplists:get_value(keyfile, ServerOpts),
- Version = ssl_test_lib:protocol_version(Config),
-
- Exe = "openssl",
- Args = ["s_server", "-accept", integer_to_list(Port),
- ssl_test_lib:version_flag(Version),
- "-CAfile", CaCertFile,
- "-cert", CertFile, "-key", KeyFile, "-msg"],
-
- OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
-
- ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)),
-
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE,
- delayed_send, [[ErlData, OpenSslData]]}},
- {options, ClientOpts}]),
-
- true = port_command(OpensslPort, ?OPENSSL_RENEGOTIATE),
- ct:sleep(?SLEEP),
- true = port_command(OpensslPort, OpenSslData),
-
- ssl_test_lib:check_result(Client, ok),
-
- %% Clean close down! Server needs to be closed first !!
- ssl_test_lib:close_port(OpensslPort),
- ssl_test_lib:close(Client),
- process_flag(trap_exit, false),
- ok.
-%%--------------------------------------------------------------------
-erlang_client_openssl_server_renegotiate_after_client_data() ->
- [{doc,"Test erlang client when openssl server issuses a renegotiate after reading client data"}].
-erlang_client_openssl_server_renegotiate_after_client_data(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
- ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
-
- {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
-
- ErlData = "From erlang to openssl",
- OpenSslData = "From openssl to erlang",
-
- Port = ssl_test_lib:inet_port(node()),
- CaCertFile = proplists:get_value(cacertfile, ServerOpts),
- CertFile = proplists:get_value(certfile, ServerOpts),
- KeyFile = proplists:get_value(keyfile, ServerOpts),
- Version = ssl_test_lib:protocol_version(Config),
-
- Exe = "openssl",
- Args = ["s_server", "-accept", integer_to_list(Port),
- ssl_test_lib:version_flag(Version),
- "-CAfile", CaCertFile,
- "-cert", CertFile, "-key", KeyFile, "-msg"],
-
- OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
-
- ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)),
-
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE,
- send_wait_send, [[ErlData, OpenSslData]]}},
- {options, ClientOpts}]),
-
- true = port_command(OpensslPort, ?OPENSSL_RENEGOTIATE),
- ct:sleep(?SLEEP),
- true = port_command(OpensslPort, OpenSslData),
-
- ssl_test_lib:check_result(Client, ok),
-
- %% Clean close down! Server needs to be closed first !!
- ssl_test_lib:close_port(OpensslPort),
- ssl_test_lib:close(Client),
- process_flag(trap_exit, false),
- ok.
-
-%%--------------------------------------------------------------------
-
-erlang_client_openssl_server_nowrap_seqnum() ->
- [{doc, "Test that erlang client will renegotiate session when",
- "max sequence number celing is about to be reached. Although"
- "in the testcase we use the test option renegotiate_at"
- " to lower treashold substantially."}].
-erlang_client_openssl_server_nowrap_seqnum(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
- ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
-
- {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
-
- ErlData = "From erlang to openssl\n",
- N = 10,
-
- Port = ssl_test_lib:inet_port(node()),
- CaCertFile = proplists:get_value(cacertfile, ServerOpts),
- CertFile = proplists:get_value(certfile, ServerOpts),
- KeyFile = proplists:get_value(keyfile, ServerOpts),
- Version = ssl_test_lib:protocol_version(Config),
- Exe = "openssl",
- Args = ["s_server", "-accept", integer_to_list(Port),
- ssl_test_lib:version_flag(Version),
- "-CAfile", CaCertFile,
- "-cert", CertFile, "-key", KeyFile, "-msg"],
-
- OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
-
- ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)),
-
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {ssl_test_lib,
- trigger_renegotiate, [[ErlData, N+2]]}},
- {options, [{reuse_sessions, false},
- {renegotiate_at, N} | ClientOpts]}]),
-
- ssl_test_lib:check_result(Client, ok),
-
- %% Clean close down! Server needs to be closed first !!
- ssl_test_lib:close_port(OpensslPort),
- ssl_test_lib:close(Client),
- process_flag(trap_exit, false).
-%%--------------------------------------------------------------------
-erlang_server_openssl_client_nowrap_seqnum() ->
- [{doc, "Test that erlang client will renegotiate session when",
- "max sequence number celing is about to be reached. Although"
- "in the testcase we use the test option renegotiate_at"
- " to lower treashold substantially."}].
-erlang_server_openssl_client_nowrap_seqnum(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
-
- {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Data = "From openssl to erlang",
-
- N = 10,
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {ssl_test_lib,
- trigger_renegotiate, [[Data, N+2]]}},
- {options, [{renegotiate_at, N}, {reuse_sessions, false} | ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
- Version = ssl_test_lib:protocol_version(Config),
- Exe = "openssl",
- Args = ["s_client","-connect", hostname_format(Hostname) ++ ":" ++ integer_to_list(Port),
- ssl_test_lib:version_flag(Version),
- "-msg"],
-
- OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
-
- true = port_command(OpenSslPort, Data),
-
- ssl_test_lib:check_result(Server, ok),
-
- %% Clean close down! Server needs to be closed first !!
- ssl_test_lib:close(Server),
- ssl_test_lib:close_port(OpenSslPort),
- process_flag(trap_exit, false).
-
-%%--------------------------------------------------------------------
-
-erlang_client_openssl_server_no_server_ca_cert() ->
- [{doc, "Test erlang client when openssl server sends a cert chain not"
- "including the ca cert. Explicitly test this even if it is"
- "implicitly tested eleswhere."}].
-erlang_client_openssl_server_no_server_ca_cert(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
- ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
-
- {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
-
- Data = "From openssl to erlang",
-
- Port = ssl_test_lib:inet_port(node()),
- CertFile = proplists:get_value(certfile, ServerOpts),
- KeyFile = proplists:get_value(keyfile, ServerOpts),
- Version = ssl_test_lib:protocol_version(Config),
- Exe = "openssl",
- Args = ["s_server", "-accept", integer_to_list(Port),
- ssl_test_lib:version_flag(Version),
- "-cert", CertFile, "-key", KeyFile, "-msg"],
-
- OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
-
- ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)),
-
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE,
- erlang_ssl_receive, [Data]}},
- {options, ClientOpts}]),
-
- true = port_command(OpensslPort, Data),
-
- ssl_test_lib:check_result(Client, ok),
-
- %% Clean close down! Server needs to be closed first !!
- ssl_test_lib:close_port(OpensslPort),
- ssl_test_lib:close(Client),
- process_flag(trap_exit, false).
-
-%%--------------------------------------------------------------------
-erlang_client_openssl_server_client_cert() ->
- [{doc,"Test erlang client with openssl server when client sends cert"}].
-erlang_client_openssl_server_client_cert(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
- ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
-
- {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
-
- Data = "From openssl to erlang",
-
- Port = ssl_test_lib:inet_port(node()),
- CertFile = proplists:get_value(certfile, ServerOpts),
- CaCertFile = proplists:get_value(cacertfile, ServerOpts),
- KeyFile = proplists:get_value(keyfile, ServerOpts),
- Version = ssl_test_lib:protocol_version(Config),
- Exe = "openssl",
- Args = ["s_server", "-accept", integer_to_list(Port),
- ssl_test_lib:version_flag(Version),
- "-cert", CertFile, "-CAfile", CaCertFile,
- "-key", KeyFile, "-Verify", "2"],
-
- OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
-
- ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)),
-
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE,
- erlang_ssl_receive, [Data]}},
- {options, ClientOpts}]),
- true = port_command(OpensslPort, Data),
-
- ssl_test_lib:check_result(Client, ok),
-
- %% Clean close down! Server needs to be closed first !!
- ssl_test_lib:close_port(OpensslPort),
- ssl_test_lib:close(Client),
- process_flag(trap_exit, false).
-
-%%--------------------------------------------------------------------
-erlang_server_openssl_client_client_cert() ->
- [{doc,"Test erlang server with openssl client when client sends cert"}].
-erlang_server_openssl_client_client_cert(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
- ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
-
- {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Data = "From openssl to erlang",
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE,
- erlang_ssl_receive, [Data]}},
- {options,
- [{verify , verify_peer}
- | ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
-
- CaCertFile = proplists:get_value(cacertfile, ClientOpts),
- CertFile = proplists:get_value(certfile, ClientOpts),
- KeyFile = proplists:get_value(keyfile, ClientOpts),
- Version = ssl_test_lib:protocol_version(Config),
- Exe = "openssl",
- Args = ["s_client", "-cert", CertFile,
- "-CAfile", CaCertFile,
- "-key", KeyFile,"-connect", hostname_format(Hostname) ++ ":" ++ integer_to_list(Port),
- ssl_test_lib:version_flag(Version)],
- OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
-
- true = port_command(OpenSslPort, Data),
- ssl_test_lib:check_result(Server, ok),
-
- %% Clean close down! Server needs to be closed first !!
- ssl_test_lib:close_port(OpenSslPort),
- ssl_test_lib:close(Server),
- process_flag(trap_exit, false).
-
-%%--------------------------------------------------------------------
-erlang_server_erlang_client_client_cert() ->
- [{doc,"Test erlang server with erlang client when client sends cert"}].
-erlang_server_erlang_client_client_cert(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- ServerOpts = proplists:get_value(server_rsa_verify_opts, Config),
- ClientOpts = proplists:get_value(client_rsa_verify_opts, Config),
- Version = ssl_test_lib:protocol_version(Config),
- {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Data = "From erlang to erlang",
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE,
- erlang_ssl_receive,
- %% Due to 1/n-1 splitting countermeasure Rizzo/Duong-Beast
- [Data]}},
- {options,
- [{verify , verify_peer}
- | ServerOpts]}]),
- Port = ssl_test_lib:inet_port(Server),
-
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- %% Due to 1/n-1 splitting countermeasure Rizzo/Duong-Beast
- {mfa, {ssl, send, [Data]}},
- {options,
- [{versions, [Version]} | ClientOpts]}]),
-
- ssl_test_lib:check_result(Server, ok, Client, ok),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close(Client),
- process_flag(trap_exit, false).
-
-%%--------------------------------------------------------------------
-
-ciphers_rsa_signed_certs() ->
- [{doc,"Test cipher suites that uses rsa certs"}].
-ciphers_rsa_signed_certs(Config) when is_list(Config) ->
- Version = ssl_test_lib:protocol_version(Config),
- Ciphers = ssl_test_lib:rsa_suites(openssl),
- run_suites(Ciphers, Version, Config, rsa).
-%%--------------------------------------------------------------------
-
-ciphers_dsa_signed_certs() ->
- [{doc,"Test cipher suites that uses dsa certs"}].
-ciphers_dsa_signed_certs(Config) when is_list(Config) ->
- Version = ssl_test_lib:protocol_version(Config),
- NVersion = ssl_test_lib:protocol_version(Config, tuple),
- Ciphers = ssl_test_lib:dsa_suites(NVersion),
- run_suites(Ciphers, Version, Config, dsa).
-
-%%--------------------------------------------------------------------
-erlang_client_bad_openssl_server() ->
- [{doc,"Test what happens if openssl server sends garbage to erlang ssl client"}].
-erlang_client_bad_openssl_server(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
- ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
-
- {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
-
- Port = ssl_test_lib:inet_port(node()),
- CertFile = proplists:get_value(certfile, ServerOpts),
- KeyFile = proplists:get_value(keyfile, ServerOpts),
- Version = ssl_test_lib:protocol_version(Config),
- Exe = "openssl",
- Args = ["s_server", "-accept", integer_to_list(Port), ssl_test_lib:version_flag(Version),
- "-cert", CertFile, "-key", KeyFile],
- OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
-
- ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)),
-
- Client0 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE, server_sent_garbage, []}},
- {options,
- [{versions, [Version]} | ClientOpts]}]),
-
- %% Send garbage
- true = port_command(OpensslPort, ?OPENSSL_GARBAGE),
-
- ct:sleep(?SLEEP),
-
- Client0 ! server_sent_garbage,
-
- ssl_test_lib:check_result(Client0, true),
-
- ssl_test_lib:close(Client0),
-
- %% Make sure openssl does not hang and leave zombie process
- Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {ssl_test_lib, no_result, []}},
- {options,
- [{versions, [Version]} | ClientOpts]}]),
-
- %% Clean close down! Server needs to be closed first !!
- ssl_test_lib:close_port(OpensslPort),
- ssl_test_lib:close(Client1),
- process_flag(trap_exit, false),
- ok.
-
-%%--------------------------------------------------------------------
-
-expired_session() ->
- [{doc, "Test our ssl client handling of expired sessions. Will make"
- "better code coverage of the ssl_manager module"}].
-expired_session(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
- ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
- {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
-
- Port = ssl_test_lib:inet_port(node()),
- CertFile = proplists:get_value(certfile, ServerOpts),
- KeyFile = proplists:get_value(keyfile, ServerOpts),
-
- Exe = "openssl",
- Args = ["s_server", "-accept", integer_to_list(Port),
- "-cert", CertFile,"-key", KeyFile],
-
- OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
-
- ssl_test_lib:wait_for_openssl_server(Port, tls),
-
- Client0 =
- ssl_test_lib:start_client([{node, ClientNode},
- {port, Port}, {host, Hostname},
- {mfa, {ssl_test_lib, no_result, []}},
- {from, self()}, {options, ClientOpts}]),
-
- ssl_test_lib:close(Client0),
-
- %% Make sure session is registered
- ct:sleep(?SLEEP),
-
- Client1 =
- ssl_test_lib:start_client([{node, ClientNode},
- {port, Port}, {host, Hostname},
- {mfa, {ssl_test_lib, no_result, []}},
- {from, self()}, {options, ClientOpts}]),
-
- ssl_test_lib:close(Client1),
- %% Make sure session is unregistered due to expiration
- ct:sleep((?EXPIRE+1) * 1000),
-
- Client2 =
- ssl_test_lib:start_client([{node, ClientNode},
- {port, Port}, {host, Hostname},
- {mfa, {ssl_test_lib, no_result, []}},
- {from, self()}, {options, ClientOpts}]),
-
- %% Clean close down! Server needs to be closed first !!
- ssl_test_lib:close_port(OpensslPort),
- ssl_test_lib:close(Client2),
- process_flag(trap_exit, false).
-
-%%--------------------------------------------------------------------
-ssl2_erlang_server_openssl_client() ->
- [{doc,"Test that ssl v2 clients are rejected"}].
-
-ssl2_erlang_server_openssl_client(Config) when is_list(Config) ->
- process_flag(trap_exit, true),
- ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
-
- {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
- {from, self()},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
-
- Exe = "openssl",
- Args = ["s_client", "-connect", hostname_format(Hostname) ++ ":" ++ integer_to_list(Port),
- "-ssl2", "-msg"],
-
- OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
-
- ct:log("Ports ~p~n", [[erlang:port_info(P) || P <- erlang:ports()]]),
- ssl_test_lib:consume_port_exit(OpenSslPort),
- ssl_test_lib:check_server_alert(Server, unexpected_message),
- process_flag(trap_exit, false).
-
-%%--------------------------------------------------------------------
-
-erlang_client_alpn_openssl_server_alpn(Config) when is_list(Config) ->
- Data = "From openssl to erlang",
- start_erlang_client_and_openssl_server_for_alpn_negotiation(Config, Data, fun(Client, OpensslPort) ->
- true = port_command(OpensslPort, Data),
-
- ssl_test_lib:check_result(Client, ok)
- end),
- ok.
-
-%%--------------------------------------------------------------------
-
-erlang_server_alpn_openssl_client_alpn(Config) when is_list(Config) ->
- Data = "From openssl to erlang",
- start_erlang_server_and_openssl_client_for_alpn_negotiation(Config, Data, fun(Client, OpensslPort) ->
- true = port_command(OpensslPort, Data),
-
- ssl_test_lib:check_result(Client, ok)
- end),
- ok.
-
-%%--------------------------------------------------------------------------
-
-erlang_client_alpn_openssl_server(Config) when is_list(Config) ->
- Data = "From openssl to erlang",
- start_erlang_client_and_openssl_server_with_opts(Config,
- [{alpn_advertised_protocols, [<<"spdy/2">>]}],
- [],
- Data, fun(Server, OpensslPort) ->
- true = port_command(OpensslPort, Data),
- ssl_test_lib:check_result(Server, ok)
- end),
- ok.
-
-%%--------------------------------------------------------------------------
-
-erlang_client_openssl_server_alpn(Config) when is_list(Config) ->
- Data = "From openssl to erlang",
- start_erlang_client_and_openssl_server_with_opts(Config,
- [],
- ["-alpn", "spdy/2"],
- Data, fun(Server, OpensslPort) ->
- true = port_command(OpensslPort, Data),
- ssl_test_lib:check_result(Server, ok)
- end),
- ok.
-
-%%--------------------------------------------------------------------------
-
-erlang_server_alpn_openssl_client(Config) when is_list(Config) ->
- Data = "From openssl to erlang",
- start_erlang_server_and_openssl_client_with_opts(Config,
- [{alpn_preferred_protocols, [<<"spdy/2">>]}],
- [],
- Data, fun(Server, OpensslPort) ->
- true = port_command(OpensslPort, Data),
- ssl_test_lib:check_result(Server, ok)
- end),
- ok.
-
-%%--------------------------------------------------------------------------
-
-erlang_server_openssl_client_alpn(Config) when is_list(Config) ->
- Data = "From openssl to erlang",
- start_erlang_server_and_openssl_client_with_opts(Config,
- [],
- ["-alpn", "spdy/2"],
- Data, fun(Server, OpensslPort) ->
- true = port_command(OpensslPort, Data),
- ssl_test_lib:check_result(Server, ok)
- end),
- ok.
-
-%%--------------------------------------------------------------------
-
-erlang_client_alpn_openssl_server_alpn_renegotiate(Config) when is_list(Config) ->
- Data = "From openssl to erlang",
- start_erlang_client_and_openssl_server_for_alpn_negotiation(Config, Data, fun(Client, OpensslPort) ->
- true = port_command(OpensslPort, ?OPENSSL_RENEGOTIATE),
- ct:sleep(?SLEEP),
- true = port_command(OpensslPort, Data),
-
- ssl_test_lib:check_result(Client, ok)
- end),
- ok.
-
-%%--------------------------------------------------------------------
-
-erlang_server_alpn_openssl_client_alpn_renegotiate(Config) when is_list(Config) ->
- Data = "From openssl to erlang",
- start_erlang_server_and_openssl_client_for_alpn_negotiation(Config, Data, fun(Client, OpensslPort) ->
- true = port_command(OpensslPort, ?OPENSSL_RENEGOTIATE),
- ct:sleep(?SLEEP),
- true = port_command(OpensslPort, Data),
-
- ssl_test_lib:check_result(Client, ok)
- end),
- ok.
-
-%%--------------------------------------------------------------------
-
-erlang_client_alpn_npn_openssl_server_alpn_npn(Config) when is_list(Config) ->
- Data = "From openssl to erlang",
- start_erlang_client_and_openssl_server_for_alpn_npn_negotiation(Config, Data, fun(Client, OpensslPort) ->
- true = port_command(OpensslPort, Data),
-
- ssl_test_lib:check_result(Client, ok)
- end),
- ok.
-
-%%--------------------------------------------------------------------
-
-erlang_server_alpn_npn_openssl_client_alpn_npn(Config) when is_list(Config) ->
- Data = "From openssl to erlang",
- start_erlang_server_and_openssl_client_for_alpn_npn_negotiation(Config, Data, fun(Client, OpensslPort) ->
- true = port_command(OpensslPort, Data),
-
- ssl_test_lib:check_result(Client, ok)
- end),
- ok.
-
-%%--------------------------------------------------------------------
-erlang_client_openssl_server_npn() ->
- [{doc,"Test erlang client with openssl server doing npn negotiation"}].
-
-erlang_client_openssl_server_npn(Config) when is_list(Config) ->
- Data = "From openssl to erlang",
- start_erlang_client_and_openssl_server_for_npn_negotiation(Config, Data, fun(Client, OpensslPort) ->
- true = port_command(OpensslPort, Data),
-
- ssl_test_lib:check_result(Client, ok)
- end),
- ok.
-
-%%--------------------------------------------------------------------
-erlang_client_openssl_server_npn_renegotiate() ->
- [{doc,"Test erlang client with openssl server doing npn negotiation and renegotiate"}].
-
-erlang_client_openssl_server_npn_renegotiate(Config) when is_list(Config) ->
- Data = "From openssl to erlang",
- start_erlang_client_and_openssl_server_for_npn_negotiation(Config, Data, fun(Client, OpensslPort) ->
- true = port_command(OpensslPort, ?OPENSSL_RENEGOTIATE),
- ct:sleep(?SLEEP),
- true = port_command(OpensslPort, Data),
- ssl_test_lib:check_result(Client, ok)
- end),
- ok.
-%%--------------------------------------------------------------------------
-erlang_server_openssl_client_npn() ->
- [{doc,"Test erlang server with openssl client and npn negotiation"}].
-
-erlang_server_openssl_client_npn(Config) when is_list(Config) ->
-
- Data = "From openssl to erlang",
- start_erlang_server_and_openssl_client_for_npn_negotiation(Config, Data, fun(Server, OpensslPort) ->
- true = port_command(OpensslPort, Data),
- ssl_test_lib:check_result(Server, ok)
- end),
- ok.
-
-%%--------------------------------------------------------------------------
-erlang_server_openssl_client_npn_renegotiate() ->
- [{doc,"Test erlang server with openssl client and npn negotiation with renegotiation"}].
-
-erlang_server_openssl_client_npn_renegotiate(Config) when is_list(Config) ->
- Data = "From openssl to erlang",
- start_erlang_server_and_openssl_client_for_npn_negotiation(Config, Data, fun(Server, OpensslPort) ->
- true = port_command(OpensslPort, ?OPENSSL_RENEGOTIATE),
- ct:sleep(?SLEEP),
- true = port_command(OpensslPort, Data),
- ssl_test_lib:check_result(Server, ok)
- end),
- ok.
-%%--------------------------------------------------------------------------
-erlang_client_openssl_server_npn_only_server(Config) when is_list(Config) ->
- Data = "From openssl to erlang",
- start_erlang_client_and_openssl_server_with_opts(Config, [],
- ["-nextprotoneg", "spdy/2"], Data, fun(Server, OpensslPort) ->
- true = port_command(OpensslPort, Data),
- ssl_test_lib:check_result(Server, ok)
- end),
- ok.
-
-%%--------------------------------------------------------------------------
-
-erlang_client_openssl_server_npn_only_client(Config) when is_list(Config) ->
- Data = "From openssl to erlang",
- start_erlang_client_and_openssl_server_with_opts(Config,
- [{client_preferred_next_protocols,
- {client, [<<"spdy/2">>], <<"http/1.1">>}}], [],
- Data, fun(Server, OpensslPort) ->
- true = port_command(OpensslPort, Data),
- ssl_test_lib:check_result(Server, ok)
- end),
- ok.
-
-%%--------------------------------------------------------------------------
-erlang_server_openssl_client_npn_only_server(Config) when is_list(Config) ->
- Data = "From openssl to erlang",
- start_erlang_server_and_openssl_client_with_opts(Config, [{next_protocols_advertised, [<<"spdy/2">>]}], [],
- Data, fun(Server, OpensslPort) ->
- true = port_command(OpensslPort, Data),
- ssl_test_lib:check_result(Server, ok)
- end),
- ok.
-
-erlang_server_openssl_client_npn_only_client(Config) when is_list(Config) ->
- Data = "From openssl to erlang",
- start_erlang_server_and_openssl_client_with_opts(Config, [], ["-nextprotoneg", "spdy/2"],
- Data, fun(Server, OpensslPort) ->
- true = port_command(OpensslPort, Data),
- ssl_test_lib:check_result(Server, ok)
- end),
- ok.
-%--------------------------------------------------------------------------
-erlang_server_openssl_client_sni_no_header(Config) when is_list(Config) ->
- erlang_server_openssl_client_sni_test(Config, undefined, undefined, "server Peer cert").
-
-erlang_server_openssl_client_sni_no_header_fun(Config) when is_list(Config) ->
- erlang_server_openssl_client_sni_test_sni_fun(Config, undefined, undefined, "server Peer cert").
-
-erlang_server_openssl_client_sni_match(Config) when is_list(Config) ->
- erlang_server_openssl_client_sni_test(Config, "a.server", "a.server", "server Peer cert").
-
-erlang_server_openssl_client_sni_match_fun(Config) when is_list(Config) ->
- erlang_server_openssl_client_sni_test_sni_fun(Config, "a.server", "a.server", "server Peer cert").
-
-erlang_server_openssl_client_sni_no_match(Config) when is_list(Config) ->
- erlang_server_openssl_client_sni_test(Config, "c.server", undefined, "server Peer cert").
-
-erlang_server_openssl_client_sni_no_match_fun(Config) when is_list(Config) ->
- erlang_server_openssl_client_sni_test_sni_fun(Config, "c.server", undefined, "server Peer cert").
-
-
-%%--------------------------------------------------------------------
-%% Internal functions ------------------------------------------------
-%%--------------------------------------------------------------------
-run_suites(Ciphers, Version, Config, Type) ->
- {ClientOpts, ServerOpts} =
- case Type of
- rsa ->
- {ssl_test_lib:ssl_options(client_rsa_opts, Config),
- ssl_test_lib:ssl_options(server_rsa_opts, Config)};
- dsa ->
- {ssl_test_lib:ssl_options(client_dsa_opts, Config),
- ssl_test_lib:ssl_options(server_dsa_verify_opts, Config)}
- end,
-
- Result = lists:map(fun(Cipher) ->
- cipher(Cipher, Version, Config, ClientOpts, ServerOpts) end,
- Ciphers),
- case lists:flatten(Result) of
- [] ->
- ok;
- Error ->
- ct:log("Cipher suite errors: ~p~n", [Error]),
- ct:fail(cipher_suite_failed_see_test_case_log)
- end.
-
-client_read_check([], _Data) ->
- ok;
-client_read_check([Hd | T], Data) ->
- case binary:match(Data, list_to_binary(Hd)) of
- nomatch ->
- nomatch;
- _ ->
- client_read_check(T, Data)
- end.
-client_check_result(Port, DataExpected, DataReceived) ->
- receive
- {Port, {data, TheData}} ->
- Data = list_to_binary(TheData),
- NewData = <<DataReceived/binary, Data/binary>>,
- ct:log("New Data: ~p", [NewData]),
- case client_read_check(DataExpected, NewData) of
- ok ->
- ok;
- _ ->
- client_check_result(Port, DataExpected, NewData)
- end
- after 20000 ->
- ct:fail({"Time out on openSSL Client", {expected, DataExpected},
- {got, DataReceived}})
- end.
-client_check_result(Port, DataExpected) ->
- client_check_result(Port, DataExpected, <<"">>).
-
-send_and_hostname(SSLSocket) ->
- ssl:send(SSLSocket, "OK"),
- case ssl:connection_information(SSLSocket, [sni_hostname]) of
- {ok, []} ->
- undefined;
- {ok, [{sni_hostname, Hostname}]} ->
- Hostname
- end.
-
-erlang_server_openssl_client_sni_test(Config, SNIHostname, ExpectedSNIHostname, ExpectedCN) ->
- Version = ssl_test_lib:protocol_version(Config),
- ct:log("Start running handshake, Config: ~p, SNIHostname: ~p, ExpectedSNIHostname: ~p, ExpectedCN: ~p", [Config, SNIHostname, ExpectedSNIHostname, ExpectedCN]),
- ServerOptions = proplists:get_value(sni_server_opts, Config) ++ proplists:get_value(server_rsa_opts, Config),
- {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()}, {mfa, {?MODULE, send_and_hostname, []}},
- {options, ServerOptions}]),
- Port = ssl_test_lib:inet_port(Server),
- Exe = "openssl",
- ClientArgs = case SNIHostname of
- undefined ->
- openssl_client_args(Version, Hostname,Port);
- _ ->
- openssl_client_args(Version, Hostname, Port, SNIHostname)
- end,
- ClientPort = ssl_test_lib:portable_open_port(Exe, ClientArgs),
-
- ssl_test_lib:check_result(Server, ExpectedSNIHostname),
- ssl_test_lib:close_port(ClientPort),
- ssl_test_lib:close(Server),
- ok.
-
-
-erlang_server_openssl_client_sni_test_sni_fun(Config, SNIHostname, ExpectedSNIHostname, ExpectedCN) ->
- Version = ssl_test_lib:protocol_version(Config),
- ct:log("Start running handshake for sni_fun, Config: ~p, SNIHostname: ~p, ExpectedSNIHostname: ~p, ExpectedCN: ~p", [Config, SNIHostname, ExpectedSNIHostname, ExpectedCN]),
- [{sni_hosts, ServerSNIConf}] = proplists:get_value(sni_server_opts, Config),
- SNIFun = fun(Domain) -> proplists:get_value(Domain, ServerSNIConf, undefined) end,
- ServerOptions = proplists:get_value(server_rsa_opts, Config) ++ [{sni_fun, SNIFun}],
- {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()}, {mfa, {?MODULE, send_and_hostname, []}},
- {options, ServerOptions}]),
- Port = ssl_test_lib:inet_port(Server),
- Exe = "openssl",
- ClientArgs = case SNIHostname of
- undefined ->
- openssl_client_args(Version, Hostname,Port);
- _ ->
- openssl_client_args(Version, Hostname, Port, SNIHostname)
- end,
-
- ClientPort = ssl_test_lib:portable_open_port(Exe, ClientArgs),
-
- ssl_test_lib:check_result(Server, ExpectedSNIHostname),
- ssl_test_lib:close_port(ClientPort),
- ssl_test_lib:close(Server).
-
-
-cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) ->
- process_flag(trap_exit, true),
- ct:log("Testing CipherSuite ~p~n", [CipherSuite]),
- {ClientNode, _ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
- Port = ssl_test_lib:inet_port(node()),
- CertFile = proplists:get_value(certfile, ServerOpts),
- KeyFile = proplists:get_value(keyfile, ServerOpts),
-
- Exe = "openssl",
- Args = ["s_server", "-accept", integer_to_list(Port), ssl_test_lib:version_flag(Version),
- "-cert", CertFile, "-key", KeyFile],
-
- OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
-
- ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)),
-
- ConnectionInfo = {ok, {Version, CipherSuite}},
-
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {ssl_test_lib, cipher_result, [ConnectionInfo]}},
- {options,
- [{ciphers,[CipherSuite]} |
- ClientOpts]}]),
-
- true = port_command(OpenSslPort, "Hello\n"),
-
- receive
- {Port, {data, _}} when is_port(Port) ->
- ok
- after 500 ->
- ct:log("Time out on openssl port, check that"
- " the messages Hello and world are received"
- " during close of port" , []),
- ok
- end,
-
- true = port_command(OpenSslPort, " world\n"),
-
- Result = ssl_test_lib:wait_for_result(Client, ok),
-
- %% Clean close down! Server needs to be closed first !!
- ssl_test_lib:close_port(OpenSslPort),
- ssl_test_lib:close(Client),
-
- Return = case Result of
- ok ->
- [];
- Error ->
- [{CipherSuite, Error}]
- end,
- process_flag(trap_exit, false),
- Return.
-
-start_erlang_client_and_openssl_server_with_opts(Config, ErlangClientOpts, OpensslServerOpts, Data, Callback) ->
- process_flag(trap_exit, true),
- ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
- ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
- ClientOpts = ErlangClientOpts ++ ClientOpts0,
-
- {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
-
- Data = "From openssl to erlang",
-
- Port = ssl_test_lib:inet_port(node()),
- CaCertFile = proplists:get_value(cacertfile, ServerOpts),
- CertFile = proplists:get_value(certfile, ServerOpts),
- KeyFile = proplists:get_value(keyfile, ServerOpts),
- Version = ssl_test_lib:protocol_version(Config),
-
- Exe = "openssl",
- Args = case OpensslServerOpts of
- [] ->
- ["s_server", "-accept",
- integer_to_list(Port), ssl_test_lib:version_flag(Version),
- "-CAfile", CaCertFile,
- "-cert", CertFile,"-key", KeyFile];
- [Opt, Value] ->
- ["s_server", Opt, Value, "-accept",
- integer_to_list(Port), ssl_test_lib:version_flag(Version),
- "-CAfile", CaCertFile,
- "-cert", CertFile,"-key", KeyFile]
- end,
-
- OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
-
- ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)),
-
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE,
- erlang_ssl_receive, [Data]}},
- {options, ClientOpts}]),
-
- Callback(Client, OpensslPort),
-
- %% Clean close down! Server needs to be closed first !!
- ssl_test_lib:close_port(OpensslPort),
-
- ssl_test_lib:close(Client),
- process_flag(trap_exit, false).
-
-start_erlang_client_and_openssl_server_for_alpn_negotiation(Config, Data, Callback) ->
- process_flag(trap_exit, true),
- ServerOpts = proplists:get_value(server_rsa_verify_opts, Config),
- ClientOpts0 = proplists:get_value(client_rsa_verify_opts, Config),
- ClientOpts = [{alpn_advertised_protocols, [<<"spdy/2">>]} | ClientOpts0],
-
- {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
-
- Data = "From openssl to erlang",
-
- Port = ssl_test_lib:inet_port(node()),
- CaCertFile = proplists:get_value(cacertfile, ServerOpts),
- CertFile = proplists:get_value(certfile, ServerOpts),
- KeyFile = proplists:get_value(keyfile, ServerOpts),
- Version = ssl_test_lib:protocol_version(Config),
-
- Exe = "openssl",
- Args = ["s_server", "-msg", "-alpn", "http/1.1,spdy/2", "-accept", integer_to_list(Port), ssl_test_lib:version_flag(Version),
- "-CAfile", CaCertFile,
- "-cert", CertFile, "-key", KeyFile],
- OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
- ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)),
-
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE,
- erlang_ssl_receive_and_assert_negotiated_protocol, [<<"spdy/2">>, Data]}},
- {options, ClientOpts}]),
-
- Callback(Client, OpensslPort),
-
- %% Clean close down! Server needs to be closed first !!
- ssl_test_lib:close_port(OpensslPort),
-
- ssl_test_lib:close(Client),
- process_flag(trap_exit, false).
-
-start_erlang_server_and_openssl_client_for_alpn_negotiation(Config, Data, Callback) ->
- process_flag(trap_exit, true),
- ServerOpts0 = proplists:get_value(server_rsa_opts, Config),
- ServerOpts = [{alpn_preferred_protocols, [<<"spdy/2">>]} | ServerOpts0],
-
- {_, ServerNode, _} = ssl_test_lib:run_where(Config),
-
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, erlang_ssl_receive_and_assert_negotiated_protocol, [<<"spdy/2">>, Data]}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
- Version = ssl_test_lib:protocol_version(Config),
-
- Exe = "openssl",
- Args = ["s_client", "-alpn", "http/1.0,spdy/2", "-msg", "-port",
- integer_to_list(Port), ssl_test_lib:version_flag(Version),
- "-host", "localhost"],
-
- OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
-
- Callback(Server, OpenSslPort),
-
- ssl_test_lib:close(Server),
-
- ssl_test_lib:close_port(OpenSslPort),
- process_flag(trap_exit, false).
-
-start_erlang_client_and_openssl_server_for_alpn_npn_negotiation(Config, Data, Callback) ->
- process_flag(trap_exit, true),
- ServerOpts = proplists:get_value(server_rsa_opts, Config),
- ClientOpts0 = proplists:get_value(client_rsa_opts, Config),
- ClientOpts = [{alpn_advertised_protocols, [<<"spdy/2">>]},
- {client_preferred_next_protocols, {client, [<<"spdy/3">>, <<"http/1.1">>]}} | ClientOpts0],
-
- {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
-
- Data = "From openssl to erlang",
-
- Port = ssl_test_lib:inet_port(node()),
- CertFile = proplists:get_value(certfile, ServerOpts),
- KeyFile = proplists:get_value(keyfile, ServerOpts),
- Version = ssl_test_lib:protocol_version(Config),
-
- Exe = "openssl",
- Args = ["s_server", "-msg", "-alpn", "http/1.1,spdy/2", "-nextprotoneg",
- "spdy/3", "-accept", integer_to_list(Port), ssl_test_lib:version_flag(Version),
- "-cert", CertFile, "-key", KeyFile],
-
- OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
-
- ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)),
-
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE,
- erlang_ssl_receive_and_assert_negotiated_protocol, [<<"spdy/2">>, Data]}},
- {options, ClientOpts}]),
-
- Callback(Client, OpensslPort),
-
- %% Clean close down! Server needs to be closed first !!
- ssl_test_lib:close_port(OpensslPort),
-
- ssl_test_lib:close(Client),
- process_flag(trap_exit, false).
-
-start_erlang_server_and_openssl_client_for_alpn_npn_negotiation(Config, Data, Callback) ->
- process_flag(trap_exit, true),
- ServerOpts0 = proplists:get_value(server_rsa_opts, Config),
- ServerOpts = [{alpn_preferred_protocols, [<<"spdy/2">>]},
- {next_protocols_advertised, [<<"spdy/3">>, <<"http/1.1">>]} | ServerOpts0],
-
- {_, ServerNode, _} = ssl_test_lib:run_where(Config),
-
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, erlang_ssl_receive_and_assert_negotiated_protocol, [<<"spdy/2">>, Data]}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
- Version = ssl_test_lib:protocol_version(Config),
- Exe = "openssl",
- Args = ["s_client", "-alpn", "http/1.1,spdy/2", "-nextprotoneg", "spdy/3",
- "-msg", "-port", integer_to_list(Port), ssl_test_lib:version_flag(Version),
- "-host", "localhost"],
- OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
-
- Callback(Server, OpenSslPort),
-
- ssl_test_lib:close(Server),
- ssl_test_lib:close_port(OpenSslPort),
- process_flag(trap_exit, false).
-
-start_erlang_client_and_openssl_server_for_npn_negotiation(Config, Data, Callback) ->
- process_flag(trap_exit, true),
- ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
- ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
- ClientOpts = [{client_preferred_next_protocols, {client, [<<"spdy/2">>], <<"http/1.1">>}} | ClientOpts0],
-
- {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
-
- Data = "From openssl to erlang",
-
- Port = ssl_test_lib:inet_port(node()),
- CaCertFile = proplists:get_value(cacertfile, ServerOpts),
- CertFile = proplists:get_value(certfile, ServerOpts),
- KeyFile = proplists:get_value(keyfile, ServerOpts),
- Version = ssl_test_lib:protocol_version(Config),
-
- Exe = "openssl",
- Args = ["s_server", "-msg", "-nextprotoneg", "http/1.1,spdy/2", "-accept", integer_to_list(Port),
- ssl_test_lib:version_flag(Version),
- "-CAfile", CaCertFile,
- "-cert", CertFile, "-key", KeyFile],
- OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
-
- ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)),
-
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {mfa, {?MODULE,
- erlang_ssl_receive_and_assert_negotiated_protocol, [<<"spdy/2">>, Data]}},
- {options, ClientOpts}]),
-
- Callback(Client, OpensslPort),
-
- %% Clean close down! Server needs to be closed first !!
- ssl_test_lib:close_port(OpensslPort),
-
- ssl_test_lib:close(Client),
- process_flag(trap_exit, false).
-
-start_erlang_server_and_openssl_client_for_npn_negotiation(Config, Data, Callback) ->
- process_flag(trap_exit, true),
- ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config),
- ServerOpts = [{next_protocols_advertised, [<<"spdy/2">>]}, ServerOpts0],
-
- {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, erlang_ssl_receive_and_assert_negotiated_protocol, [<<"spdy/2">>, Data]}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
- Version = ssl_test_lib:protocol_version(Config),
-
- Exe = "openssl",
- Args = ["s_client", "-nextprotoneg", "http/1.0,spdy/2", "-msg", "-connect",
- hostname_format(Hostname) ++ ":"
- ++ integer_to_list(Port), ssl_test_lib:version_flag(Version)],
-
- OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
-
- Callback(Server, OpenSslPort),
-
- ssl_test_lib:close(Server),
-
- ssl_test_lib:close_port(OpenSslPort),
- process_flag(trap_exit, false).
-
-
-start_erlang_server_and_openssl_client_with_opts(Config, ErlangServerOpts, OpenSSLClientOpts, Data, Callback) ->
- process_flag(trap_exit, true),
- ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config),
- ServerOpts = ErlangServerOpts ++ ServerOpts0,
-
- {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-
-
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
- Version = ssl_test_lib:protocol_version(Config),
-
- Exe = "openssl",
- Args = ["s_client"] ++ OpenSSLClientOpts ++ ["-msg", "-connect",
- hostname_format(Hostname) ++ ":" ++ integer_to_list(Port),
- ssl_test_lib:version_flag(Version)],
-
- OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
-
- Callback(Server, OpenSslPort),
-
- ssl_test_lib:close(Server),
-
- ssl_test_lib:close_port(OpenSslPort),
- process_flag(trap_exit, false).
-
-
-erlang_ssl_receive_and_assert_negotiated_protocol(Socket, Protocol, Data) ->
- {ok, Protocol} = ssl:negotiated_protocol(Socket),
- erlang_ssl_receive(Socket, Data),
- {ok, Protocol} = ssl:negotiated_protocol(Socket),
- ok.
-
-erlang_ssl_receive(Socket, Data) ->
- ct:log("Connection info: ~p~n",
- [ssl:connection_information(Socket)]),
- receive
- {ssl, Socket, "R\n"} ->
- %% Swallow s_client renegotiation command.
- %% openssl s_client connected commands can appear on
- %% server side with some openssl versions.
- erlang_ssl_receive(Socket,Data);
- {ssl, Socket, Data} ->
- io:format("Received ~p~n",[Data]),
- %% open_ssl server sometimes hangs waiting in blocking read
- ssl:send(Socket, "Got it"),
- ok;
- {ssl, Socket, Byte} when length(Byte) == 1 ->
- erlang_ssl_receive(Socket, tl(Data));
- {Port, {data,Debug}} when is_port(Port) ->
- io:format("openssl ~s~n",[Debug]),
- erlang_ssl_receive(Socket,Data);
- Other ->
- ct:fail({unexpected_message, Other})
- end.
-
-connection_info(Socket, Version) ->
- case ssl:connection_information(Socket, [version]) of
- {ok, [{version, Version}] = Info} ->
- ct:log("Connection info: ~p~n", [Info]),
- ok;
- {ok, [{version, OtherVersion}]} ->
- {wrong_version, OtherVersion}
- end.
-
-connection_info_result(Socket) ->
- ssl:connection_information(Socket).
-
-
-delayed_send(Socket, [ErlData, OpenSslData]) ->
- ct:sleep(?SLEEP),
- ssl:send(Socket, ErlData),
- erlang_ssl_receive(Socket, OpenSslData).
-
-server_sent_garbage(Socket) ->
- receive
- server_sent_garbage ->
- {error, closed} == ssl:send(Socket, "data")
-
- end.
-
-send_wait_send(Socket, [ErlData, OpenSslData]) ->
- ssl:send(Socket, ErlData),
- ct:sleep(?SLEEP),
- ssl:send(Socket, ErlData),
- erlang_ssl_receive(Socket, OpenSslData).
-
-check_openssl_sni_support(Config) ->
- HelpText = os:cmd("openssl s_client --help"),
- case ssl_test_lib:is_sane_oppenssl_client() of
- true ->
- case string:str(HelpText, "-servername") of
- 0 ->
- {skip, "Current openssl doesn't support SNI"};
- _ ->
- Config
- end;
- false ->
- {skip, "Current openssl doesn't support SNI or extension handling is flawed"}
- end.
-
-
-check_openssl_npn_support(Config) ->
- HelpText = os:cmd("openssl s_client --help"),
- case string:str(HelpText, "nextprotoneg") of
- 0 ->
- {skip, "Openssl not compiled with nextprotoneg support"};
- _ ->
- Config
- end.
-
-check_openssl_alpn_support(Config) ->
- HelpText = os:cmd("openssl s_client --help"),
- case string:str(HelpText, "alpn") of
- 0 ->
- {skip, "Openssl not compiled with alpn support"};
- _ ->
- Config
- end.
-
-check_sane_openssl_renegotaite(Config, Version) when Version == 'tlsv1.1';
- Version == 'tlsv1.2' ->
- case os:cmd("openssl version") of
- "OpenSSL 1.0.1c" ++ _ ->
- {skip, "Known renegotiation bug in OpenSSL"};
- "OpenSSL 1.0.1b" ++ _ ->
- {skip, "Known renegotiation bug in OpenSSL"};
- "OpenSSL 1.0.1a" ++ _ ->
- {skip, "Known renegotiation bug in OpenSSL"};
- "OpenSSL 1.0.1 " ++ _ ->
- {skip, "Known renegotiation bug in OpenSSL"};
- _ ->
- check_sane_openssl_renegotaite(Config)
- end;
-check_sane_openssl_renegotaite(Config, _) ->
- check_sane_openssl_renegotaite(Config).
-
-check_sane_openssl_renegotaite(Config) ->
- case os:cmd("openssl version") of
- "OpenSSL 1.0.0" ++ _ ->
- {skip, "Known renegotiation bug in OpenSSL"};
- "OpenSSL 0.9.8" ++ _ ->
- {skip, "Known renegotiation bug in OpenSSL"};
- "OpenSSL 0.9.7" ++ _ ->
- {skip, "Known renegotiation bug in OpenSSL"};
- _ ->
- Config
- end.
-
-workaround_openssl_s_clinent() ->
- %% http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=683159
- %% https://bugs.archlinux.org/task/33919
- %% Bug seems to manifests it self if TLS version is not
- %% explicitly specified
- case os:cmd("openssl version") of
- "OpenSSL 1.0.1c" ++ _ ->
- ["-no_tls1_2"];
- "OpenSSL 1.0.1d" ++ _ ->
- ["-no_tls1_2"];
- "OpenSSL 1.0.1e" ++ _ ->
- ["-no_tls1_2"];
- "OpenSSL 1.0.1f" ++ _ ->
- ["-no_tls1_2"];
- _ ->
- []
- end.
-
-openssl_client_args(Version, Hostname, Port) ->
- ["s_client", "-connect", Hostname ++ ":" ++ integer_to_list(Port), ssl_test_lib:version_flag(Version)].
-
-openssl_client_args(Version, Hostname, Port, ServerName) ->
- ["s_client", "-connect", Hostname ++ ":" ++
- integer_to_list(Port), ssl_test_lib:version_flag(Version), "-servername", ServerName].
-
-
-hostname_format(Hostname) ->
- case lists:member($., Hostname) of
- true ->
- Hostname;
- false ->
- "localhost"
- end.
-
-
-openssl_has_common_ciphers(Ciphers) ->
- OCiphers = ssl_test_lib:common_ciphers(openssl),
- has_common_ciphers(Ciphers, OCiphers).
-
-has_common_ciphers([], _) ->
- false;
-has_common_ciphers([Cipher | Rest], OCiphers) ->
- case lists:member(Cipher, OCiphers) of
- true ->
- true;
- _ ->
- has_common_ciphers(Rest, OCiphers)
- end.
diff --git a/lib/ssl/test/tls_1_3_record_SUITE.erl b/lib/ssl/test/tls_1_3_record_SUITE.erl
new file mode 100644
index 0000000000..5df8853b1b
--- /dev/null
+++ b/lib/ssl/test/tls_1_3_record_SUITE.erl
@@ -0,0 +1,973 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2008-2018. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(tls_1_3_record_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("ssl/src/tls_record.hrl").
+-include_lib("ssl/src/tls_handshake.hrl").
+-include_lib("ssl/src/ssl_cipher.hrl").
+-include_lib("ssl/src/ssl_internal.hrl").
+
+all() ->
+ [encode_decode,
+ finished_verify_data,
+ '1_RTT_handshake'].
+
+init_per_suite(Config) ->
+ catch crypto:stop(),
+ try (ok == crypto:start()) andalso ssl_test_lib:sufficient_crypto_support('tlsv1.3') of
+ true ->
+ ssl_test_lib:clean_start(),
+ Config;
+ false ->
+ {skip, "Not enough crypto support for TLS-1.3"}
+ catch _:_ ->
+ {skip, "Crypto did not start"}
+ end.
+
+end_per_suite(_Config) ->
+ ssl:stop(),
+ application:unload(ssl),
+ application:stop(crypto).
+%%--------------------------------------------------------------------
+%% Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
+
+encode_decode() ->
+ [{doc,"Test TLS 1.3 record encode/decode functions"}].
+
+encode_decode(_Config) ->
+ ConnectionStates =
+ #{current_read =>
+ #{beast_mitigation => one_n_minus_one,
+ cipher_state =>
+ {cipher_state,
+ <<14,172,111,243,199,170,242,203,126,205,34,93,122,115,226,14,
+ 15,117,155,48,24,112,61,15,113,208,127,51,179,227,194,232>>,
+ <<197,54,168,218,54,91,157,58,30,201,197,142,51,58,53,231,228,
+ 131,57,122,170,78,82,196,30,48,23,16,95,255,185,236>>,
+ undefined,undefined,undefined,16},
+ client_verify_data => undefined,compression_state => undefined,
+ mac_secret => undefined,secure_renegotiation => undefined,
+ security_parameters =>
+ {security_parameters,
+ <<19,2>>,
+ 0,8,2,undefined,undefined,undefined,undefined,undefined,
+ sha384,undefined,undefined,
+ {handshake_secret,
+ <<128,229,186,211,62,127,182,20,62,166,233,23,135,64,121,
+ 3,104,251,214,161,253,31,3,2,232,37,8,221,189,72,64,218,
+ 121,41,112,148,254,34,68,164,228,60,161,201,132,55,56,
+ 157>>},
+ undefined,
+ <<92,24,205,75,244,60,136,212,250,32,214,20,37,3,213,87,61,207,
+ 147,61,168,145,177,118,160,153,33,53,48,108,191,174>>,
+ undefined},
+ sequence_number => 0,server_verify_data => undefined},
+ current_write =>
+ #{beast_mitigation => one_n_minus_one,
+ cipher_state =>
+ {cipher_state,
+ <<14,172,111,243,199,170,242,203,126,205,34,93,122,115,226,14,
+ 15,117,155,48,24,112,61,15,113,208,127,51,179,227,194,232>>,
+ <<197,54,168,218,54,91,157,58,30,201,197,142,51,58,53,231,228,
+ 131,57,122,170,78,82,196,30,48,23,16,95,255,185,236>>,
+ undefined,undefined,undefined,16},
+ client_verify_data => undefined,compression_state => undefined,
+ mac_secret => undefined,secure_renegotiation => undefined,
+ security_parameters =>
+ {security_parameters,
+ <<19,2>>,
+ 0,8,2,undefined,undefined,undefined,undefined,undefined,
+ sha384,undefined,undefined,
+ {handshake_secret,
+ <<128,229,186,211,62,127,182,20,62,166,233,23,135,64,121,
+ 3,104,251,214,161,253,31,3,2,232,37,8,221,189,72,64,218,
+ 121,41,112,148,254,34,68,164,228,60,161,201,132,55,56,
+ 157>>},
+ undefined,
+ <<92,24,205,75,244,60,136,212,250,32,214,20,37,3,213,87,61,207,
+ 147,61,168,145,177,118,160,153,33,53,48,108,191,174>>,
+ undefined},
+ sequence_number => 0,server_verify_data => undefined}},
+
+ PlainText = [11,
+ <<0,2,175>>,
+ <<0,0,2,171,0,2,166,48,130,2,162,48,130,1,138,2,9,0,186,57,220,137,88,255,
+ 191,235,48,13,6,9,42,134,72,134,247,13,1,1,11,5,0,48,18,49,16,48,14,6,3,85,
+ 4,3,12,7,84,101,115,116,32,67,65,48,30,23,13,49,56,48,53,48,52,49,52,49,50,
+ 51,56,90,23,13,50,56,48,50,48,52,49,52,49,50,51,56,90,48,20,49,18,48,16,6,
+ 3,85,4,3,12,9,108,111,99,97,108,104,111,115,116,48,130,1,34,48,13,6,9,42,
+ 134,72,134,247,13,1,1,1,5,0,3,130,1,15,0,48,130,1,10,2,130,1,1,0,169,40,
+ 144,176,121,63,134,97,144,126,243,183,225,157,37,131,183,225,87,243,23,88,
+ 230,70,9,134,32,147,7,27,167,98,51,81,224,75,199,12,229,251,195,207,75,179,
+ 181,78,128,3,255,44,58,39,43,172,142,45,186,58,51,65,187,199,154,153,245,
+ 70,133,137,1,27,87,42,116,65,251,129,109,145,233,97,171,71,54,213,185,74,
+ 209,166,11,218,189,119,206,86,170,60,212,213,85,189,30,50,215,23,185,53,
+ 132,238,132,176,198,250,139,251,198,221,225,128,109,113,23,220,39,143,71,
+ 30,59,189,51,244,61,158,214,146,180,196,103,169,189,221,136,78,129,216,148,
+ 2,9,8,65,37,224,215,233,13,209,21,235,20,143,33,74,59,53,208,90,152,94,251,
+ 54,114,171,39,88,230,227,158,211,135,37,182,67,205,161,59,20,138,58,253,15,
+ 53,48,8,157,9,95,197,9,177,116,21,54,9,125,78,109,182,83,20,16,234,223,116,
+ 41,155,123,87,77,17,120,153,246,239,124,130,105,219,166,146,242,151,66,198,
+ 75,72,63,28,246,86,16,244,223,22,36,50,15,247,222,98,6,152,136,154,72,150,
+ 73,127,2,3,1,0,1,48,13,6,9,42,134,72,134,247,13,1,1,11,5,0,3,130,1,1,0,76,
+ 33,54,160,229,219,219,193,150,116,245,252,18,39,235,145,86,12,167,171,52,
+ 117,166,30,83,5,216,245,177,217,247,95,1,136,94,246,212,108,248,230,111,
+ 225,202,189,6,129,8,70,128,245,18,204,215,87,82,129,253,227,122,66,182,184,
+ 189,30,193,169,144,218,216,109,105,110,215,144,60,104,162,178,101,164,218,
+ 122,60,37,41,143,57,150,52,59,51,112,238,113,239,168,114,69,183,143,154,73,
+ 61,58,80,247,172,95,251,55,28,186,28,200,206,230,118,243,92,202,189,49,76,
+ 124,252,76,0,247,112,85,194,69,59,222,163,228,103,49,110,104,109,251,155,
+ 138,9,37,167,49,189,48,134,52,158,185,129,24,96,153,196,251,90,206,76,239,
+ 175,119,174,165,133,108,222,125,237,125,187,149,152,83,190,16,202,94,202,
+ 201,40,218,22,254,63,189,41,174,97,140,203,70,18,196,118,237,175,134,79,78,
+ 246,2,61,54,77,186,112,32,17,193,192,188,217,252,215,200,7,245,180,179,132,
+ 183,212,229,155,15,152,206,135,56,81,88,3,123,244,149,110,182,72,109,70,62,
+ 146,152,146,151,107,126,216,210,9,93,0,0>>],
+
+ {[_Header|Encoded], _} = tls_record_1_3:encode_plain_text(22, PlainText, ConnectionStates),
+ CipherText = #ssl_tls{type = 23, version = {3,3}, fragment = Encoded},
+
+ {#ssl_tls{type = 22, version = {3,4}, fragment = DecodedText}, _} =
+ tls_record_1_3:decode_cipher_text(CipherText, ConnectionStates),
+
+ DecodedText = iolist_to_binary(PlainText),
+ ct:log("Decoded: ~p ~n", [DecodedText]),
+ ok.
+%%--------------------------------------------------------------------
+'1_RTT_handshake'() ->
+ [{doc,"Test TLS 1.3 1-RTT Handshake"}].
+
+'1_RTT_handshake'(_Config) ->
+ %% ConnectionStates with NULL cipher
+ ConnStatesNull =
+ #{current_write =>
+ #{security_parameters =>
+ #security_parameters{cipher_suite = ?TLS_NULL_WITH_NULL_NULL},
+ sequence_number => 0
+ }
+ },
+
+ %% {client} construct a ClientHello handshake message:
+ %%
+ %% ClientHello (196 octets): 01 00 00 c0 03 03 cb 34 ec b1 e7 81 63
+ %% ba 1c 38 c6 da cb 19 6a 6d ff a2 1a 8d 99 12 ec 18 a2 ef 62 83
+ %% 02 4d ec e7 00 00 06 13 01 13 03 13 02 01 00 00 91 00 00 00 0b
+ %% 00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 00 00 0a 00 14 00
+ %% 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 01 03 01 04 00 23
+ %% 00 00 00 33 00 26 00 24 00 1d 00 20 99 38 1d e5 60 e4 bd 43 d2
+ %% 3d 8e 43 5a 7d ba fe b3 c0 6e 51 c1 3c ae 4d 54 13 69 1e 52 9a
+ %% af 2c 00 2b 00 03 02 03 04 00 0d 00 20 00 1e 04 03 05 03 06 03
+ %% 02 03 08 04 08 05 08 06 04 01 05 01 06 01 02 01 04 02 05 02 06
+ %% 02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01
+ %%
+ %% {client} send handshake record:
+ %%
+ %% payload (196 octets): 01 00 00 c0 03 03 cb 34 ec b1 e7 81 63 ba
+ %% 1c 38 c6 da cb 19 6a 6d ff a2 1a 8d 99 12 ec 18 a2 ef 62 83 02
+ %% 4d ec e7 00 00 06 13 01 13 03 13 02 01 00 00 91 00 00 00 0b 00
+ %% 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 00 00 0a 00 14 00 12
+ %% 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 01 03 01 04 00 23 00
+ %% 00 00 33 00 26 00 24 00 1d 00 20 99 38 1d e5 60 e4 bd 43 d2 3d
+ %% 8e 43 5a 7d ba fe b3 c0 6e 51 c1 3c ae 4d 54 13 69 1e 52 9a af
+ %% 2c 00 2b 00 03 02 03 04 00 0d 00 20 00 1e 04 03 05 03 06 03 02
+ %% 03 08 04 08 05 08 06 04 01 05 01 06 01 02 01 04 02 05 02 06 02
+ %% 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01
+ %%
+ %% complete record (201 octets): 16 03 01 00 c4 01 00 00 c0 03 03 cb
+ %% 34 ec b1 e7 81 63 ba 1c 38 c6 da cb 19 6a 6d ff a2 1a 8d 99 12
+ %% ec 18 a2 ef 62 83 02 4d ec e7 00 00 06 13 01 13 03 13 02 01 00
+ %% 00 91 00 00 00 0b 00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01
+ %% 00 00 0a 00 14 00 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02
+ %% 01 03 01 04 00 23 00 00 00 33 00 26 00 24 00 1d 00 20 99 38 1d
+ %% e5 60 e4 bd 43 d2 3d 8e 43 5a 7d ba fe b3 c0 6e 51 c1 3c ae 4d
+ %% 54 13 69 1e 52 9a af 2c 00 2b 00 03 02 03 04 00 0d 00 20 00 1e
+ %% 04 03 05 03 06 03 02 03 08 04 08 05 08 06 04 01 05 01 06 01 02
+ %% 01 04 02 05 02 06 02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01
+ ClientHello =
+ hexstr2bin("01 00 00 c0 03 03 cb 34 ec b1 e7 81 63
+ ba 1c 38 c6 da cb 19 6a 6d ff a2 1a 8d 99 12 ec 18 a2 ef 62 83
+ 02 4d ec e7 00 00 06 13 01 13 03 13 02 01 00 00 91 00 00 00 0b
+ 00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 00 00 0a 00 14 00
+ 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 01 03 01 04 00 23
+ 00 00 00 33 00 26 00 24 00 1d 00 20 99 38 1d e5 60 e4 bd 43 d2
+ 3d 8e 43 5a 7d ba fe b3 c0 6e 51 c1 3c ae 4d 54 13 69 1e 52 9a
+ af 2c 00 2b 00 03 02 03 04 00 0d 00 20 00 1e 04 03 05 03 06 03
+ 02 03 08 04 08 05 08 06 04 01 05 01 06 01 02 01 04 02 05 02 06
+ 02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01"),
+
+ ClientHelloRecord =
+ %% Current implementation always sets
+ %% legacy_record_version to Ox0303
+ hexstr2bin("16 03 03 00 c4 01 00 00 c0 03 03 cb
+ 34 ec b1 e7 81 63 ba 1c 38 c6 da cb 19 6a 6d ff a2 1a 8d 99 12
+ ec 18 a2 ef 62 83 02 4d ec e7 00 00 06 13 01 13 03 13 02 01 00
+ 00 91 00 00 00 0b 00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01
+ 00 00 0a 00 14 00 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02
+ 01 03 01 04 00 23 00 00 00 33 00 26 00 24 00 1d 00 20 99 38 1d
+ e5 60 e4 bd 43 d2 3d 8e 43 5a 7d ba fe b3 c0 6e 51 c1 3c ae 4d
+ 54 13 69 1e 52 9a af 2c 00 2b 00 03 02 03 04 00 0d 00 20 00 1e
+ 04 03 05 03 06 03 02 03 08 04 08 05 08 06 04 01 05 01 06 01 02
+ 01 04 02 05 02 06 02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01"),
+
+ {CHEncrypted, _} =
+ tls_record:encode_handshake(ClientHello, {3,4}, ConnStatesNull),
+ ClientHelloRecord = iolist_to_binary(CHEncrypted),
+
+ %% {server} extract secret "early":
+ %%
+ %% salt: 0 (all zero octets)
+ %%
+ %% IKM (32 octets): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ %% 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ %%
+ %% secret (32 octets): 33 ad 0a 1c 60 7e c0 3b 09 e6 cd 98 93 68 0c
+ %% e2 10 ad f3 00 aa 1f 26 60 e1 b2 2e 10 f1 70 f9 2a
+ HKDFAlgo = sha256,
+ Salt = binary:copy(<<?BYTE(0)>>, 32),
+ IKM = binary:copy(<<?BYTE(0)>>, 32),
+ EarlySecret =
+ hexstr2bin("33 ad 0a 1c 60 7e c0 3b 09 e6 cd 98 93 68 0c
+ e2 10 ad f3 00 aa 1f 26 60 e1 b2 2e 10 f1 70 f9 2a"),
+
+ {early_secret, EarlySecret} = tls_v1:key_schedule(early_secret, HKDFAlgo, {psk, Salt}),
+
+ %% {client} create an ephemeral x25519 key pair:
+ %%
+ %% private key (32 octets): 49 af 42 ba 7f 79 94 85 2d 71 3e f2 78
+ %% 4b cb ca a7 91 1d e2 6a dc 56 42 cb 63 45 40 e7 ea 50 05
+ %%
+ %% public key (32 octets): 99 38 1d e5 60 e4 bd 43 d2 3d 8e 43 5a 7d
+ %% ba fe b3 c0 6e 51 c1 3c ae 4d 54 13 69 1e 52 9a af 2c
+ CPublicKey =
+ hexstr2bin("99 38 1d e5 60 e4 bd 43 d2 3d 8e 43 5a 7d
+ ba fe b3 c0 6e 51 c1 3c ae 4d 54 13 69 1e 52 9a af 2c"),
+
+ %% {server} create an ephemeral x25519 key pair:
+ %%
+ %% private key (32 octets): b1 58 0e ea df 6d d5 89 b8 ef 4f 2d 56
+ %% 52 57 8c c8 10 e9 98 01 91 ec 8d 05 83 08 ce a2 16 a2 1e
+ %%
+ %% public key (32 octets): c9 82 88 76 11 20 95 fe 66 76 2b db f7 c6
+ %% 72 e1 56 d6 cc 25 3b 83 3d f1 dd 69 b1 b0 4e 75 1f 0f
+ SPrivateKey =
+ hexstr2bin("b1 58 0e ea df 6d d5 89 b8 ef 4f 2d 56
+ 52 57 8c c8 10 e9 98 01 91 ec 8d 05 83 08 ce a2 16 a2 1e"),
+
+ SPublicKey =
+ hexstr2bin("c9 82 88 76 11 20 95 fe 66 76 2b db f7 c6
+ 72 e1 56 d6 cc 25 3b 83 3d f1 dd 69 b1 b0 4e 75 1f 0f"),
+
+ %% {server} construct a ServerHello handshake message:
+ %%
+ %% ServerHello (90 octets): 02 00 00 56 03 03 a6 af 06 a4 12 18 60
+ %% dc 5e 6e 60 24 9c d3 4c 95 93 0c 8a c5 cb 14 34 da c1 55 77 2e
+ %% d3 e2 69 28 00 13 01 00 00 2e 00 33 00 24 00 1d 00 20 c9 82 88
+ %% 76 11 20 95 fe 66 76 2b db f7 c6 72 e1 56 d6 cc 25 3b 83 3d f1
+ %% dd 69 b1 b0 4e 75 1f 0f 00 2b 00 02 03 04
+ ServerHello =
+ hexstr2bin("02 00 00 56 03 03 a6 af 06 a4 12 18 60
+ dc 5e 6e 60 24 9c d3 4c 95 93 0c 8a c5 cb 14 34 da c1 55 77 2e
+ d3 e2 69 28 00 13 01 00 00 2e 00 33 00 24 00 1d 00 20 c9 82 88
+ 76 11 20 95 fe 66 76 2b db f7 c6 72 e1 56 d6 cc 25 3b 83 3d f1
+ dd 69 b1 b0 4e 75 1f 0f 00 2b 00 02 03 04"),
+
+ %% {server} derive secret for handshake "tls13 derived":
+ %%
+ %% PRK (32 octets): 33 ad 0a 1c 60 7e c0 3b 09 e6 cd 98 93 68 0c e2
+ %% 10 ad f3 00 aa 1f 26 60 e1 b2 2e 10 f1 70 f9 2a
+ %%
+ %% hash (32 octets): e3 b0 c4 42 98 fc 1c 14 9a fb f4 c8 99 6f b9 24
+ %% 27 ae 41 e4 64 9b 93 4c a4 95 99 1b 78 52 b8 55
+ %%
+ %% info (49 octets): 00 20 0d 74 6c 73 31 33 20 64 65 72 69 76 65 64
+ %% 20 e3 b0 c4 42 98 fc 1c 14 9a fb f4 c8 99 6f b9 24 27 ae 41 e4
+ %% 64 9b 93 4c a4 95 99 1b 78 52 b8 55
+ %%
+ %% expanded (32 octets): 6f 26 15 a1 08 c7 02 c5 67 8f 54 fc 9d ba
+ %% b6 97 16 c0 76 18 9c 48 25 0c eb ea c3 57 6c 36 11 ba
+ Hash =
+ hexstr2bin("e3 b0 c4 42 98 fc 1c 14 9a fb f4 c8 99 6f b9 24
+ 27 ae 41 e4 64 9b 93 4c a4 95 99 1b 78 52 b8 55"),
+
+ Hash = crypto:hash(HKDFAlgo, <<>>),
+
+ Info =
+ hexstr2bin("00 20 0d 74 6c 73 31 33 20 64 65 72 69 76 65 64
+ 20 e3 b0 c4 42 98 fc 1c 14 9a fb f4 c8 99 6f b9 24 27 ae 41 e4
+ 64 9b 93 4c a4 95 99 1b 78 52 b8 55"),
+
+ Info = tls_v1:create_info(<<"derived">>, Hash, ssl_cipher:hash_size(HKDFAlgo)),
+
+ Expanded =
+ hexstr2bin("6f 26 15 a1 08 c7 02 c5 67 8f 54 fc 9d ba
+ b6 97 16 c0 76 18 9c 48 25 0c eb ea c3 57 6c 36 11 ba"),
+
+ Expanded = tls_v1:derive_secret(EarlySecret, <<"derived">>, <<>>, HKDFAlgo),
+
+ %% {server} extract secret "handshake":
+ %%
+ %% salt (32 octets): 6f 26 15 a1 08 c7 02 c5 67 8f 54 fc 9d ba b6 97
+ %% 16 c0 76 18 9c 48 25 0c eb ea c3 57 6c 36 11 ba
+ %%
+ %% IKM (32 octets): 8b d4 05 4f b5 5b 9d 63 fd fb ac f9 f0 4b 9f 0d
+ %% 35 e6 d6 3f 53 75 63 ef d4 62 72 90 0f 89 49 2d
+ %%
+ %% secret (32 octets): 1d c8 26 e9 36 06 aa 6f dc 0a ad c1 2f 74 1b
+ %% 01 04 6a a6 b9 9f 69 1e d2 21 a9 f0 ca 04 3f be ac
+
+ %% salt = Expanded
+ HandshakeIKM =
+ hexstr2bin("8b d4 05 4f b5 5b 9d 63 fd fb ac f9 f0 4b 9f 0d
+ 35 e6 d6 3f 53 75 63 ef d4 62 72 90 0f 89 49 2d"),
+
+ HandshakeSecret =
+ hexstr2bin("1d c8 26 e9 36 06 aa 6f dc 0a ad c1 2f 74 1b
+ 01 04 6a a6 b9 9f 69 1e d2 21 a9 f0 ca 04 3f be ac"),
+
+ HandshakeIKM = crypto:compute_key(ecdh, CPublicKey, SPrivateKey, x25519),
+
+ {handshake_secret, HandshakeSecret} =
+ tls_v1:key_schedule(handshake_secret, HKDFAlgo, HandshakeIKM,
+ {early_secret, EarlySecret}),
+
+ %% {server} derive secret "tls13 c hs traffic":
+ %%
+ %% PRK (32 octets): 1d c8 26 e9 36 06 aa 6f dc 0a ad c1 2f 74 1b 01
+ %% 04 6a a6 b9 9f 69 1e d2 21 a9 f0 ca 04 3f be ac
+ %%
+ %% hash (32 octets): 86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58 ed
+ %% d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8
+ %%
+ %% info (54 octets): 00 20 12 74 6c 73 31 33 20 63 20 68 73 20 74 72
+ %% 61 66 66 69 63 20 86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58
+ %% ed d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8
+ %%
+ %% expanded (32 octets): b3 ed db 12 6e 06 7f 35 a7 80 b3 ab f4 5e
+ %% 2d 8f 3b 1a 95 07 38 f5 2e 96 00 74 6a 0e 27 a5 5a 21
+
+ %% PRK = HandshakeSecret
+ CHSTHash =
+ hexstr2bin("86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58 ed
+ d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8"),
+
+ CHSTInfo =
+ hexstr2bin("00 20 12 74 6c 73 31 33 20 63 20 68 73 20 74 72
+ 61 66 66 69 63 20 86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58
+ ed d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8"),
+
+ CHSTrafficSecret =
+ hexstr2bin(" b3 ed db 12 6e 06 7f 35 a7 80 b3 ab f4 5e
+ 2d 8f 3b 1a 95 07 38 f5 2e 96 00 74 6a 0e 27 a5 5a 21"),
+
+ CHSH = <<ClientHello/binary,ServerHello/binary>>,
+ CHSTHash = crypto:hash(HKDFAlgo, CHSH),
+ CHSTInfo = tls_v1:create_info(<<"c hs traffic">>, CHSTHash, ssl_cipher:hash_size(HKDFAlgo)),
+
+ CHSTrafficSecret =
+ tls_v1:client_handshake_traffic_secret(HKDFAlgo, {handshake_secret, HandshakeSecret}, CHSH),
+
+ %% {server} derive secret "tls13 s hs traffic":
+ %%
+ %% PRK (32 octets): 1d c8 26 e9 36 06 aa 6f dc 0a ad c1 2f 74 1b 01
+ %% 04 6a a6 b9 9f 69 1e d2 21 a9 f0 ca 04 3f be ac
+ %%
+ %% hash (32 octets): 86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58 ed
+ %% d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8
+ %%
+ %% info (54 octets): 00 20 12 74 6c 73 31 33 20 73 20 68 73 20 74 72
+ %% 61 66 66 69 63 20 86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58
+ %% ed d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8
+ %%
+ %% expanded (32 octets): b6 7b 7d 69 0c c1 6c 4e 75 e5 42 13 cb 2d
+ %% 37 b4 e9 c9 12 bc de d9 10 5d 42 be fd 59 d3 91 ad 38
+
+ %% PRK = HandshakeSecret
+ %% hash = CHSTHash
+ SHSTInfo =
+ hexstr2bin("00 20 12 74 6c 73 31 33 20 73 20 68 73 20 74 72
+ 61 66 66 69 63 20 86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58
+ ed d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8"),
+
+ SHSTrafficSecret =
+ hexstr2bin("b6 7b 7d 69 0c c1 6c 4e 75 e5 42 13 cb 2d
+ 37 b4 e9 c9 12 bc de d9 10 5d 42 be fd 59 d3 91 ad 38"),
+
+ SHSTInfo = tls_v1:create_info(<<"s hs traffic">>, CHSTHash, ssl_cipher:hash_size(HKDFAlgo)),
+
+ SHSTrafficSecret =
+ tls_v1:server_handshake_traffic_secret(HKDFAlgo, {handshake_secret, HandshakeSecret}, CHSH),
+
+
+ %% {server} derive secret for master "tls13 derived":
+ %%
+ %% PRK (32 octets): 1d c8 26 e9 36 06 aa 6f dc 0a ad c1 2f 74 1b 01
+ %% 04 6a a6 b9 9f 69 1e d2 21 a9 f0 ca 04 3f be ac
+ %%
+ %% hash (32 octets): e3 b0 c4 42 98 fc 1c 14 9a fb f4 c8 99 6f b9 24
+ %% 27 ae 41 e4 64 9b 93 4c a4 95 99 1b 78 52 b8 55
+ %%
+ %% info (49 octets): 00 20 0d 74 6c 73 31 33 20 64 65 72 69 76 65 64
+ %% 20 e3 b0 c4 42 98 fc 1c 14 9a fb f4 c8 99 6f b9 24 27 ae 41 e4
+ %% 64 9b 93 4c a4 95 99 1b 78 52 b8 55
+ %%
+ %% expanded (32 octets): 43 de 77 e0 c7 77 13 85 9a 94 4d b9 db 25
+ %% 90 b5 31 90 a6 5b 3e e2 e4 f1 2d d7 a0 bb 7c e2 54 b4
+
+ %% PRK = HandshakeSecret
+ %% hash = Hash
+ %% info = Info
+ MasterDeriveSecret =
+ hexstr2bin("43 de 77 e0 c7 77 13 85 9a 94 4d b9 db 25
+ 90 b5 31 90 a6 5b 3e e2 e4 f1 2d d7 a0 bb 7c e2 54 b4"),
+
+ MasterDeriveSecret = tls_v1:derive_secret(HandshakeSecret, <<"derived">>, <<>>, HKDFAlgo),
+
+ %% {server} extract secret "master":
+ %%
+ %% salt (32 octets): 43 de 77 e0 c7 77 13 85 9a 94 4d b9 db 25 90 b5
+ %% 31 90 a6 5b 3e e2 e4 f1 2d d7 a0 bb 7c e2 54 b4
+ %%
+ %% IKM (32 octets): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ %% 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ %%
+ %% secret (32 octets): 18 df 06 84 3d 13 a0 8b f2 a4 49 84 4c 5f 8a
+ %% 47 80 01 bc 4d 4c 62 79 84 d5 a4 1d a8 d0 40 29 19
+
+ %% salt = MasterDeriveSecret
+ %% IKM = IKM
+ MasterSecret =
+ hexstr2bin("18 df 06 84 3d 13 a0 8b f2 a4 49 84 4c 5f 8a
+ 47 80 01 bc 4d 4c 62 79 84 d5 a4 1d a8 d0 40 29 19"),
+
+ {master_secret, MasterSecret} =
+ tls_v1:key_schedule(master_secret, HKDFAlgo, {handshake_secret, HandshakeSecret}),
+
+ %% {server} send handshake record:
+ %%
+ %% payload (90 octets): 02 00 00 56 03 03 a6 af 06 a4 12 18 60 dc 5e
+ %% 6e 60 24 9c d3 4c 95 93 0c 8a c5 cb 14 34 da c1 55 77 2e d3 e2
+ %% 69 28 00 13 01 00 00 2e 00 33 00 24 00 1d 00 20 c9 82 88 76 11
+ %% 20 95 fe 66 76 2b db f7 c6 72 e1 56 d6 cc 25 3b 83 3d f1 dd 69
+ %% b1 b0 4e 75 1f 0f 00 2b 00 02 03 04
+ %%
+ %% complete record (95 octets): 16 03 03 00 5a 02 00 00 56 03 03 a6
+ %% af 06 a4 12 18 60 dc 5e 6e 60 24 9c d3 4c 95 93 0c 8a c5 cb 14
+ %% 34 da c1 55 77 2e d3 e2 69 28 00 13 01 00 00 2e 00 33 00 24 00
+ %% 1d 00 20 c9 82 88 76 11 20 95 fe 66 76 2b db f7 c6 72 e1 56 d6
+ %% cc 25 3b 83 3d f1 dd 69 b1 b0 4e 75 1f 0f 00 2b 00 02 03 04
+
+ %% payload = ServerHello
+ ServerHelloRecord =
+ hexstr2bin("16 03 03 00 5a 02 00 00 56 03 03 a6
+ af 06 a4 12 18 60 dc 5e 6e 60 24 9c d3 4c 95 93 0c 8a c5 cb 14
+ 34 da c1 55 77 2e d3 e2 69 28 00 13 01 00 00 2e 00 33 00 24 00
+ 1d 00 20 c9 82 88 76 11 20 95 fe 66 76 2b db f7 c6 72 e1 56 d6
+ cc 25 3b 83 3d f1 dd 69 b1 b0 4e 75 1f 0f 00 2b 00 02 03 04"),
+
+ {SHEncrypted, _} =
+ tls_record:encode_handshake(ServerHello, {3,4}, ConnStatesNull),
+ ServerHelloRecord = iolist_to_binary(SHEncrypted),
+
+ %% {server} derive write traffic keys for handshake data:
+ %%
+ %% PRK (32 octets): b6 7b 7d 69 0c c1 6c 4e 75 e5 42 13 cb 2d 37 b4
+ %% e9 c9 12 bc de d9 10 5d 42 be fd 59 d3 91 ad 38
+ %%
+ %% key info (13 octets): 00 10 09 74 6c 73 31 33 20 6b 65 79 00
+ %%
+ %% key expanded (16 octets): 3f ce 51 60 09 c2 17 27 d0 f2 e4 e8 6e
+ %% e4 03 bc
+ %%
+ %% iv info (12 octets): 00 0c 08 74 6c 73 31 33 20 69 76 00
+ %%
+ %% iv expanded (12 octets): 5d 31 3e b2 67 12 76 ee 13 00 0b 30
+
+ %% PRK = SHSTrafficSecret
+ WriteKeyInfo =
+ hexstr2bin("00 10 09 74 6c 73 31 33 20 6b 65 79 00"),
+
+ WriteKey =
+ hexstr2bin("3f ce 51 60 09 c2 17 27 d0 f2 e4 e8 6e e4 03 bc"),
+
+ WriteIVInfo =
+ hexstr2bin("00 0c 08 74 6c 73 31 33 20 69 76 00"),
+
+ WriteIV =
+ hexstr2bin(" 5d 31 3e b2 67 12 76 ee 13 00 0b 30"),
+
+ Cipher = aes_128_gcm, %% TODO: get from ServerHello
+
+ WriteKeyInfo = tls_v1:create_info(<<"key">>, <<>>, ssl_cipher:key_material(Cipher)),
+ %% TODO: remove hardcoded IV size
+ WriteIVInfo = tls_v1:create_info(<<"iv">>, <<>>, 12),
+
+ {WriteKey, WriteIV} = tls_v1:calculate_traffic_keys(HKDFAlgo, Cipher, SHSTrafficSecret),
+
+ %% {server} construct an EncryptedExtensions handshake message:
+ %%
+ %% EncryptedExtensions (40 octets): 08 00 00 24 00 22 00 0a 00 14 00
+ %% 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 01 03 01 04 00 1c
+ %% 00 02 40 01 00 00 00 00
+ %%
+ %% {server} construct a Certificate handshake message:
+ %%
+ %% Certificate (445 octets): 0b 00 01 b9 00 00 01 b5 00 01 b0 30 82
+ %% 01 ac 30 82 01 15 a0 03 02 01 02 02 01 02 30 0d 06 09 2a 86 48
+ %% 86 f7 0d 01 01 0b 05 00 30 0e 31 0c 30 0a 06 03 55 04 03 13 03
+ %% 72 73 61 30 1e 17 0d 31 36 30 37 33 30 30 31 32 33 35 39 5a 17
+ %% 0d 32 36 30 37 33 30 30 31 32 33 35 39 5a 30 0e 31 0c 30 0a 06
+ %% 03 55 04 03 13 03 72 73 61 30 81 9f 30 0d 06 09 2a 86 48 86 f7
+ %% 0d 01 01 01 05 00 03 81 8d 00 30 81 89 02 81 81 00 b4 bb 49 8f
+ %% 82 79 30 3d 98 08 36 39 9b 36 c6 98 8c 0c 68 de 55 e1 bd b8 26
+ %% d3 90 1a 24 61 ea fd 2d e4 9a 91 d0 15 ab bc 9a 95 13 7a ce 6c
+ %% 1a f1 9e aa 6a f9 8c 7c ed 43 12 09 98 e1 87 a8 0e e0 cc b0 52
+ %% 4b 1b 01 8c 3e 0b 63 26 4d 44 9a 6d 38 e2 2a 5f da 43 08 46 74
+ %% 80 30 53 0e f0 46 1c 8c a9 d9 ef bf ae 8e a6 d1 d0 3e 2b d1 93
+ %% ef f0 ab 9a 80 02 c4 74 28 a6 d3 5a 8d 88 d7 9f 7f 1e 3f 02 03
+ %% 01 00 01 a3 1a 30 18 30 09 06 03 55 1d 13 04 02 30 00 30 0b 06
+ %% 03 55 1d 0f 04 04 03 02 05 a0 30 0d 06 09 2a 86 48 86 f7 0d 01
+ %% 01 0b 05 00 03 81 81 00 85 aa d2 a0 e5 b9 27 6b 90 8c 65 f7 3a
+ %% 72 67 17 06 18 a5 4c 5f 8a 7b 33 7d 2d f7 a5 94 36 54 17 f2 ea
+ %% e8 f8 a5 8c 8f 81 72 f9 31 9c f3 6b 7f d6 c5 5b 80 f2 1a 03 01
+ %% 51 56 72 60 96 fd 33 5e 5e 67 f2 db f1 02 70 2e 60 8c ca e6 be
+ %% c1 fc 63 a4 2a 99 be 5c 3e b7 10 7c 3c 54 e9 b9 eb 2b d5 20 3b
+ %% 1c 3b 84 e0 a8 b2 f7 59 40 9b a3 ea c9 d9 1d 40 2d cc 0c c8 f8
+ %% 96 12 29 ac 91 87 b4 2b 4d e1 00 00
+ %%
+ %% {server} construct a CertificateVerify handshake message:
+ %%
+ %% CertificateVerify (136 octets): 0f 00 00 84 08 04 00 80 5a 74 7c
+ %% 5d 88 fa 9b d2 e5 5a b0 85 a6 10 15 b7 21 1f 82 4c d4 84 14 5a
+ %% b3 ff 52 f1 fd a8 47 7b 0b 7a bc 90 db 78 e2 d3 3a 5c 14 1a 07
+ %% 86 53 fa 6b ef 78 0c 5e a2 48 ee aa a7 85 c4 f3 94 ca b6 d3 0b
+ %% be 8d 48 59 ee 51 1f 60 29 57 b1 54 11 ac 02 76 71 45 9e 46 44
+ %% 5c 9e a5 8c 18 1e 81 8e 95 b8 c3 fb 0b f3 27 84 09 d3 be 15 2a
+ %% 3d a5 04 3e 06 3d da 65 cd f5 ae a2 0d 53 df ac d4 2f 74 f3
+ EncryptedExtensions =
+ hexstr2bin("08 00 00 24 00 22 00 0a 00 14 00
+ 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 01 03 01 04 00 1c
+ 00 02 40 01 00 00 00 00"),
+
+ Certificate =
+ hexstr2bin("0b 00 01 b9 00 00 01 b5 00 01 b0 30 82
+ 01 ac 30 82 01 15 a0 03 02 01 02 02 01 02 30 0d 06 09 2a 86 48
+ 86 f7 0d 01 01 0b 05 00 30 0e 31 0c 30 0a 06 03 55 04 03 13 03
+ 72 73 61 30 1e 17 0d 31 36 30 37 33 30 30 31 32 33 35 39 5a 17
+ 0d 32 36 30 37 33 30 30 31 32 33 35 39 5a 30 0e 31 0c 30 0a 06
+ 03 55 04 03 13 03 72 73 61 30 81 9f 30 0d 06 09 2a 86 48 86 f7
+ 0d 01 01 01 05 00 03 81 8d 00 30 81 89 02 81 81 00 b4 bb 49 8f
+ 82 79 30 3d 98 08 36 39 9b 36 c6 98 8c 0c 68 de 55 e1 bd b8 26
+ d3 90 1a 24 61 ea fd 2d e4 9a 91 d0 15 ab bc 9a 95 13 7a ce 6c
+ 1a f1 9e aa 6a f9 8c 7c ed 43 12 09 98 e1 87 a8 0e e0 cc b0 52
+ 4b 1b 01 8c 3e 0b 63 26 4d 44 9a 6d 38 e2 2a 5f da 43 08 46 74
+ 80 30 53 0e f0 46 1c 8c a9 d9 ef bf ae 8e a6 d1 d0 3e 2b d1 93
+ ef f0 ab 9a 80 02 c4 74 28 a6 d3 5a 8d 88 d7 9f 7f 1e 3f 02 03
+ 01 00 01 a3 1a 30 18 30 09 06 03 55 1d 13 04 02 30 00 30 0b 06
+ 03 55 1d 0f 04 04 03 02 05 a0 30 0d 06 09 2a 86 48 86 f7 0d 01
+ 01 0b 05 00 03 81 81 00 85 aa d2 a0 e5 b9 27 6b 90 8c 65 f7 3a
+ 72 67 17 06 18 a5 4c 5f 8a 7b 33 7d 2d f7 a5 94 36 54 17 f2 ea
+ e8 f8 a5 8c 8f 81 72 f9 31 9c f3 6b 7f d6 c5 5b 80 f2 1a 03 01
+ 51 56 72 60 96 fd 33 5e 5e 67 f2 db f1 02 70 2e 60 8c ca e6 be
+ c1 fc 63 a4 2a 99 be 5c 3e b7 10 7c 3c 54 e9 b9 eb 2b d5 20 3b
+ 1c 3b 84 e0 a8 b2 f7 59 40 9b a3 ea c9 d9 1d 40 2d cc 0c c8 f8
+ 96 12 29 ac 91 87 b4 2b 4d e1 00 00"),
+
+ CertificateVerify =
+ hexstr2bin("0f 00 00 84 08 04 00 80 5a 74 7c
+ 5d 88 fa 9b d2 e5 5a b0 85 a6 10 15 b7 21 1f 82 4c d4 84 14 5a
+ b3 ff 52 f1 fd a8 47 7b 0b 7a bc 90 db 78 e2 d3 3a 5c 14 1a 07
+ 86 53 fa 6b ef 78 0c 5e a2 48 ee aa a7 85 c4 f3 94 ca b6 d3 0b
+ be 8d 48 59 ee 51 1f 60 29 57 b1 54 11 ac 02 76 71 45 9e 46 44
+ 5c 9e a5 8c 18 1e 81 8e 95 b8 c3 fb 0b f3 27 84 09 d3 be 15 2a
+ 3d a5 04 3e 06 3d da 65 cd f5 ae a2 0d 53 df ac d4 2f 74 f3"),
+
+ %% {server} calculate finished "tls13 finished":
+ %%
+ %% PRK (32 octets): b6 7b 7d 69 0c c1 6c 4e 75 e5 42 13 cb 2d 37 b4
+ %% e9 c9 12 bc de d9 10 5d 42 be fd 59 d3 91 ad 38
+ %%
+ %% hash (0 octets): (empty)
+ %%
+ %% info (18 octets): 00 20 0e 74 6c 73 31 33 20 66 69 6e 69 73 68 65
+ %% 64 00
+ %%
+ %% expanded (32 octets): 00 8d 3b 66 f8 16 ea 55 9f 96 b5 37 e8 85
+ %% c3 1f c0 68 bf 49 2c 65 2f 01 f2 88 a1 d8 cd c1 9f c8
+ %%
+ %% finished (32 octets): 9b 9b 14 1d 90 63 37 fb d2 cb dc e7 1d f4
+ %% de da 4a b4 2c 30 95 72 cb 7f ff ee 54 54 b7 8f 07 18
+
+ %% PRK = SHSTrafficSecret
+ FInfo =
+ hexstr2bin("00 20 0e 74 6c 73 31 33 20 66 69 6e 69 73 68 65
+ 64 00"),
+
+ FExpanded =
+ hexstr2bin("00 8d 3b 66 f8 16 ea 55 9f 96 b5 37 e8 85
+ c3 1f c0 68 bf 49 2c 65 2f 01 f2 88 a1 d8 cd c1 9f c8"),
+
+ FinishedVerifyData =
+ hexstr2bin("9b 9b 14 1d 90 63 37 fb d2 cb dc e7 1d f4
+ de da 4a b4 2c 30 95 72 cb 7f ff ee 54 54 b7 8f 07 18"),
+
+ FInfo = tls_v1:create_info(<<"finished">>, <<>>, ssl_cipher:hash_size(HKDFAlgo)),
+
+ FExpanded = tls_v1:finished_key(SHSTrafficSecret, HKDFAlgo),
+
+ MessageHistory0 = [CertificateVerify,
+ Certificate,
+ EncryptedExtensions,
+ ServerHello,
+ ClientHello],
+
+ FinishedVerifyData = tls_v1:finished_verify_data(FExpanded, HKDFAlgo, MessageHistory0),
+
+ %% {server} construct a Finished handshake message:
+ %%
+ %% Finished (36 octets): 14 00 00 20 9b 9b 14 1d 90 63 37 fb d2 cb
+ %% dc e7 1d f4 de da 4a b4 2c 30 95 72 cb 7f ff ee 54 54 b7 8f 07
+ %% 18
+ FinishedHSBin =
+ hexstr2bin("14 00 00 20 9b 9b 14 1d 90 63 37 fb d2 cb
+ dc e7 1d f4 de da 4a b4 2c 30 95 72 cb 7f ff ee 54 54 b7 8f 07
+ 18"),
+
+ FinishedHS = #finished{verify_data = FinishedVerifyData},
+
+ FinishedIOList = tls_handshake:encode_handshake(FinishedHS, {3,4}),
+ FinishedHSBin = iolist_to_binary(FinishedIOList),
+
+ %% {server} derive secret "tls13 c ap traffic":
+ %%
+ %% PRK (32 octets): 18 df 06 84 3d 13 a0 8b f2 a4 49 84 4c 5f 8a 47
+ %% 80 01 bc 4d 4c 62 79 84 d5 a4 1d a8 d0 40 29 19
+ %%
+ %% hash (32 octets): 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a
+ %% 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13
+ %%
+ %% info (54 octets): 00 20 12 74 6c 73 31 33 20 63 20 61 70 20 74 72
+ %% 61 66 66 69 63 20 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b
+ %% 1a 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13
+ %%
+ %% expanded (32 octets): 9e 40 64 6c e7 9a 7f 9d c0 5a f8 88 9b ce
+ %% 65 52 87 5a fa 0b 06 df 00 87 f7 92 eb b7 c1 75 04 a5
+
+ %% PRK = MasterSecret
+ CAPTHash =
+ hexstr2bin("96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a
+ 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13"),
+ CAPTInfo =
+ hexstr2bin("00 20 12 74 6c 73 31 33 20 63 20 61 70 20 74 72
+ 61 66 66 69 63 20 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b
+ 1a 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13"),
+
+ CAPTrafficSecret =
+ hexstr2bin("9e 40 64 6c e7 9a 7f 9d c0 5a f8 88 9b ce
+ 65 52 87 5a fa 0b 06 df 00 87 f7 92 eb b7 c1 75 04 a5"),
+
+ CHSF = <<ClientHello/binary,
+ ServerHello/binary,
+ EncryptedExtensions/binary,
+ Certificate/binary,
+ CertificateVerify/binary,
+ FinishedHSBin/binary>>,
+
+ CAPTHash = crypto:hash(HKDFAlgo, CHSF),
+
+ CAPTInfo =
+ tls_v1:create_info(<<"c ap traffic">>, CAPTHash, ssl_cipher:hash_size(HKDFAlgo)),
+
+ CAPTrafficSecret =
+ tls_v1:client_application_traffic_secret_0(HKDFAlgo, {master_secret, MasterSecret}, CHSF),
+
+ %% {server} derive secret "tls13 s ap traffic":
+ %%
+ %% PRK (32 octets): 18 df 06 84 3d 13 a0 8b f2 a4 49 84 4c 5f 8a 47
+ %% 80 01 bc 4d 4c 62 79 84 d5 a4 1d a8 d0 40 29 19
+ %%
+ %% hash (32 octets): 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a
+ %% 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13
+ %%
+ %% info (54 octets): 00 20 12 74 6c 73 31 33 20 73 20 61 70 20 74 72
+ %% 61 66 66 69 63 20 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b
+ %% 1a 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13
+ %%
+ %% expanded (32 octets): a1 1a f9 f0 55 31 f8 56 ad 47 11 6b 45 a9
+ %% 50 32 82 04 b4 f4 4b fb 6b 3a 4b 4f 1f 3f cb 63 16 43
+
+ %% PRK = MasterSecret
+ %% hash = CAPTHash
+ SAPTInfo =
+ hexstr2bin(" 00 20 12 74 6c 73 31 33 20 73 20 61 70 20 74 72
+ 61 66 66 69 63 20 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b
+ 1a 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13"),
+
+ SAPTrafficSecret =
+ hexstr2bin("a1 1a f9 f0 55 31 f8 56 ad 47 11 6b 45 a9
+ 50 32 82 04 b4 f4 4b fb 6b 3a 4b 4f 1f 3f cb 63 16 43"),
+
+ SAPTInfo =
+ tls_v1:create_info(<<"s ap traffic">>, CAPTHash, ssl_cipher:hash_size(HKDFAlgo)),
+
+ SAPTrafficSecret =
+ tls_v1:server_application_traffic_secret_0(HKDFAlgo, {master_secret, MasterSecret}, CHSF),
+
+ %% {server} derive secret "tls13 exp master":
+ %%
+ %% PRK (32 octets): 18 df 06 84 3d 13 a0 8b f2 a4 49 84 4c 5f 8a 47
+ %% 80 01 bc 4d 4c 62 79 84 d5 a4 1d a8 d0 40 29 19
+ %%
+ %% hash (32 octets): 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a
+ %% 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13
+ %%
+ %% info (52 octets): 00 20 10 74 6c 73 31 33 20 65 78 70 20 6d 61 73
+ %% 74 65 72 20 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a 00
+ %% 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13
+ %%
+ %% expanded (32 octets): fe 22 f8 81 17 6e da 18 eb 8f 44 52 9e 67
+ %% 92 c5 0c 9a 3f 89 45 2f 68 d8 ae 31 1b 43 09 d3 cf 50
+
+ %% PRK = MasterSecret
+ %% hash = CAPTHash
+ ExporterInfo =
+ hexstr2bin("00 20 10 74 6c 73 31 33 20 65 78 70 20 6d 61 73
+ 74 65 72 20 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a 00
+ 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13"),
+
+ ExporterMasterSecret =
+ hexstr2bin("fe 22 f8 81 17 6e da 18 eb 8f 44 52 9e 67
+ 92 c5 0c 9a 3f 89 45 2f 68 d8 ae 31 1b 43 09 d3 cf 50"),
+
+ ExporterInfo =
+ tls_v1:create_info(<<"exp master">>, CAPTHash, ssl_cipher:hash_size(HKDFAlgo)),
+
+ ExporterMasterSecret =
+ tls_v1:exporter_master_secret(HKDFAlgo, {master_secret, MasterSecret}, CHSF),
+
+ %% {server} derive write traffic keys for application data:
+ %%
+ %% PRK (32 octets): a1 1a f9 f0 55 31 f8 56 ad 47 11 6b 45 a9 50 32
+ %% 82 04 b4 f4 4b fb 6b 3a 4b 4f 1f 3f cb 63 16 43
+ %%
+ %% key info (13 octets): 00 10 09 74 6c 73 31 33 20 6b 65 79 00
+ %%
+ %% key expanded (16 octets): 9f 02 28 3b 6c 9c 07 ef c2 6b b9 f2 ac
+ %% 92 e3 56
+ %%
+ %% iv info (12 octets): 00 0c 08 74 6c 73 31 33 20 69 76 00
+ %%
+ %% iv expanded (12 octets): cf 78 2b 88 dd 83 54 9a ad f1 e9 84
+
+ %% PRK = SAPTrafficsecret
+ %% key info = WriteKeyInfo
+ %% iv info = WrtieIVInfo
+ SWKey =
+ hexstr2bin("9f 02 28 3b 6c 9c 07 ef c2 6b b9 f2 ac 92 e3 56"),
+
+ SWIV =
+ hexstr2bin("cf 78 2b 88 dd 83 54 9a ad f1 e9 84"),
+
+ {SWKey, SWIV} = tls_v1:calculate_traffic_keys(HKDFAlgo, Cipher, SAPTrafficSecret),
+
+ %% {server} derive read traffic keys for handshake data:
+ %%
+ %% PRK (32 octets): b3 ed db 12 6e 06 7f 35 a7 80 b3 ab f4 5e 2d 8f
+ %% 3b 1a 95 07 38 f5 2e 96 00 74 6a 0e 27 a5 5a 21
+ %%
+ %% key info (13 octets): 00 10 09 74 6c 73 31 33 20 6b 65 79 00
+ %%
+ %% key expanded (16 octets): db fa a6 93 d1 76 2c 5b 66 6a f5 d9 50
+ %% 25 8d 01
+ %%
+ %% iv info (12 octets): 00 0c 08 74 6c 73 31 33 20 69 76 00
+ %%
+ %% iv expanded (12 octets): 5b d3 c7 1b 83 6e 0b 76 bb 73 26 5f
+
+ %% PRK = CHSTrafficsecret
+ %% key info = WriteKeyInfo
+ %% iv info = WrtieIVInfo
+ SRKey =
+ hexstr2bin("db fa a6 93 d1 76 2c 5b 66 6a f5 d9 50 25 8d 01"),
+
+ SRIV =
+ hexstr2bin("5b d3 c7 1b 83 6e 0b 76 bb 73 26 5f"),
+
+ {SRKey, SRIV} = tls_v1:calculate_traffic_keys(HKDFAlgo, Cipher, CHSTrafficSecret).
+
+%%--------------------------------------------------------------------
+finished_verify_data() ->
+ [{doc,"Test TLS 1.3 Finished message handling"}].
+
+finished_verify_data(_Config) ->
+ ClientHello =
+ hexstr2bin("01 00 00 c6 03 03 00 01 02 03 04 05 06 07 08 09
+ 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19
+ 1a 1b 1c 1d 1e 1f 20 e0 e1 e2 e3 e4 e5 e6 e7 e8
+ e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8
+ f9 fa fb fc fd fe ff 00 06 13 01 13 02 13 03 01
+ 00 00 77 00 00 00 18 00 16 00 00 13 65 78 61 6d
+ 70 6c 65 2e 75 6c 66 68 65 69 6d 2e 6e 65 74 00
+ 0a 00 08 00 06 00 1d 00 17 00 18 00 0d 00 14 00
+ 12 04 03 08 04 04 01 05 03 08 05 05 01 08 06 06
+ 01 02 01 00 33 00 26 00 24 00 1d 00 20 35 80 72
+ d6 36 58 80 d1 ae ea 32 9a df 91 21 38 38 51 ed
+ 21 a2 8e 3b 75 e9 65 d0 d2 cd 16 62 54 00 2d 00
+ 02 01 01 00 2b 00 03 02 03 04"),
+
+ ServerHello =
+ hexstr2bin("02 00 00 76 03 03 70 71 72 73 74 75 76 77 78 79
+ 7a 7b 7c 7d 7e 7f 80 81 82 83 84 85 86 87 88 89
+ 8a 8b 8c 8d 8e 8f 20 e0 e1 e2 e3 e4 e5 e6 e7 e8
+ e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8
+ f9 fa fb fc fd fe ff 13 01 00 00 2e 00 33 00 24
+ 00 1d 00 20 9f d7 ad 6d cf f4 29 8d d3 f9 6d 5b
+ 1b 2a f9 10 a0 53 5b 14 88 d7 f8 fa bb 34 9a 98
+ 28 80 b6 15 00 2b 00 02 03 04"),
+
+ EncryptedExtensions =
+ hexstr2bin("08 00 00 02 00 00"),
+
+ Certificate =
+ hexstr2bin("0b 00 03 2e 00 00 03 2a 00 03 25 30 82 03 21 30
+ 82 02 09 a0 03 02 01 02 02 08 15 5a 92 ad c2 04
+ 8f 90 30 0d 06 09 2a 86 48 86 f7 0d 01 01 0b 05
+ 00 30 22 31 0b 30 09 06 03 55 04 06 13 02 55 53
+ 31 13 30 11 06 03 55 04 0a 13 0a 45 78 61 6d 70
+ 6c 65 20 43 41 30 1e 17 0d 31 38 31 30 30 35 30
+ 31 33 38 31 37 5a 17 0d 31 39 31 30 30 35 30 31
+ 33 38 31 37 5a 30 2b 31 0b 30 09 06 03 55 04 06
+ 13 02 55 53 31 1c 30 1a 06 03 55 04 03 13 13 65
+ 78 61 6d 70 6c 65 2e 75 6c 66 68 65 69 6d 2e 6e
+ 65 74 30 82 01 22 30 0d 06 09 2a 86 48 86 f7 0d
+ 01 01 01 05 00 03 82 01 0f 00 30 82 01 0a 02 82
+ 01 01 00 c4 80 36 06 ba e7 47 6b 08 94 04 ec a7
+ b6 91 04 3f f7 92 bc 19 ee fb 7d 74 d7 a8 0d 00
+ 1e 7b 4b 3a 4a e6 0f e8 c0 71 fc 73 e7 02 4c 0d
+ bc f4 bd d1 1d 39 6b ba 70 46 4a 13 e9 4a f8 3d
+ f3 e1 09 59 54 7b c9 55 fb 41 2d a3 76 52 11 e1
+ f3 dc 77 6c aa 53 37 6e ca 3a ec be c3 aa b7 3b
+ 31 d5 6c b6 52 9c 80 98 bc c9 e0 28 18 e2 0b f7
+ f8 a0 3a fd 17 04 50 9e ce 79 bd 9f 39 f1 ea 69
+ ec 47 97 2e 83 0f b5 ca 95 de 95 a1 e6 04 22 d5
+ ee be 52 79 54 a1 e7 bf 8a 86 f6 46 6d 0d 9f 16
+ 95 1a 4c f7 a0 46 92 59 5c 13 52 f2 54 9e 5a fb
+ 4e bf d7 7a 37 95 01 44 e4 c0 26 87 4c 65 3e 40
+ 7d 7d 23 07 44 01 f4 84 ff d0 8f 7a 1f a0 52 10
+ d1 f4 f0 d5 ce 79 70 29 32 e2 ca be 70 1f df ad
+ 6b 4b b7 11 01 f4 4b ad 66 6a 11 13 0f e2 ee 82
+ 9e 4d 02 9d c9 1c dd 67 16 db b9 06 18 86 ed c1
+ ba 94 21 02 03 01 00 01 a3 52 30 50 30 0e 06 03
+ 55 1d 0f 01 01 ff 04 04 03 02 05 a0 30 1d 06 03
+ 55 1d 25 04 16 30 14 06 08 2b 06 01 05 05 07 03
+ 02 06 08 2b 06 01 05 05 07 03 01 30 1f 06 03 55
+ 1d 23 04 18 30 16 80 14 89 4f de 5b cc 69 e2 52
+ cf 3e a3 00 df b1 97 b8 1d e1 c1 46 30 0d 06 09
+ 2a 86 48 86 f7 0d 01 01 0b 05 00 03 82 01 01 00
+ 59 16 45 a6 9a 2e 37 79 e4 f6 dd 27 1a ba 1c 0b
+ fd 6c d7 55 99 b5 e7 c3 6e 53 3e ff 36 59 08 43
+ 24 c9 e7 a5 04 07 9d 39 e0 d4 29 87 ff e3 eb dd
+ 09 c1 cf 1d 91 44 55 87 0b 57 1d d1 9b df 1d 24
+ f8 bb 9a 11 fe 80 fd 59 2b a0 39 8c de 11 e2 65
+ 1e 61 8c e5 98 fa 96 e5 37 2e ef 3d 24 8a fd e1
+ 74 63 eb bf ab b8 e4 d1 ab 50 2a 54 ec 00 64 e9
+ 2f 78 19 66 0d 3f 27 cf 20 9e 66 7f ce 5a e2 e4
+ ac 99 c7 c9 38 18 f8 b2 51 07 22 df ed 97 f3 2e
+ 3e 93 49 d4 c6 6c 9e a6 39 6d 74 44 62 a0 6b 42
+ c6 d5 ba 68 8e ac 3a 01 7b dd fc 8e 2c fc ad 27
+ cb 69 d3 cc dc a2 80 41 44 65 d3 ae 34 8c e0 f3
+ 4a b2 fb 9c 61 83 71 31 2b 19 10 41 64 1c 23 7f
+ 11 a5 d6 5c 84 4f 04 04 84 99 38 71 2b 95 9e d6
+ 85 bc 5c 5d d6 45 ed 19 90 94 73 40 29 26 dc b4
+ 0e 34 69 a1 59 41 e8 e2 cc a8 4b b6 08 46 36 a0
+ 00 00"),
+
+ CertificateVerify =
+ hexstr2bin("0f 00 01 04 08 04 01 00 17 fe b5 33 ca 6d 00 7d
+ 00 58 25 79 68 42 4b bc 3a a6 90 9e 9d 49 55 75
+ 76 a5 20 e0 4a 5e f0 5f 0e 86 d2 4f f4 3f 8e b8
+ 61 ee f5 95 22 8d 70 32 aa 36 0f 71 4e 66 74 13
+ 92 6e f4 f8 b5 80 3b 69 e3 55 19 e3 b2 3f 43 73
+ df ac 67 87 06 6d cb 47 56 b5 45 60 e0 88 6e 9b
+ 96 2c 4a d2 8d ab 26 ba d1 ab c2 59 16 b0 9a f2
+ 86 53 7f 68 4f 80 8a ef ee 73 04 6c b7 df 0a 84
+ fb b5 96 7a ca 13 1f 4b 1c f3 89 79 94 03 a3 0c
+ 02 d2 9c bd ad b7 25 12 db 9c ec 2e 5e 1d 00 e5
+ 0c af cf 6f 21 09 1e bc 4f 25 3c 5e ab 01 a6 79
+ ba ea be ed b9 c9 61 8f 66 00 6b 82 44 d6 62 2a
+ aa 56 88 7c cf c6 6a 0f 38 51 df a1 3a 78 cf f7
+ 99 1e 03 cb 2c 3a 0e d8 7d 73 67 36 2e b7 80 5b
+ 00 b2 52 4f f2 98 a4 da 48 7c ac de af 8a 23 36
+ c5 63 1b 3e fa 93 5b b4 11 e7 53 ca 13 b0 15 fe
+ c7 e4 a7 30 f1 36 9f 9e"),
+
+ BaseKey =
+ hexstr2bin("a2 06 72 65 e7 f0 65 2a 92 3d 5d 72 ab 04 67 c4
+ 61 32 ee b9 68 b6 a3 2d 31 1c 80 58 68 54 88 14"),
+
+ VerifyData =
+ hexstr2bin("ea 6e e1 76 dc cc 4a f1 85 9e 9e 4e 93 f7 97 ea
+ c9 a7 8c e4 39 30 1e 35 27 5a d4 3f 3c dd bd e3"),
+
+ Messages = [CertificateVerify,
+ Certificate,
+ EncryptedExtensions,
+ ServerHello,
+ ClientHello],
+
+ FinishedKey = tls_v1:finished_key(BaseKey, sha256),
+ VerifyData = tls_v1:finished_verify_data(FinishedKey, sha256, Messages).
+
+%%--------------------------------------------------------------------
+%% Internal functions ------------------------------------------------
+%%--------------------------------------------------------------------
+
+hexstr2int(S) ->
+ B = hexstr2bin(S),
+ Bits = size(B) * 8,
+ <<Integer:Bits/integer>> = B,
+ Integer.
+
+hexstr2bin(S) when is_binary(S) ->
+ hexstr2bin(S, <<>>);
+hexstr2bin(S) ->
+ hexstr2bin(list_to_binary(S), <<>>).
+%%
+hexstr2bin(<<>>, Acc) ->
+ Acc;
+hexstr2bin(<<C,T/binary>>, Acc) when C =:= 32; %% SPACE
+ C =:= 10; %% LF
+ C =:= 13 -> %% CR
+ hexstr2bin(T, Acc);
+hexstr2bin(<<X,Y,T/binary>>, Acc) ->
+ I = hex2int(X) * 16 + hex2int(Y),
+ hexstr2bin(T, <<Acc/binary,I>>).
+
+hex2int(C) when $0 =< C, C =< $9 ->
+ C - $0;
+hex2int(C) when $A =< C, C =< $F ->
+ C - $A + 10;
+hex2int(C) when $a =< C, C =< $f ->
+ C - $a + 10.
diff --git a/lib/ssl/test/tls_1_3_version_SUITE.erl b/lib/ssl/test/tls_1_3_version_SUITE.erl
new file mode 100644
index 0000000000..f0b224d4e5
--- /dev/null
+++ b/lib/ssl/test/tls_1_3_version_SUITE.erl
@@ -0,0 +1,153 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2019-2019. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+
+-module(tls_1_3_version_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("public_key/include/public_key.hrl").
+
+%%--------------------------------------------------------------------
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
+all() ->
+ [
+ {group, 'tlsv1.3'}
+ ].
+
+groups() ->
+ [
+ {'tlsv1.3', [], cert_groups()},
+ {rsa, [], tests()},
+ {ecdsa, [], tests()}
+ ].
+
+cert_groups() ->
+ [{group, rsa},
+ {group, ecdsa}].
+
+tests() ->
+ [tls13_client_tls12_server,
+ tls13_client_with_ext_tls12_server,
+ tls12_client_tls13_server].
+
+init_per_suite(Config) ->
+ catch crypto:stop(),
+ try crypto:start() of
+ ok ->
+ ssl_test_lib:clean_start(),
+ [{client_type, erlang}, {server_type, erlang} |
+ Config]
+ catch _:_ ->
+ {skip, "Crypto did not start"}
+ end.
+
+end_per_suite(_Config) ->
+ ssl:stop(),
+ application:stop(crypto).
+
+init_per_group(rsa, Config0) ->
+ Config = ssl_test_lib:make_rsa_cert(Config0),
+ COpts = proplists:get_value(client_rsa_opts, Config),
+ SOpts = proplists:get_value(server_rsa_opts, Config),
+ [{client_cert_opts, COpts}, {server_cert_opts, SOpts} |
+ lists:delete(server_cert_opts, lists:delete(client_cert_opts, Config))];
+init_per_group(ecdsa, Config0) ->
+ PKAlg = crypto:supports(public_keys),
+ case lists:member(ecdsa, PKAlg) andalso
+ (lists:member(ecdh, PKAlg) orelse lists:member(dh, PKAlg)) of
+ true ->
+ Config = ssl_test_lib:make_ecdsa_cert(Config0),
+ COpts = proplists:get_value(client_ecdsa_opts, Config),
+ SOpts = proplists:get_value(server_ecdsa_opts, Config),
+ [{client_cert_opts, COpts}, {server_cert_opts, SOpts} |
+ lists:delete(server_cert_opts, lists:delete(client_cert_opts, Config))];
+ false ->
+ {skip, "Missing EC crypto support"}
+ end;
+init_per_group(GroupName, Config) ->
+ ssl_test_lib:clean_tls_version(Config),
+ case ssl_test_lib:is_tls_version(GroupName) andalso
+ ssl_test_lib:sufficient_crypto_support(GroupName) of
+ true ->
+ ssl_test_lib:init_tls_version(GroupName, Config);
+ _ ->
+ case ssl_test_lib:sufficient_crypto_support(GroupName) of
+ true ->
+ ssl:start(),
+ Config;
+ false ->
+ {skip, "Missing crypto support"}
+ end
+ end.
+
+end_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ ssl_test_lib:clean_tls_version(Config);
+ false ->
+ Config
+ end.
+
+%%--------------------------------------------------------------------
+%% Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
+
+tls13_client_tls12_server() ->
+ [{doc,"Test that a TLS 1.3 client can connect to a TLS 1.2 server."}].
+
+tls13_client_tls12_server(Config) when is_list(Config) ->
+ ClientOpts = [{versions,
+ ['tlsv1.3', 'tlsv1.2']} | ssl_test_lib:ssl_options(client_cert_opts, Config)],
+ ServerOpts = [{versions,
+ ['tlsv1.1', 'tlsv1.2']} | ssl_test_lib:ssl_options(server_cert_opts, Config)],
+ ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config).
+
+tls13_client_with_ext_tls12_server() ->
+ [{doc,"Test basic connection between TLS 1.2 server and TLS 1.3 client when "
+ "client has TLS 1.3 specsific extensions"}].
+
+tls13_client_with_ext_tls12_server(Config) ->
+ ClientOpts0 = ssl_test_lib:ssl_options(client_cert_opts, Config),
+ ServerOpts0 = ssl_test_lib:ssl_options(server_cert_opts, Config),
+
+ ServerOpts = [{versions, ['tlsv1.2']}|ServerOpts0],
+ ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']},
+ {signature_algs_cert, [ecdsa_secp384r1_sha384,
+ ecdsa_secp256r1_sha256,
+ rsa_pss_rsae_sha256,
+ rsa_pkcs1_sha256,
+ {sha256,rsa},{sha256,dsa}]}|ClientOpts0],
+ ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config).
+
+tls12_client_tls13_server() ->
+ [{doc,"Test that a TLS 1.2 client can connect to a TLS 1.3 server."}].
+
+tls12_client_tls13_server(Config) when is_list(Config) ->
+ ClientOpts = [{versions,
+ ['tlsv1.1', 'tlsv1.2']} | ssl_test_lib:ssl_options(client_cert_opts, Config)],
+ ServerOpts = [{versions,
+ ['tlsv1.3', 'tlsv1.2']} | ssl_test_lib:ssl_options(server_cert_opts, Config)],
+ ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config).
+
diff --git a/lib/ssl/test/tls_api_SUITE.erl b/lib/ssl/test/tls_api_SUITE.erl
new file mode 100644
index 0000000000..7239d4cb90
--- /dev/null
+++ b/lib/ssl/test/tls_api_SUITE.erl
@@ -0,0 +1,880 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2019-2019. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(tls_api_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+-include_lib("common_test/include/ct.hrl").
+-include_lib("ssl/src/ssl_record.hrl").
+-include_lib("ssl/src/ssl_internal.hrl").
+-include_lib("ssl/src/ssl_api.hrl").
+-include_lib("ssl/src/tls_handshake.hrl").
+
+-define(SLEEP, 500).
+
+%%--------------------------------------------------------------------
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
+
+all() ->
+ [
+ {group, 'tlsv1.3'},
+ {group, 'tlsv1.2'},
+ {group, 'tlsv1.1'},
+ {group, 'tlsv1'},
+ {group, 'sslv3'}
+ ].
+
+groups() ->
+ [
+ {'tlsv1.3', [], api_tests() -- [sockname]},
+ {'tlsv1.2', [], api_tests()},
+ {'tlsv1.1', [], api_tests()},
+ {'tlsv1', [], api_tests()},
+ {'sslv3', [], api_tests() ++ [ssl3_cipher_suite_limitation]}
+ ].
+
+api_tests() ->
+ [
+ tls_upgrade,
+ tls_upgrade_with_timeout,
+ tls_downgrade,
+ tls_shutdown,
+ tls_shutdown_write,
+ tls_shutdown_both,
+ tls_shutdown_error,
+ tls_client_closes_socket,
+ tls_closed_in_active_once,
+ tls_tcp_msg,
+ tls_tcp_msg_big,
+ tls_dont_crash_on_handshake_garbage,
+ tls_tcp_error_propagation_in_active_mode,
+ peername,
+ sockname,
+ tls_server_handshake_timeout,
+ transport_close,
+ emulated_options,
+ accept_pool,
+ reuseaddr
+ ].
+
+init_per_suite(Config0) ->
+ catch crypto:stop(),
+ try crypto:start() of
+ ok ->
+ ssl_test_lib:clean_start(),
+ ssl_test_lib:make_rsa_cert(Config0)
+ catch _:_ ->
+ {skip, "Crypto did not start"}
+ end.
+
+end_per_suite(_Config) ->
+ ssl:stop(),
+ application:unload(ssl),
+ application:stop(crypto).
+
+
+init_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ case ssl_test_lib:sufficient_crypto_support(GroupName) of
+ true ->
+ ssl_test_lib:init_tls_version(GroupName, Config);
+ false ->
+ {skip, "Missing crypto support"}
+ end;
+ _ ->
+ ssl:start(),
+ Config
+ end.
+
+end_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ ssl_test_lib:clean_tls_version(Config);
+ false ->
+ Config
+ end.
+
+init_per_testcase(_TestCase, Config) ->
+ ssl_test_lib:ct_log_supported_protocol_versions(Config),
+ ct:timetrap({seconds, 10}),
+ Config.
+
+end_per_testcase(_TestCase, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
+tls_upgrade() ->
+ [{doc,"Test that you can upgrade an tcp connection to an ssl connection"}].
+
+tls_upgrade(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ TcpOpts = [binary, {reuseaddr, true}],
+
+ Server = ssl_test_lib:start_upgrade_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE,
+ upgrade_result, []}},
+ {tcp_options,
+ [{active, false} | TcpOpts]},
+ {ssl_options, [{verify, verify_peer} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_upgrade_client([{node, ClientNode},
+ {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, upgrade_result, []}},
+ {tcp_options, [binary]},
+ {ssl_options, [{verify, verify_peer},
+ {server_name_indication, Hostname} | ClientOpts]}]),
+
+ ct:log("Testcase ~p, Client ~p Server ~p ~n",
+ [self(), Client, Server]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+%%--------------------------------------------------------------------
+tls_upgrade_with_timeout() ->
+ [{doc,"Test ssl_accept/3"}].
+
+tls_upgrade_with_timeout(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ TcpOpts = [binary, {reuseaddr, true}],
+
+ Server = ssl_test_lib:start_upgrade_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {timeout, 5000},
+ {mfa, {?MODULE,
+ upgrade_result, []}},
+ {tcp_options,
+ [{active, false} | TcpOpts]},
+ {ssl_options, [{verify, verify_peer} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_upgrade_client([{node, ClientNode},
+ {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, upgrade_result, []}},
+ {tcp_options, TcpOpts},
+ {ssl_options, [{verify, verify_peer},
+ {server_name_indication, Hostname} | ClientOpts]}]),
+
+ ct:log("Testcase ~p, Client ~p Server ~p ~n",
+ [self(), Client, Server]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+tls_downgrade() ->
+ [{doc,"Test that you can downgarde an ssl connection to an tcp connection"}].
+tls_downgrade(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, tls_downgrade_result, [self()]}},
+ {options, [{active, false}, {verify, verify_peer} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, tls_downgrade_result, [self()]}},
+ {options, [{active, false}, {verify, verify_peer} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ready, Client, ready),
+
+ Server ! go,
+ Client ! go,
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+
+%%--------------------------------------------------------------------
+tls_shutdown() ->
+ [{doc,"Test API function ssl:shutdown/2"}].
+tls_shutdown(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, tls_shutdown_result, [server]}},
+ {options, [{exit_on_close, false},
+ {active, false} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa,
+ {?MODULE, tls_shutdown_result, [client]}},
+ {options,
+ [{exit_on_close, false},
+ {active, false} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+tls_shutdown_write() ->
+ [{doc,"Test API function ssl:shutdown/2 with option write."}].
+tls_shutdown_write(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, tls_shutdown_write_result, [server]}},
+ {options, [{active, false} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, tls_shutdown_write_result, [client]}},
+ {options, [{active, false} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, {error, closed}).
+
+%%--------------------------------------------------------------------
+tls_shutdown_both() ->
+ [{doc,"Test API function ssl:shutdown/2 with option both."}].
+tls_shutdown_both(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, tls_shutdown_both_result, [server]}},
+ {options, [{active, false} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, tls_shutdown_both_result, [client]}},
+ {options, [{active, false} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, {error, closed}).
+
+%%--------------------------------------------------------------------
+tls_shutdown_error() ->
+ [{doc,"Test ssl:shutdown/2 error handling"}].
+tls_shutdown_error(Config) when is_list(Config) ->
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ Port = ssl_test_lib:inet_port(node()),
+ {ok, Listen} = ssl:listen(Port, ServerOpts),
+ {error, enotconn} = ssl:shutdown(Listen, read_write),
+ ok = ssl:close(Listen),
+ {error, closed} = ssl:shutdown(Listen, read_write).
+%%--------------------------------------------------------------------
+tls_client_closes_socket() ->
+ [{doc,"Test what happens when client closes socket before handshake is compleated"}].
+
+tls_client_closes_socket(Config) when is_list(Config) ->
+ ServerOpts = ssl_test_lib:ssl_options(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ TcpOpts = [binary, {reuseaddr, true}],
+
+ Server = ssl_test_lib:start_upgrade_server_error([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {tcp_options, TcpOpts},
+ {ssl_options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ Connect = fun() ->
+ {ok, _Socket} = rpc:call(ClientNode, gen_tcp, connect,
+ [Hostname, Port, [binary]]),
+ %% Make sure that ssl_accept is called before
+ %% client process ends and closes socket.
+ ct:sleep(?SLEEP)
+ end,
+
+ _Client = spawn_link(Connect),
+
+ ssl_test_lib:check_result(Server, {error,closed}).
+
+%%--------------------------------------------------------------------
+tls_closed_in_active_once() ->
+ [{doc, "Test that ssl_closed is delivered in active once with non-empty buffer, check ERL-420."}].
+
+tls_closed_in_active_once(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ {_ClientNode, _ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ TcpOpts = [binary, {reuseaddr, true}],
+ Port = ssl_test_lib:inet_port(node()),
+ Server = fun() ->
+ {ok, Listen} = gen_tcp:listen(Port, TcpOpts),
+ {ok, TcpServerSocket} = gen_tcp:accept(Listen),
+ {ok, ServerSocket} = ssl:handshake(TcpServerSocket, ServerOpts),
+ lists:foreach(
+ fun(_) ->
+ ssl:send(ServerSocket, "some random message\r\n")
+ end, lists:seq(1, 20)),
+ %% Close TCP instead of SSL socket to trigger the bug:
+ gen_tcp:close(TcpServerSocket),
+ gen_tcp:close(Listen)
+ end,
+ spawn_link(Server),
+ {ok, Socket} = ssl:connect(Hostname, Port, [{active, false} | ClientOpts]),
+ Result = tls_closed_in_active_once_loop(Socket),
+ ssl:close(Socket),
+ case Result of
+ ok -> ok;
+ _ -> ct:fail(Result)
+ end.
+%%--------------------------------------------------------------------
+tls_tcp_msg() ->
+ [{doc,"Test what happens when a tcp tries to connect, i,e. a bad (ssl) packet is sent first"}].
+
+tls_tcp_msg(Config) when is_list(Config) ->
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ TcpOpts = [binary, {reuseaddr, true}, {active, false}],
+
+ Server = ssl_test_lib:start_upgrade_server_error([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {timeout, 5000},
+ {mfa, {?MODULE, dummy, []}},
+ {tcp_options, TcpOpts},
+ {ssl_options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ {ok, Socket} = gen_tcp:connect(Hostname, Port, [binary, {packet, 0}]),
+ ct:log("Testcase ~p connected to Server ~p ~n", [self(), Server]),
+ gen_tcp:send(Socket, "<SOME GARBLED NON SSL MESSAGE>"),
+
+ receive
+ {tcp_closed, Socket} ->
+ receive
+ {Server, {error, Error}} ->
+ ct:log("Error ~p", [Error])
+ end
+ end.
+%%--------------------------------------------------------------------
+tls_tcp_msg_big() ->
+ [{doc,"Test what happens when a tcp tries to connect, i,e. a bad big (ssl) packet is sent first"}].
+
+tls_tcp_msg_big(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ TcpOpts = [binary, {reuseaddr, true}],
+
+ Rand = crypto:strong_rand_bytes(?MAX_CIPHER_TEXT_LENGTH+1),
+ Server = ssl_test_lib:start_upgrade_server_error([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {timeout, 5000},
+ {mfa, {?MODULE, dummy, []}},
+ {tcp_options, TcpOpts},
+ {ssl_options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ {ok, Socket} = gen_tcp:connect(Hostname, Port, [binary, {packet, 0}]),
+ ct:log("Testcase ~p connected to Server ~p ~n", [self(), Server]),
+
+ gen_tcp:send(Socket, <<?BYTE(0),
+ ?BYTE(3), ?BYTE(1), ?UINT16(?MAX_CIPHER_TEXT_LENGTH), Rand/binary>>),
+
+ receive
+ {tcp_closed, Socket} ->
+ receive
+ {Server, {error, timeout}} ->
+ ct:fail("hangs");
+ {Server, {error, Error}} ->
+ ct:log("Error ~p", [Error]);
+ {'EXIT', Server, _} ->
+ ok
+ end
+ end.
+
+%%--------------------------------------------------------------------
+tls_dont_crash_on_handshake_garbage() ->
+ [{doc, "Ensure SSL server worker thows an alert on garbage during handshake "
+ "instead of crashing and exposing state to user code"}].
+
+tls_dont_crash_on_handshake_garbage(Config) ->
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+
+ {_ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, ServerOpts}]),
+ unlink(Server), monitor(process, Server),
+ Port = ssl_test_lib:inet_port(Server),
+
+ {ok, Socket} = gen_tcp:connect(Hostname, Port, [binary, {active, false}]),
+
+ % Send hello and garbage record
+ ok = gen_tcp:send(Socket,
+ [<<22, 3,3, 49:16, 1, 45:24, 3,3, % client_hello
+ 16#deadbeef:256, % 32 'random' bytes = 256 bits
+ 0, 6:16, 0,255, 0,61, 0,57, 1, 0 >>, % some hello values
+
+ <<22, 3,3, 5:16, 92,64,37,228,209>> % garbage
+ ]),
+ % Send unexpected change_cipher_spec
+ ok = gen_tcp:send(Socket, <<20, 3,3, 12:16, 111,40,244,7,137,224,16,109,197,110,249,152>>),
+
+ % Ensure we receive an alert, not sudden disconnect
+ {ok, <<21, _/binary>>} = drop_handshakes(Socket, 1000).
+
+%%--------------------------------------------------------------------
+tls_tcp_error_propagation_in_active_mode() ->
+ [{doc,"Test that process recives {ssl_error, Socket, closed} when tcp error ocurres"}].
+tls_tcp_error_propagation_in_active_mode(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ {Client, #sslsocket{pid=[Pid|_]} = SslSocket} = ssl_test_lib:start_client([return_socket,
+ {node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, receive_msg, []}},
+ {options, ClientOpts}]),
+
+ {status, _, _, StatusInfo} = sys:get_status(Pid),
+ [_, _,_, _, Prop] = StatusInfo,
+ State = ssl_test_lib:state(Prop),
+ StaticEnv = element(2, State),
+ Socket = element(11, StaticEnv),
+ %% Fake tcp error
+ Pid ! {tcp_error, Socket, etimedout},
+
+ ssl_test_lib:check_result(Client, {ssl_closed, SslSocket}).
+
+%%--------------------------------------------------------------------
+peername() ->
+ [{doc,"Test API function peername/1"}].
+
+peername(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, peername_result, []}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, peername_result, []}},
+ {options, [{port, 0} | ClientOpts]}]),
+
+ ClientPort = ssl_test_lib:inet_port(Client),
+ ServerIp = ssl_test_lib:node_to_hostip(ServerNode, server),
+ ClientIp = ssl_test_lib:node_to_hostip(ClientNode, client),
+ ServerMsg = {ok, {ClientIp, ClientPort}},
+ ClientMsg = {ok, {ServerIp, Port}},
+
+ ct:log("Testcase ~p, Client ~p Server ~p ~n",
+ [self(), Client, Server]),
+
+ ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+sockname() ->
+ [{doc,"Test API function sockname/1"}].
+sockname(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, sockname_result, []}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, sockname_result, []}},
+ {options, [{port, 0} | ClientOpts]}]),
+
+ ClientPort = ssl_test_lib:inet_port(Client),
+ ServerIp = ssl_test_lib:node_to_hostip(ServerNode, server),
+ ClientIp = ssl_test_lib:node_to_hostip(ClientNode, client),
+ ServerMsg = {ok, {ServerIp, Port}},
+ ClientMsg = {ok, {ClientIp, ClientPort}},
+
+ ct:log("Testcase ~p, Client ~p Server ~p ~n",
+ [self(), Client, Server]),
+
+ ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+%%--------------------------------------------------------------------
+tls_server_handshake_timeout() ->
+ [{doc,"Test server handshake timeout"}].
+
+tls_server_handshake_timeout(Config) ->
+ process_flag(trap_exit, true),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {timeout, 5000},
+ {mfa, {ssl_test_lib,
+ no_result_msg, []}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ {ok, CSocket} = gen_tcp:connect(Hostname, Port, [binary, {active, true}]),
+
+ receive
+ {tcp_closed, CSocket} ->
+ ssl_test_lib:check_result(Server, {error, timeout}),
+ receive
+ {'EXIT', Server, _} ->
+ %% Make sure supervisor had time to react on process exit
+ %% Could we come up with a better solution to this?
+ ct:sleep(500),
+ [] = supervisor:which_children(tls_connection_sup)
+ end
+ end.
+transport_close() ->
+ [{doc, "Test what happens if socket is closed on TCP level after a while of normal operation"}].
+transport_close(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result, []}},
+ {options, [{active, false} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ {ok, TcpS} = rpc:call(ClientNode, gen_tcp, connect,
+ [Hostname,Port,[binary, {active, false}]]),
+ {ok, SslS} = rpc:call(ClientNode, ssl, connect,
+ [TcpS,[{active, false}|ClientOpts]]),
+
+ ct:log("Testcase ~p, Client ~p Server ~p ~n",
+ [self(), self(), Server]),
+ ok = ssl:send(SslS, "Hello world"),
+ {ok,<<"Hello world">>} = ssl:recv(SslS, 11),
+ gen_tcp:close(TcpS),
+ {error, _} = ssl:send(SslS, "Hello world").
+
+%%--------------------------------------------------------------------
+ssl3_cipher_suite_limitation() ->
+ [{doc,"Test a SSLv3 client cannot negotiate a TLSv* cipher suite."}].
+ssl3_cipher_suite_limitation(Config) when is_list(Config) ->
+
+ {_ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+
+ Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ {ok, Socket} = gen_tcp:connect(Hostname, Port, [binary, {active, false}]),
+ ok = gen_tcp:send(Socket,
+ <<22, 3,0, 49:16, % handshake, SSL 3.0, length
+ 1, 45:24, % client_hello, length
+ 3,0, % SSL 3.0
+ 16#deadbeef:256, % 32 'random' bytes = 256 bits
+ 0, % no session ID
+ %% three cipher suites -- null, one with sha256 hash and one with sha hash
+ 6:16, 0,255, 0,61, 0,57,
+ 1, 0 % no compression
+ >>),
+ {ok, <<22, RecMajor:8, RecMinor:8, _RecLen:16, 2, HelloLen:24>>} = gen_tcp:recv(Socket, 9, 10000),
+ {ok, <<HelloBin:HelloLen/binary>>} = gen_tcp:recv(Socket, HelloLen, 5000),
+ ServerHello = tls_handshake:decode_handshake({RecMajor, RecMinor}, 2, HelloBin),
+ case ServerHello of
+ #server_hello{server_version = {3,0}, cipher_suite = <<0,57>>} ->
+ ok;
+ _ ->
+ ct:fail({unexpected_server_hello, ServerHello})
+ end.
+%%--------------------------------------------------------------------
+emulated_options() ->
+ [{doc,"Test API function getopts/2 and setopts/2"}].
+
+emulated_options(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Values = [{mode, list}, {packet, 0}, {header, 0},
+ {active, true}],
+ %% Shall be the reverse order of Values!
+ Options = [active, header, packet, mode],
+
+ NewValues = [{mode, binary}, {active, once}],
+ %% Shall be the reverse order of NewValues!
+ NewOptions = [active, mode],
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, tls_socket_options_result,
+ [Options, Values, NewOptions, NewValues]}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, tls_socket_options_result,
+ [Options, Values, NewOptions, NewValues]}},
+ {options, ClientOpts}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+
+ {ok, Listen} = ssl:listen(0, ServerOpts),
+ {ok,[{mode,list}]} = ssl:getopts(Listen, [mode]),
+ ok = ssl:setopts(Listen, [{mode, binary}]),
+ {ok,[{mode, binary}]} = ssl:getopts(Listen, [mode]),
+ {ok,[{recbuf, _}]} = ssl:getopts(Listen, [recbuf]),
+ ssl:close(Listen).
+accept_pool() ->
+ [{doc,"Test having an accept pool."}].
+accept_pool(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server0 = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {accepters, 3},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server0),
+ [Server1, Server2] = ssl_test_lib:accepters(2),
+
+ Client0 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, ClientOpts}
+ ]),
+
+ Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, ClientOpts}
+ ]),
+
+ Client2 = 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_ok([Server0, Server1, Server2, Client0, Client1, Client2]),
+
+ ssl_test_lib:close(Server0),
+ ssl_test_lib:close(Server1),
+ ssl_test_lib:close(Server2),
+ ssl_test_lib:close(Client0),
+ ssl_test_lib:close(Client1),
+ ssl_test_lib:close(Client2).
+
+%%--------------------------------------------------------------------
+reuseaddr() ->
+ [{doc,"Test reuseaddr option"}].
+
+reuseaddr(Config) when is_list(Config) ->
+ ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config),
+ ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {options, [{active, false}, {reuseaddr, true}| 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, no_result, []}},
+ {options, [{active, false} | ClientOpts]}]),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client),
+
+ Server1 =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, Port},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result, []}},
+ {options, [{active, false}, {reuseaddr, true} | ServerOpts]}]),
+ Client1 =
+ ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result, []}},
+ {options, [{active, false} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server1, ok, Client1, ok),
+ ssl_test_lib:close(Server1),
+ ssl_test_lib:close(Client1).
+
+%%--------------------------------------------------------------------
+%% Internal functions ------------------------------------------------
+%%--------------------------------------------------------------------
+
+upgrade_result(Socket) ->
+ ssl:setopts(Socket, [{active, true}]),
+ ok = ssl:send(Socket, "Hello world"),
+ %% Make sure binary is inherited from tcp socket and that we do
+ %% not get the list default!
+ receive
+ {ssl, _, <<"H">>} ->
+ receive
+ {ssl, _, <<"ello world">>} ->
+ ok
+ end;
+ {ssl, _, <<"Hello world">>} ->
+ ok
+ end.
+tls_downgrade_result(Socket, Pid) ->
+ ok = ssl_test_lib:send_recv_result(Socket),
+ Pid ! {self(), ready},
+ receive
+ go ->
+ ok
+ end,
+ case ssl:close(Socket, {self(), 10000}) of
+ {ok, TCPSocket} ->
+ inet:setopts(TCPSocket, [{active, true}]),
+ gen_tcp:send(TCPSocket, "Downgraded"),
+ receive
+ {tcp, TCPSocket, <<"Downgraded">>} ->
+ ok;
+ {tcp_closed, TCPSocket} ->
+ ct:fail("Did not receive TCP data"),
+ ok;
+ Other ->
+ {error, Other}
+ end;
+ {error, timeout} ->
+ ct:comment("Timed out, downgrade aborted"),
+ ok;
+ Fail ->
+ ct:fail(Fail)
+ end.
+
+tls_shutdown_result(Socket, server) ->
+ ssl:send(Socket, "Hej"),
+ ok = ssl:shutdown(Socket, write),
+ {ok, "Hej hopp"} = ssl:recv(Socket, 8),
+ ok;
+
+tls_shutdown_result(Socket, client) ->
+ ssl:send(Socket, "Hej hopp"),
+ ok = ssl:shutdown(Socket, write),
+ {ok, "Hej"} = ssl:recv(Socket, 3),
+ ok.
+
+tls_shutdown_write_result(Socket, server) ->
+ ct:sleep(?SLEEP),
+ ssl:shutdown(Socket, write);
+tls_shutdown_write_result(Socket, client) ->
+ ssl:recv(Socket, 0).
+
+tls_shutdown_both_result(Socket, server) ->
+ ct:sleep(?SLEEP),
+ ssl:shutdown(Socket, read_write);
+tls_shutdown_both_result(Socket, client) ->
+ ssl:recv(Socket, 0).
+
+tls_closed_in_active_once_loop(Socket) ->
+ case ssl:setopts(Socket, [{active, once}]) of
+ ok ->
+ receive
+ {ssl, Socket, _} ->
+ tls_closed_in_active_once_loop(Socket);
+ {ssl_closed, Socket} ->
+ ok
+ after 5000 ->
+ no_ssl_closed_received
+ end;
+ {error, closed} ->
+ ok
+ end.
+
+drop_handshakes(Socket, Timeout) ->
+ {ok, <<RecType:8, _RecMajor:8, _RecMinor:8, RecLen:16>> = Header} = gen_tcp:recv(Socket, 5, Timeout),
+ {ok, <<Frag:RecLen/binary>>} = gen_tcp:recv(Socket, RecLen, Timeout),
+ case RecType of
+ 22 -> drop_handshakes(Socket, Timeout);
+ _ -> {ok, <<Header/binary, Frag/binary>>}
+ end.
+
+receive_msg(_) ->
+ receive
+ Msg ->
+ Msg
+ end.
+
+sockname_result(S) ->
+ ssl:sockname(S).
+
+peername_result(S) ->
+ ssl:peername(S).
+
+tls_socket_options_result(Socket, Options, DefaultValues, NewOptions, NewValues) ->
+ %% Test get/set emulated opts
+ {ok, DefaultValues} = ssl:getopts(Socket, Options),
+ ssl:setopts(Socket, NewValues),
+ {ok, NewValues} = ssl:getopts(Socket, NewOptions),
+ %% Test get/set inet opts
+ {ok,[{nodelay,false}]} = ssl:getopts(Socket, [nodelay]),
+ ssl:setopts(Socket, [{nodelay, true}]),
+ {ok,[{nodelay, true}]} = ssl:getopts(Socket, [nodelay]),
+ {ok, All} = ssl:getopts(Socket, []),
+ ct:log("All opts ~p~n", [All]),
+ ok.
+
diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk
index c9547cae36..c7404c0169 100644
--- a/lib/ssl/vsn.mk
+++ b/lib/ssl/vsn.mk
@@ -1 +1 @@
-SSL_VSN = 9.3.5
+SSL_VSN = 9.4
diff --git a/lib/stdlib/Makefile b/lib/stdlib/Makefile
index 0444cedadb..cae3844126 100644
--- a/lib/stdlib/Makefile
+++ b/lib/stdlib/Makefile
@@ -36,4 +36,6 @@ SPECIAL_TARGETS =
#
include $(ERL_TOP)/make/otp_subdir.mk
+DIA_PLT_APPS=compiler crypto
+
include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/stdlib/doc/src/digraph.xml b/lib/stdlib/doc/src/digraph.xml
index cf2c0844c9..fa6d2b81fe 100644
--- a/lib/stdlib/doc/src/digraph.xml
+++ b/lib/stdlib/doc/src/digraph.xml
@@ -36,10 +36,23 @@
<modulesummary>Directed graphs.</modulesummary>
<description>
<p>This module provides a version of labeled
- directed graphs. What makes the graphs provided here
- non-proper directed graphs is that multiple edges between
- vertices are allowed. However, the customary definition of
- directed graphs is used here.</p>
+ directed graphs ("digraphs").</p>
+
+ <p>The digraphs managed by this module are stored in
+ <seealso marker="ets">ETS tables</seealso>.
+ That implies the following:</p>
+
+ <list type="bulleted">
+ <item><p>Only the process that created the digraph is allowed to update it.</p></item>
+ <item><p>Digraphs will not be garbage collected. The ETS tables used for a
+ digraph will only be deleted when <seealso marker="#delete/1"><c>delete/1</c></seealso>
+ is called or the process that created the digraph terminates.</p></item>
+ <item><p>A digraph is a mutable data structure.</p></item>
+ </list>
+
+ <p>What makes the graphs provided here non-proper directed graphs
+ is that multiple edges between vertices are allowed. However, the
+ customary definition of directed graphs is used here.</p>
<list type="bulleted">
<item>
diff --git a/lib/stdlib/doc/src/ets.xml b/lib/stdlib/doc/src/ets.xml
index 2cb677785d..2d0a88fc6e 100644
--- a/lib/stdlib/doc/src/ets.xml
+++ b/lib/stdlib/doc/src/ets.xml
@@ -635,6 +635,14 @@ Error: fun containing local Erlang function calls
the following items are allowed:</p>
<list type="bulleted">
<item>
+ <p><c>Item=binary, Value=BinInfo</c></p>
+ <p><c>BinInfo</c> is a list containing miscellaneous
+ information about binaries kept by the table.
+ This <c><anno>Item</anno></c> can be changed or removed without
+ prior notice. In the current implementation <c>BinInfo</c> is a
+ list of tuples <c>{BinaryId,BinarySize,BinaryRefcCount}</c>.</p>
+ </item>
+ <item>
<p><c>Item=fixed, Value=boolean()</c></p>
<p>Indicates if the table is fixed by any process.</p>
</item>
@@ -782,29 +790,25 @@ Error: fun containing local Erlang function calls
<fsummary>Check if an Erlang term is the result of
<c>match_spec_compile</c>.</fsummary>
<desc>
- <p>Checks if a term is a valid
- compiled <seealso marker="#match_spec">match specification</seealso>.
- The compiled match specification is an opaque datatype that
- <em>cannot</em> be sent between Erlang nodes or be stored on
- disk. Any attempt to create an external representation of a
- compiled match specification results in an empty binary
- (<c><![CDATA[<<>>]]></c>).</p>
- <p><em>Examples:</em></p>
- <p>The following expression yields <c>true</c>::</p>
- <code type="none">
-ets:is_compiled_ms(ets:match_spec_compile([{'_',[],[true]}])).</code>
- <p>The following expressions yield <c>false</c>, as variable
- <c>Broken</c> contains a compiled match specification that has
- passed through external representation:</p>
- <code type="none">
-MS = ets:match_spec_compile([{'_',[],[true]}]),
-Broken = binary_to_term(term_to_binary(MS)),
-ets:is_compiled_ms(Broken).</code>
+ <p>Checks if a term represent a valid compiled
+ <seealso marker="#match_spec">match specification</seealso>.
+ A compiled match specifications is only valid on the Erlang node where
+ it was compiled by calling <seealso marker="#match_spec_compile/1">
+ <c>match_spec_compile/1</c></seealso>.</p>
<note>
- <p>The reason for not having an external representation of
- compiled match specifications is performance. It can be
- subject to change in future releases, while this interface
- remains for backward compatibility.</p>
+ <p>
+ Before STDLIB 3.4 (OTP 20.0) compiled match specifications did
+ not have an external representation. If passed through
+ <c>binary_to_term(term_to_binary(CMS))</c> or sent to another node
+ and back, the result was always an empty binary <c>&lt;&lt;>></c>.</p>
+ <p>
+ After STDLIB 3.4 (OTP 20.0) compiled match specifications have an
+ external representation as a node specific reference to the original
+ compiled match specification. If passed through
+ <c>binary_to_term(term_to_binary(CMS))</c> or sent to another node
+ and back, the result <em>may or may not</em> be a valid compiled match
+ specification depending on if the original compiled match
+ specification was still alive.</p>
</note>
</desc>
</func>
@@ -1033,11 +1037,7 @@ ets:is_compiled_ms(Broken).</code>
<seealso marker="#match_spec">match specification</seealso> into an
internal representation that can be used in subsequent calls to
<seealso marker="#match_spec_run/2"><c>match_spec_run/2</c></seealso>.
- The internal representation is
- opaque and cannot be converted to external term format and
- then back again without losing its properties (that is, it cannot
- be sent to a process on another node and still remain a
- valid compiled match specification, nor can it be stored on disk).
+ The internal representation is opaque.
To check the validity of a compiled match specification, use
<seealso marker="#is_compiled_ms/1"><c>is_compiled_ms/1</c></seealso>.
</p>
@@ -1330,46 +1330,41 @@ ets:select(Table, MatchSpec),</code>
continuation has passed through external term format (been
sent between nodes or stored on disk).</p>
<p>The reason for this function is that continuation terms
- contain compiled match specifications and therefore are
+ contain compiled match specifications and may therefore be
invalidated if converted to external term format. Given that the
original match specification is kept intact, the continuation can
be restored, meaning it can once again be used in subsequent
<c>select/1</c> calls even though it has been stored on
disk or on another node.</p>
<p><em>Examples:</em></p>
- <p>The following sequence of calls fails:</p>
+ <p>The following sequence of calls may fail:</p>
<code type="none">
T=ets:new(x,[]),
...
-{_,C} = ets:select(T,ets:fun2ms(fun({N,_}=A)
-when (N rem 10) =:= 0 ->
-A
-end),10),
-Broken = binary_to_term(term_to_binary(C)),
-ets:select(Broken).</code>
+MS = ets:fun2ms(fun({N,_}=A) when (N rem 10) =:= 0 -> A end),
+{_,C} = ets:select(T, MS, 10),
+MaybeBroken = binary_to_term(term_to_binary(C)),
+ets:select(MaybeBroken).</code>
<p>The following sequence works, as the call to
- <c>repair_continuation/2</c> reestablishes the (deliberately)
- invalidated continuation <c>Broken</c>.</p>
+ <c>repair_continuation/2</c> reestablishes the
+ <c>MaybeBroken</c> continuation.</p>
<code type="none">
T=ets:new(x,[]),
...
-MS = ets:fun2ms(fun({N,_}=A)
-when (N rem 10) =:= 0 ->
-A
-end),
+MS = ets:fun2ms(fun({N,_}=A) when (N rem 10) =:= 0 -> A end),
{_,C} = ets:select(T,MS,10),
-Broken = binary_to_term(term_to_binary(C)),
-ets:select(ets:repair_continuation(Broken,MS)).</code>
+MaybeBroken = binary_to_term(term_to_binary(C)),
+ets:select(ets:repair_continuation(MaybeBroken,MS)).</code>
<note>
<p>This function is rarely needed in application code. It is used
by Mnesia to provide distributed <c>select/3</c>
and <c>select/1</c> sequences. A normal application would
either use Mnesia or keep the continuation from being
converted to external format.</p>
- <p>The reason for not having an external representation of a
- compiled match specification is performance. It can be subject to
- change in future releases, while this interface remains
- for backward compatibility.</p>
+ <p>The actual behavior of compiled match specifications when recreated
+ from external format has changed and may change in future releases,
+ but this interface remains for backward compatibility.
+ See <seealso marker="#is_compiled_ms/1"><c>is_compiled_ms/1</c></seealso>.</p>
</note>
</desc>
</func>
diff --git a/lib/stdlib/doc/src/gen_statem.xml b/lib/stdlib/doc/src/gen_statem.xml
index ef548ad643..dabce02b3d 100644
--- a/lib/stdlib/doc/src/gen_statem.xml
+++ b/lib/stdlib/doc/src/gen_statem.xml
@@ -33,43 +33,73 @@
<description>
<p>
<c>gen_statem</c> provides a generic state machine behaviour
- and replaces its predecessor
+ that for new code replaces its predecessor
<seealso marker="gen_fsm"><c>gen_fsm</c></seealso>
- since Erlang/OTP 20.0.
+ since Erlang/OTP 20.0. The <c>gen_fsm</c> behaviour remains
+ in OTP "as is".
</p>
+ <note>
+ <p>
+ If you are new to <c>gen_statem</c> and want an overview
+ of concepts and operation the section
+ <seealso marker="doc/design_principles:statem">
+ <c>gen_statem</c>&nbsp;Behaviour
+ </seealso>
+ located in the User's Guide
+ <seealso marker="doc/design_principles:users_guide">
+ OTP Design Principles
+ </seealso>
+ is recommended to read before this reference manual,
+ possibly after the Description section you are reading here.
+ </p>
+ </note>
<p>
- This reference manual describes types generated from the types
- in the <c>gen_statem</c> source code, so they are correct.
+ This reference manual contains type descriptions generated from
+ types in the <c>gen_statem</c> source code, so they are correct.
However, the generated descriptions also reflect the type hierarchy,
- which makes them kind of hard to read.
- </p>
- <p>
- To get an overview of the concepts and operation of <c>gen_statem</c>,
- do read the
+ which sometimes makes it hard to get a good overview.
+ If so, see the section
<seealso marker="doc/design_principles:statem">
<c>gen_statem</c>&nbsp;Behaviour
</seealso>
- in
+ in the
<seealso marker="doc/design_principles:users_guide">
OTP Design Principles
</seealso>
- which frequently links back to this reference manual to avoid
- containing detailed facts that may rot by age.
+ User's Guide.
</p>
<note>
- <p>
- This behavior appeared in Erlang/OTP 19.0.
- In OTP 19.1 a backwards incompatible change of
- the return tuple from
- <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>
- was made and the mandatory callback function
- <seealso marker="#Module:callback_mode/0">
- <c>Module:callback_mode/0</c>
- </seealso>
- was introduced. In OTP 20.0 the
- <seealso marker="#type-generic_timeout"><c>generic timeouts</c></seealso>
- were added.
- </p>
+ <list type="bulleted">
+ <item>This behavior appeared in Erlang/OTP 19.0.</item>
+ <item>
+ In OTP 19.1 a backwards incompatible change of
+ the return tuple from
+ <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso>
+ was made and the mandatory callback function
+ <seealso marker="#Module:callback_mode/0">
+ <c>Module:callback_mode/0</c>
+ </seealso>
+ was introduced.
+ </item>
+ <item>
+ In OTP 20.0
+ <seealso marker="#type-generic_timeout">
+ generic time-outs
+ </seealso>
+ were added.
+ </item>
+ <item>
+ In OTP 22.1 time-out content
+ <seealso marker="#type-timeout_update_action">
+ <c>update</c>
+ </seealso>
+ and explicit time-out
+ <seealso marker="#type-timeout_cancel_action">
+ <c>cancel</c>
+ </seealso>
+ were added.
+ </item>
+ </list>
</note>
<p>
<c>gen_statem</c> has got the same features that
@@ -653,7 +683,7 @@ handle_event(_, _, State, Data) ->
<name name="timeout_event_type"/>
<desc>
<p>
- There are 3 types of timeout events that the state machine
+ There are 3 types of time-out events that the state machine
can generate for itself with the corresponding
<seealso marker="#type-timeout_action">timeout_action()</seealso>s.
</p>
@@ -720,7 +750,9 @@ handle_event(_, _, State, Data) ->
the <c>gen_statem</c> engine will, at every <em>state change</em>,
call the
<seealso marker="#state callback">state callback</seealso>
- with arguments <c>(enter, OldState, Data)</c>.
+ with arguments <c>(enter, OldState, Data)</c> or
+ <c>(enter, OldState, State, Data)</c>, depending on the
+ <seealso marker="#type-callback_mode"><em>callback mode</em></seealso>.
This may look like an event but is really a call
performed after the previous
<seealso marker="#state callback"><em>state callback</em></seealso>
@@ -776,44 +808,53 @@ handle_event(_, _, State, Data) ->
</p>
<list type="ordered">
<item>
+ <p>
+ All returned
+ <seealso marker="#type-action">actions</seealso>
+ are processed in order of appearance.
+ In this step all replies generated by any
+ <seealso marker="#type-reply_action"><c>reply_action()</c></seealso>
+ are sent. Other actions set <c>transition_option()</c>s
+ that come into play in subsequent steps.
+ </p>
+ </item>
+ <item>
<p>
If
<seealso marker="#type-state_enter">
<em>state enter calls</em>
</seealso>
- are used, and either:
- the state changes, it is the initial state,
- or one of the callback results
+ are used, and either
+ it is the initial state or one of the callback results
<seealso marker="#type-state_callback_result">
- <c>repeat_state</c>
+ <c>repeat_state_and_data</c>
</seealso>
or
<seealso marker="#type-state_callback_result">
<c>repeat_state_and_data</c>
</seealso>
- is used; the <c>gen_statem</c> calls
- the new state callback with arguments
- <seealso marker="#type-state_enter"><c>(enter, OldState, Data)</c></seealso>.
+ is used the <c>gen_statem</c> engine calls
+ the current state callback with arguments
+ <seealso marker="#type-state_enter"><c>(enter, State, Data)</c></seealso>
+ or
+ <seealso marker="#type-state_enter"><c>(enter, State, State, Data)</c></seealso>
+ (depending on <seealso marker="#type-callback_mode"><em>callback mode</em></seealso>)
+ and when it returns starts again from the top of this sequence.
</p>
- <p>
- Any
- <seealso marker="#type-enter_action">actions</seealso>
- returned from this call are handled as if they were
- appended to the actions
- returned by the state callback that caused the state entry.
+ <p>
+ If
+ <seealso marker="#type-state_enter">
+ <em>state enter calls</em>
+ </seealso>
+ are used, and the state changes
+ the <c>gen_statem</c> engine calls
+ the new state callback with arguments
+ <seealso marker="#type-state_enter"><c>(enter, OldState, Data)</c></seealso>
+ or
+ <seealso marker="#type-state_enter"><c>(enter, OldState, State, Data)</c></seealso>
+ (depending on <seealso marker="#type-callback_mode"><em>callback mode</em></seealso>)
+ and when it returns starts again from the top of this sequence.
</p>
- <p>
- Should this <em>state enter call</em> return any of
- the mentioned <c>repeat_*</c> callback results
- it is repeated again, with the updated <c>Data</c>.
- </p>
- </item>
- <item>
- <p>
- All
- <seealso marker="#type-action">actions</seealso>
- are processed in order of appearance.
- </p>
</item>
<item>
<p>
@@ -863,7 +904,9 @@ handle_event(_, _, State, Data) ->
A <em>state change</em> cancels a
<seealso marker="#type-state_timeout"><c>state_timeout()</c></seealso>
and any new transition option of this type
- belongs to the new state.
+ belongs to the new state, that is; a
+ <seealso marker="#type-state_timeout"><c>state_timeout()</c></seealso>
+ applies to the state the state machine enters.
</p>
</item>
<item>
@@ -872,7 +915,7 @@ handle_event(_, _, State, Data) ->
<seealso marker="#state callback"><em>state callback</em></seealso>
for the possibly new state
is called with the oldest enqueued event,
- and we start again from the top of this list.
+ and we start again from the top of this sequence.
</p>
</item>
<item>
@@ -1176,7 +1219,7 @@ handle_event(_, _, State, Data) ->
<seealso marker="#enter_loop/5"><c>enter_loop/5,6</c></seealso>.
</p>
<p>
- These timeout actions sets timeout
+ These time-out actions sets time-out
<seealso marker="#type-transition_option">transition options</seealso>.
</p>
<taglist>
@@ -1229,6 +1272,36 @@ handle_event(_, _, State, Data) ->
</desc>
</datatype>
<datatype>
+ <name name="timeout_cancel_action" since="OTP 22.1"/>
+ <desc>
+ <p>
+ This is a shorter and clearer form of
+ <seealso marker="#type-timeout_action">
+ timeout_action()
+ </seealso>
+ with <c>Time = infinity</c> which cancels a time-out.
+ </p>
+ </desc>
+ </datatype>
+ <datatype>
+ <name name="timeout_update_action" since="OTP 22.1"/>
+ <desc>
+ <p>
+ Updates a time-out with a new <c>EventContent</c>.
+ See
+ <seealso marker="#type-timeout_action">
+ timeout_action()
+ </seealso>
+ for how to start a time-out.
+ </p>
+ <p>
+ If no time-out of the same type is active instead
+ insert the time-out event just like when starting
+ a time-out with relative <c>Time = 0</c>.
+ </p>
+ </desc>
+ </datatype>
+ <datatype>
<name name="reply_action"/>
<desc>
<p>
diff --git a/lib/stdlib/doc/src/io_protocol.xml b/lib/stdlib/doc/src/io_protocol.xml
index 84b5f62c7f..f05c358866 100644
--- a/lib/stdlib/doc/src/io_protocol.xml
+++ b/lib/stdlib/doc/src/io_protocol.xml
@@ -5,7 +5,7 @@
<header>
<copyright>
<year>1999</year>
- <year>2016</year>
+ <year>2019</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -168,16 +168,6 @@ ok
returns it "as is".</item>
</list>
- <p>For backward compatibility, the following <c>Request</c>s are also to be
- handled by an I/O server (they are not to be present after
- Erlang/OTP R15B):</p>
-
- <pre>
-{put_chars, Characters}
-{put_chars, Module, Function, Args}</pre>
-
- <p>These are to behave as <c>{put_chars, latin1, Characters}</c> and
- <c>{put_chars, latin1, Module, Function, Args}</c>, respectively.</p>
</section>
<section>
@@ -332,19 +322,6 @@ eof
</item>
</list>
- <p>For backward compatibility, the following <c>Request</c>s are also to be
- handled by an I/O server (they are not to be present after
- Erlang/OTP R15B):</p>
-
- <pre>
-{get_until, Prompt, Module, Function, ExtraArgs}
-{get_chars, Prompt, N}
-{get_line, Prompt}</pre>
-
- <p>These are to behave as
- <c>{get_until, latin1, Prompt, Module, Function, ExtraArgs}</c>,
- <c>{get_chars, latin1, Prompt, N}</c>, and
- <c>{get_line, latin1, Prompt}</c>, respectively.</p>
</section>
<section>
@@ -637,24 +614,6 @@ request({requests, Reqs}, State) -&gt;
function applying the requests in the list one after another, returning
the last result.</p>
- <p>We need to handle backward compatibility and the
- <seealso marker="kernel:file"><c>file</c></seealso> module (which
- uses the old requests until backward compatibility with pre-R13 nodes is
- no longer needed). Notice that the I/O server does not work with a simple
- <c>file:write/2</c> if these are not added:</p>
-
- <code>
-request({put_chars,Chars}, State) -&gt;
- request({put_chars,latin1,Chars}, State);
-request({put_chars,M,F,As}, State) -&gt;
- request({put_chars,latin1,M,F,As}, State);
-request({get_chars,Prompt,N}, State) -&gt;
- request({get_chars,latin1,Prompt,N}, State);
-request({get_line,Prompt}, State) -&gt;
- request({get_line,latin1,Prompt}, State);
-request({get_until, Prompt,M,F,As}, State) -&gt;
- request({get_until,latin1,Prompt,M,F,As}, State);</code>
-
<p><c>{error, request}</c> must be returned if the request is not
recognized:</p>
diff --git a/lib/stdlib/doc/src/notes.xml b/lib/stdlib/doc/src/notes.xml
index 66624c43be..e2badecffa 100644
--- a/lib/stdlib/doc/src/notes.xml
+++ b/lib/stdlib/doc/src/notes.xml
@@ -31,6 +31,148 @@
</header>
<p>This document describes the changes made to the STDLIB application.</p>
+<section><title>STDLIB 3.10</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ <c>re:run()</c> now yields when validating utf8 in a
+ large subject.</p>
+ <p>
+ Own Id: OTP-15836 Aux Id: ERL-876 </p>
+ </item>
+ <item>
+ <p>
+ Upgraded the ERTS internal PCRE library from version 8.42
+ to version 8.43. See <url
+ href="http://pcre.org/original/changelog.txt">http://pcre.org/original/changelog.txt</url>
+ for information about changes made to PCRE. This library
+ implements major parts of the <seealso
+ marker="stdlib:re"><c>re</c></seealso> regular
+ expressions module.</p>
+ <p>
+ Own Id: OTP-15889</p>
+ </item>
+ <item>
+ <p>
+ The bug with ID ERL-717 has been fixed. The functions
+ <c>io:columns()</c> and <c>io:rows()</c> only worked
+ correctly inside interactive erlang shells before this
+ fix. These functions returned <c>{error,enotsup}</c>
+ before this fix even if stdout and stdin were connected
+ to a terminal when they were invoked from an escript or a
+ program started with e.g., <c>erl -noshell</c>.</p>
+ <p>
+ Own Id: OTP-15959 Aux Id: ERL-717 </p>
+ </item>
+ <item>
+ <p>Fixed handling of ".." and "@" in wildcards. ".."
+ would only work when preceded by a literal pattern such
+ as in "a/..", not when preceded by wildcard characters
+ such as in "*/..". The combination "@/.." was also
+ broken, and in addition "@" in a pattern could degrade
+ performance of the wildcard matching.</p>
+ <p>
+ Own Id: OTP-15987 Aux Id: ERL-1029 </p>
+ </item>
+ <item>
+ <p> Make sure <c>ets:fun2ms()</c> can handle <c>++/2</c>
+ in the head of functions when called from the shell. </p>
+ <p>
+ Own Id: OTP-15992 Aux Id: PR-2322 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>Debugging of time-outs in <c>gen_statem</c> has been
+ improved. Starting a time-out is now logged in
+ <c>sys:log</c> and <c>sys:trace</c>. Running time-outs
+ are visible in server crash logs, and with
+ <c>sys:get_status</c>. Due to this system events
+ <c>{start_timer, Action, State}</c> and
+ <c>{insert_timout, Event, State}</c> have been added,
+ which may surprise tools that rely on the format of these
+ events. </p> <p>New features: The <c>EventContent</c> of
+ a running time-out can be updated with <c>{TimeoutType,
+ update, NewEventContent}</c>. Running time-outs can be
+ cancelled with <c>{TimeoutType, cancel}</c> which is more
+ readable than using <c>Time = infinity</c>. </p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-15510</p>
+ </item>
+ <item>
+ <p>
+ <c>re:run()</c> now avoids validating utf8 in the subject
+ more than once in the same call. This validation could
+ previously be performed multiple times when the
+ <c>global</c> option was passed.</p>
+ <p>
+ Own Id: OTP-15831 Aux Id: ERL-876 </p>
+ </item>
+ <item>
+ <p>
+ ETS <c>ordered_set</c> tables with
+ <c>write_concurrency</c> enabled has got a performance
+ issue fixed. There were no limits for the values of
+ internal statistics counters before this fix. This could
+ result in that the data structure sometimes reacted
+ slowly to a change in how many parallel processes were
+ using it.</p>
+ <p>
+ Own Id: OTP-15906</p>
+ </item>
+ <item>
+ <p>The <c>ordsets:union/1</c> is now faster when passed a
+ long list of ordsets.</p>
+ <p>
+ Own Id: OTP-15927</p>
+ </item>
+ <item>
+ <p>
+ <c>unicode:characters_to_binary()</c> could return very
+ small binaries as reference counted off heap binaries.
+ This could cause an unnecessary large memory usage and an
+ unnecessary load on the binary allocator. Small binaries
+ are now always returned as heap binaries.</p>
+ <p>
+ Own Id: OTP-16002 Aux Id: ERIERL-366 </p>
+ </item>
+ <item>
+ <p> Display a more meaningful error message when a bad
+ I/O server is used in a script written in Erlang
+ (<c>escript</c>). </p>
+ <p>
+ Own Id: OTP-16006 Aux Id: ERL-992 </p>
+ </item>
+ <item>
+ <p>
+ New feature <c>ets:info(_, binary)</c> to get information
+ about all reference counted binaries kept by a table.
+ This is the same kind of debug information that
+ <c>process_info(_, binary)</c> returns for a process.</p>
+ <p>
+ Own Id: OTP-16035 Aux Id: ERIERL-366 </p>
+ </item>
+ <item>
+ <p>
+ Corrected ETS documentation about the behavior of
+ compiled match specifications when serialized through
+ external format.</p>
+ <p>
+ Own Id: OTP-16038 Aux Id: PR-2366 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>STDLIB 3.9.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/stdlib/doc/src/sys.xml b/lib/stdlib/doc/src/sys.xml
index e22ca89ef5..ebea054fff 100644
--- a/lib/stdlib/doc/src/sys.xml
+++ b/lib/stdlib/doc/src/sys.xml
@@ -239,6 +239,34 @@
</item>
<tag>
<c>
+ {start_timer,<anno>Action</anno>,<anno>State</anno>}
+ </c>
+ </tag>
+ <item>
+ <p>
+ Is produced by <c>gen_statem</c>
+ when the action <c><anno>Action</anno></c>
+ starts a timer in state <c><anno>State</anno></c>.
+ </p>
+ </item>
+ <tag>
+ <c>
+ {insert_timeout,<anno>Event</anno>,<anno>State</anno>}
+ </c>
+ </tag>
+ <item>
+ <p>
+ Is produced by <c>gen_statem</c> when a timeout zero action
+ inserts event <c><anno>Event</anno></c>
+ in state <c><anno>State</anno></c>.
+ </p>
+ <p>
+ <c><anno>Event</anno></c> is
+ an <c>{EventType,EventContent}</c> tuple.
+ </p>
+ </item>
+ <tag>
+ <c>
{enter,<anno>State</anno>}
</c>
</tag>
diff --git a/lib/stdlib/src/edlin.erl b/lib/stdlib/src/edlin.erl
index f027d05f55..6078c5e67b 100644
--- a/lib/stdlib/src/edlin.erl
+++ b/lib/stdlib/src/edlin.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -352,9 +352,6 @@ do_op({blink,C,M}, Bef=[$$,$$|_], Aft, Rs) ->
%% don't blink after a $
do_op({blink,C,_}, Bef=[$$|_], Aft, Rs) ->
do_op({insert,C}, Bef, Aft, Rs);
-%do_op({blink,C,M}, Bef, [], Rs) ->
-% N = over_paren(Bef, C, M),
-% {blink,N+1,{[C|Bef],[]},[{move_rel,-(N+1)},{put_chars,[C]}|Rs]};
do_op({blink,C,M}, Bef, Aft, Rs) ->
case over_paren(Bef, C, M) of
beep ->
diff --git a/lib/stdlib/src/erl_compile.erl b/lib/stdlib/src/erl_compile.erl
index f781312ca2..2a063fede1 100644
--- a/lib/stdlib/src/erl_compile.erl
+++ b/lib/stdlib/src/erl_compile.erl
@@ -22,12 +22,10 @@
-include("erl_compile.hrl").
-include("file.hrl").
--export([compile_cmdline/0]).
+-export([compile_cmdline/0, compile/2]).
-export_type([cmd_line_arg/0]).
--define(STDERR, standard_error). %Macro to avoid misspellings.
-
%% Mapping from extension to {M,F} to run the correct compiler.
compiler(".erl") -> {compile, compile};
@@ -46,60 +44,56 @@ compiler(".asn") -> {asn1ct, compile_asn};
compiler(".py") -> {asn1ct, compile_py};
compiler(_) -> no.
-%% Entry from command line.
-
-type cmd_line_arg() :: atom() | string().
+%% Run a compilation based on the command line arguments and then halt.
+%% Intended for one-off compilation by erlc.
-spec compile_cmdline() -> no_return().
-
compile_cmdline() ->
+ cmdline_init(),
List = init:get_plain_arguments(),
- case compile(List) of
- ok -> my_halt(0);
- error -> my_halt(1);
- _ -> my_halt(2)
+ compile_cmdline1(List).
+
+%% Run a compilation. Meant to be used by the compilation server.
+-spec compile(list(), file:filename()) -> 'ok' | {'error', binary()}.
+compile(Args, Cwd) ->
+ try compile1(Args, #options{outdir=Cwd,cwd=Cwd}) of
+ ok ->
+ ok
+ catch
+ throw:{error, Output} ->
+ {error, unicode:characters_to_binary(Output)};
+ C:E:Stk ->
+ {crash, {C,E,Stk}}
end.
--spec my_halt(_) -> no_return().
-my_halt(Reason) ->
- erlang:halt(Reason).
-
-%% Run the the compiler in a separate process, trapping EXITs.
-
-compile(List) ->
- process_flag(trap_exit, true),
- Pid = spawn_link(compiler_runner(List)),
+%% Run the the compiler in a separate process.
+compile_cmdline1(Args) ->
+ {ok, Cwd} = file:get_cwd(),
+ {Pid,Ref} = spawn_monitor(fun() -> exit(compile(Args, Cwd)) end),
receive
- {'EXIT', Pid, {compiler_result, Result}} ->
- Result;
- {'EXIT', Pid, {compiler_error, Error}} ->
- io:put_chars(?STDERR, Error),
- io:nl(?STDERR),
- error;
- {'EXIT', Pid, Reason} ->
- io:format(?STDERR, "Runtime error: ~tp~n", [Reason]),
- error
+ {'DOWN', Ref, process, Pid, Result} ->
+ case Result of
+ ok ->
+ halt(0);
+ {error, Output} ->
+ io:put_chars(standard_error, Output),
+ halt(1);
+ {crash, {C,E,Stk}} ->
+ io:format(standard_error, "Crash: ~p:~tp\n~tp\n",
+ [C,E,Stk]),
+ halt(2)
+ end
end.
--spec compiler_runner([cmd_line_arg()]) -> fun(() -> no_return()).
-
-compiler_runner(List) ->
- fun() ->
- %% We don't want the current directory in the code path.
- %% Remove it.
- Path = [D || D <- code:get_path(), D =/= "."],
- true = code:set_path(Path),
- exit({compiler_result, compile1(List)})
- end.
-
-%% Parses the first part of the option list.
-
-compile1(Args) ->
- {ok, Cwd} = file:get_cwd(),
- compile1(Args, #options{outdir=Cwd,cwd=Cwd}).
-
-%% Parses all options.
+cmdline_init() ->
+ %% We don't want the current directory in the code path.
+ %% Remove it.
+ Path = [D || D <- code:get_path(), D =/= "."],
+ true = code:set_path(Path),
+ ok.
+%% Parse all options.
compile1(["--"|Files], Opts) ->
compile2(Files, Opts);
compile1(["-"++Option|T], Opts) ->
@@ -132,12 +126,8 @@ parse_generic_option("I"++Opt, T0, #options{cwd=Cwd}=Opts) ->
AbsDir = filename:absname(Dir, Cwd),
compile1(T, Opts#options{includes=[AbsDir|Opts#options.includes]});
parse_generic_option("M"++Opt, T0, #options{specific=Spec}=Opts) ->
- case parse_dep_option(Opt, T0) of
- error ->
- error;
- {SpecOpts,T} ->
- compile1(T, Opts#options{specific=SpecOpts++Spec})
- end;
+ {SpecOpts,T} = parse_dep_option(Opt, T0),
+ compile1(T, Opts#options{specific=SpecOpts++Spec});
parse_generic_option("o"++Opt, T0, #options{cwd=Cwd}=Opts) ->
{Dir,T} = get_option("o", Opt, T0),
AbsName = filename:absname(Dir, Cwd),
@@ -181,8 +171,7 @@ parse_generic_option("P", T, #options{specific=Spec}=Opts) ->
parse_generic_option("S", T, #options{specific=Spec}=Opts) ->
compile1(T, Opts#options{specific=['S'|Spec]});
parse_generic_option(Option, _T, _Opts) ->
- io:format(?STDERR, "Unknown option: -~ts\n", [Option]),
- usage().
+ usage(io_lib:format("Unknown option: -~ts\n", [Option])).
parse_dep_option("", T) ->
{[makedep,{makedep_output,standard_io}],T};
@@ -204,10 +193,14 @@ parse_dep_option("T"++Opt, T0) ->
{Target,T} = get_option("MT", Opt, T0),
{[{makedep_target,Target}],T};
parse_dep_option(Opt, _T) ->
- io:format(?STDERR, "Unknown option: -M~ts\n", [Opt]),
- usage().
+ usage(io_lib:format("Unknown option: -M~ts\n", [Opt])).
+
+-spec usage() -> no_return().
usage() ->
+ usage("").
+
+usage(Error) ->
H = [{"-b type","type of output file (e.g. beam)"},
{"-d","turn on debugging of erlc itself"},
{"-Dname","define name"},
@@ -238,18 +231,18 @@ usage() ->
{"-S","generate assembly listing (Erlang compiler)"},
{"-P","generate listing of preprocessed code (Erlang compiler)"},
{"+term","pass the Erlang term unchanged to the compiler"}],
- io:put_chars(?STDERR,
- ["Usage: erlc [Options] file.ext ...\n",
- "Options:\n",
- [io_lib:format("~-14s ~s\n", [K,D]) || {K,D} <- H]]),
- error.
+ Msg = [Error,
+ "Usage: erlc [Options] file.ext ...\n",
+ "Options:\n",
+ [io_lib:format("~-14s ~s\n", [K,D]) || {K,D} <- H]],
+ throw({error, Msg}).
get_option(_Name, [], [[C|_]=Option|T]) when C =/= $- ->
{Option,T};
get_option(_Name, [_|_]=Option, T) ->
{Option,T};
get_option(Name, _, _) ->
- exit({compiler_error,"No value given to -"++Name++" option"}).
+ throw({error, "No value given to -"++Name++" option\n"}).
split_at_equals([$=|T], Acc) ->
{lists:reverse(Acc),T};
@@ -266,14 +259,10 @@ compile2(Files, #options{cwd=Cwd,includes=Incl,outfile=Outfile}=Opts0) ->
{[_|_], 1} ->
compile3(Files, Cwd, Opts);
{[_|_], _N} ->
- io:put_chars(?STDERR,
- "Output file name given, "
- "but more than one input file.\n"),
- error
+ throw({error, "Output file name given, but more than one input file.\n"})
end.
-%% Compiles the list of files, until done or compilation fails.
-
+%% Compile the list of files, until done or compilation fails.
compile3([File|Rest], Cwd, Options) ->
Ext = filename:extension(File),
Root = filename:rootname(File),
@@ -285,43 +274,37 @@ compile3([File|Rest], Cwd, Options) ->
Outfile ->
filename:rootname(Outfile)
end,
- case compile_file(Ext, InFile, OutFile, Options) of
- ok ->
- compile3(Rest, Cwd, Options);
- Other ->
- Other
- end;
+ compile_file(Ext, InFile, OutFile, Options),
+ compile3(Rest, Cwd, Options);
compile3([], _Cwd, _Options) -> ok.
-%% Invokes the appropriate compiler, depending on the file extension.
-
+%% Invoke the appropriate compiler, depending on the file extension.
compile_file("", Input, _Output, _Options) ->
- io:format(?STDERR, "File has no extension: ~ts~n", [Input]),
- error;
+ throw({error, io_lib:format("File has no extension: ~ts~n", [Input])});
compile_file(Ext, Input, Output, Options) ->
case compiler(Ext) of
no ->
- io:format(?STDERR, "Unknown extension: '~ts'\n", [Ext]),
- error;
+ Error = io_lib:format("Unknown extension: '~ts'\n", [Ext]),
+ throw({error, Error});
{M, F} ->
- case catch M:F(Input, Output, Options) of
- ok -> ok;
- error -> error;
- {'EXIT',Reason} ->
- io:format(?STDERR,
- "Compiler function ~w:~w/3 failed:\n~p~n",
- [M,F,Reason]),
- error;
+ try M:F(Input, Output, Options) of
+ ok ->
+ ok;
+ error ->
+ throw({error, ""});
Other ->
- io:format(?STDERR,
- "Compiler function ~w:~w/3 returned:\n~p~n",
- [M,F,Other]),
- error
+ Error = io_lib:format("Compiler function ~w:~w/3 returned:\n~tp~n",
+ [M,F,Other]),
+ throw({error, Error})
+ catch
+ throw:Reason:Stk ->
+ Error = io_lib:format("Compiler function ~w:~w/3 failed:\n~tp\n~tp\n",
+ [M,F,Reason,Stk]),
+ throw({error, Error})
end
end.
-%% Guesses if a give name refers to a file or a directory.
-
+%% Guess whether a given name refers to a file or a directory.
file_or_directory(Name) ->
case file:read_file_info(Name) of
{ok, #file_info{type=regular}} ->
@@ -335,18 +318,16 @@ file_or_directory(Name) ->
end
end.
-%% Makes an Erlang term given a string.
-
-make_term(Str) ->
+%% Make an Erlang term given a string.
+make_term(Str) ->
case erl_scan:string(Str) of
- {ok, Tokens, _} ->
+ {ok, Tokens, _} ->
case erl_parse:parse_term(Tokens ++ [{dot, erl_anno:new(1)}]) of
- {ok, Term} -> Term;
+ {ok, Term} ->
+ Term;
{error, {_,_,Reason}} ->
- io:format(?STDERR, "~ts: ~ts~n", [Reason, Str]),
- throw(error)
+ throw({error, io_lib:format("~ts: ~ts~n", [Reason, Str])})
end;
{error, {_,_,Reason}, _} ->
- io:format(?STDERR, "~ts: ~ts~n", [Reason, Str]),
- throw(error)
+ throw({error, io_lib:format("~ts: ~ts~n", [Reason, Str])})
end.
diff --git a/lib/stdlib/src/erl_internal.erl b/lib/stdlib/src/erl_internal.erl
index 939abaff00..e1f8fe151e 100644
--- a/lib/stdlib/src/erl_internal.erl
+++ b/lib/stdlib/src/erl_internal.erl
@@ -245,11 +245,14 @@ bif(M, F, A) when is_atom(M), is_atom(F), is_integer(A) -> false.
bif(abs, 1) -> true;
bif(apply, 2) -> true;
bif(apply, 3) -> true;
+bif(atom_to_binary, 1) -> true;
bif(atom_to_binary, 2) -> true;
bif(atom_to_list, 1) -> true;
bif(binary_part, 2) -> true;
bif(binary_part, 3) -> true;
+bif(binary_to_atom, 1) -> true;
bif(binary_to_atom, 2) -> true;
+bif(binary_to_existing_atom, 1) -> true;
bif(binary_to_existing_atom, 2) -> true;
bif(binary_to_integer, 1) -> true;
bif(binary_to_integer, 2) -> true;
diff --git a/lib/stdlib/src/escript.erl b/lib/stdlib/src/escript.erl
index 3f14894b55..4e9ba1cc16 100644
--- a/lib/stdlib/src/escript.erl
+++ b/lib/stdlib/src/escript.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2018. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -281,11 +281,11 @@ start(EscriptOptions) ->
end
catch
throw:Str ->
- io:format("escript: ~ts\n", [Str]),
+ put_chars(io_lib:format("escript: ~ts\n", [Str])),
my_halt(127);
_:Reason:Stk ->
- io:format("escript: Internal error: ~tp\n", [Reason]),
- io:format("~tp\n", [Stk]),
+ put_chars(io_lib:format("escript: Internal error: ~tp\n", [Reason])),
+ put_chars(io_lib:format("~tp\n", [Stk])),
my_halt(127)
end.
@@ -885,13 +885,22 @@ format_exception(Class, Reason, StackTrace) ->
erl_error:format_exception(1, Class, Reason, StackTrace, StackFun, PF, Enc).
encoding() ->
- [{encoding, Encoding}] = enc(),
- Encoding.
+ case io:getopts() of
+ {error, _}=_Err ->
+ latin1;
+ Opts ->
+ case lists:keyfind(encoding, 1, Opts) of
+ false -> latin1;
+ {encoding, Encoding} -> Encoding
+ end
+ end.
-enc() ->
- case lists:keyfind(encoding, 1, io:getopts()) of
- false -> [{encoding,latin1}]; % should never happen
- Enc -> [Enc]
+put_chars(String) ->
+ try
+ io:put_chars(String)
+ catch
+ _:_ ->
+ erlang:display(lists:flatten(String))
end.
a0() ->
diff --git a/lib/stdlib/src/ets.erl b/lib/stdlib/src/ets.erl
index 29f907ad73..e926e4fcaf 100644
--- a/lib/stdlib/src/ets.erl
+++ b/lib/stdlib/src/ets.erl
@@ -174,7 +174,7 @@ info(_) ->
-spec info(Tab, Item) -> Value | undefined when
Tab :: tab(),
- Item :: compressed | fixed | heir | id | keypos | memory
+ Item :: binary | compressed | fixed | heir | id | keypos | memory
| name | named_table | node | owner | protection
| safe_fixed | safe_fixed_monotonic_time | size | stats | type
| write_concurrency | read_concurrency,
diff --git a/lib/stdlib/src/filelib.erl b/lib/stdlib/src/filelib.erl
index de839be5cf..d1a5a4dc35 100644
--- a/lib/stdlib/src/filelib.erl
+++ b/lib/stdlib/src/filelib.erl
@@ -281,6 +281,14 @@ do_wildcard_2([], _, Result, _Mod) ->
do_wildcard_3(Base, [[double_star]|Rest], Result, Mod) ->
do_double_star(".", [Base], Rest, Result, Mod, true);
+do_wildcard_3(Base, [".."|Rest], Result, Mod) ->
+ case do_is_dir(Base, Mod) of
+ true ->
+ Matches = [filename:join(Base, "..")],
+ do_wildcard_2(Matches, Rest, Result, Mod);
+ false ->
+ Result
+ end;
do_wildcard_3(Base0, [Pattern|Rest], Result, Mod) ->
case do_list_dir(Base0, Mod) of
{ok, Files} ->
@@ -387,15 +395,29 @@ compile_wildcard(Pattern0, Cwd0) ->
end.
compile_wildcard_2([Part|Rest], Root) ->
- case compile_part(Part) of
- Part ->
- compile_wildcard_2(Rest, compile_join(Root, Part));
- Pattern ->
- compile_wildcard_3(Rest, [Pattern,Root])
+ Pattern = compile_part(Part),
+ case is_literal_pattern(Pattern) of
+ true ->
+ %% Add this literal pattern to the literal pattern prefix.
+ %% This is an optimization to avoid listing all files of
+ %% a directory only to discard all but one. For example,
+ %% without this optimizaton, there would be three
+ %% redundant directory listings when executing this
+ %% wildcard: "./lib/compiler/ebin/*.beam"
+ compile_wildcard_2(Rest, compile_join(Root, Pattern));
+ false ->
+ %% This is the end of the literal prefix. Compile the
+ %% rest of the pattern.
+ compile_wildcard_3(Rest, [Pattern,Root])
end;
compile_wildcard_2([], {root,PrefixLen,Root}) ->
{{exists,Root},PrefixLen}.
+is_literal_pattern([H|T]) ->
+ is_integer(H) andalso is_literal_pattern(T);
+is_literal_pattern([]) ->
+ true.
+
compile_wildcard_3([Part|Rest], Result) ->
compile_wildcard_3(Rest, [compile_part(Part)|Result]);
compile_wildcard_3([], Result) ->
diff --git a/lib/stdlib/src/gen_statem.erl b/lib/stdlib/src/gen_statem.erl
index 49911eac2c..105b2a4577 100644
--- a/lib/stdlib/src/gen_statem.erl
+++ b/lib/stdlib/src/gen_statem.erl
@@ -176,7 +176,17 @@
{'state_timeout', % Set the state_timeout option
Time :: state_timeout(),
EventContent :: term(),
- Options :: (timeout_option() | [timeout_option()])}.
+ Options :: (timeout_option() | [timeout_option()])} |
+ timeout_cancel_action() |
+ timeout_update_action().
+-type timeout_cancel_action() ::
+ {'timeout', 'cancel'} |
+ {{'timeout', Name :: term()}, 'cancel'} |
+ {'state_timeout', 'cancel'}.
+-type timeout_update_action() ::
+ {'timeout', 'update', EventContent :: term()} |
+ {{'timeout', Name :: term()}, 'update', EventContent :: term()} |
+ {'state_timeout', 'update', EventContent :: term()}.
-type reply_action() ::
{'reply', % Reply to a caller
From :: from(), Reply :: term()}.
@@ -420,11 +430,9 @@ timeout_event_type(Type) ->
{state_data = {undefined,undefined} ::
{State :: term(),Data :: term()},
postponed = [] :: [{event_type(),term()}],
- timers = {#{},#{}} ::
- {%% TimerRef => TimeoutType
- TimerRefs :: #{reference() => timeout_event_type()},
- %% TimeoutType => TimerRef
- TimeoutTypes :: #{timeout_event_type() => reference()}},
+ timers = #{} ::
+ #{TimeoutType :: timeout_event_type() =>
+ {TimerRef :: reference(), TimeoutMsg :: term()}},
hibernate = false :: boolean()
}).
@@ -807,13 +815,14 @@ format_status(
Opt,
[PDict,SysState,Parent,Debug,
{#params{name = Name} = P,
- #state{postponed = Postponed} = S}]) ->
+ #state{postponed = Postponed, timers = Timers} = S}]) ->
Header = gen:format_status_header("Status for state machine", Name),
Log = sys:get_log(Debug),
[{header,Header},
{data,
[{"Status",SysState},
{"Parent",Parent},
+ {"Time-outs",list_timeouts(Timers)},
{"Logged Events",Log},
{"Postponed",Postponed}]} |
case format_status(Opt, PDict, update_parent(P, Parent), S) of
@@ -860,6 +869,14 @@ print_event(Dev, SystemEvent, Name) ->
io:format(
Dev, "*DBG* ~tp enter in state ~tp~n",
[Name,State]);
+ {start_timer,Action,State} ->
+ io:format(
+ Dev, "*DBG* ~tp start_timer ~tp in state ~tp~n",
+ [Name,Action,State]);
+ {insert_timeout,Event,State} ->
+ io:format(
+ Dev, "*DBG* ~tp insert_timeout ~tp in state ~tp~n",
+ [Name,Event,State]);
{terminate,Reason,State} ->
io:format(
Dev, "*DBG* ~tp terminate ~tp in state ~tp~n",
@@ -945,15 +962,12 @@ loop_receive(
{'$gen_cast',Cast} ->
loop_receive_result(P, Debug, S, {cast,Cast});
%%
- {timeout,TimerRef,TimeoutMsg} ->
- {TimerRefs,TimeoutTypes} = S#state.timers,
- case TimerRefs of
- #{TimerRef := TimeoutType} ->
+ {timeout,TimerRef,TimeoutType} ->
+ case S#state.timers of
+ #{TimeoutType := {TimerRef,TimeoutMsg}} = Timers ->
%% Our timer
- Timers =
- {maps:remove(TimerRef, TimerRefs),
- maps:remove(TimeoutType, TimeoutTypes)},
- S_1 = S#state{timers = Timers},
+ Timers_1 = maps:remove(TimeoutType, Timers),
+ S_1 = S#state{timers = Timers_1},
loop_receive_result(
P, Debug, S_1, {TimeoutType,TimeoutMsg});
#{} ->
@@ -1514,20 +1528,20 @@ loop_actions_timeout(
true ->
case listify(TimeoutOpts) of
%% Optimization cases
- [] when ?relative_timeout(Time) ->
- RelativeTimeout = {TimeoutType,Time,TimeoutMsg},
+ [{abs,true}] when ?absolute_timeout(Time) ->
loop_actions_list(
P, Debug, S, Q, NextState_NewData,
NextEventsR, Hibernate,
- [RelativeTimeout|TimeoutsR], Postpone,
+ [Timeout|TimeoutsR], Postpone,
CallEnter, StateCall, Actions);
- [{abs,true}] when ?absolute_timeout(Time) ->
+ [{abs,false}] when ?relative_timeout(Time) ->
+ RelativeTimeout = {TimeoutType,Time,TimeoutMsg},
loop_actions_list(
P, Debug, S, Q, NextState_NewData,
NextEventsR, Hibernate,
- [Timeout|TimeoutsR], Postpone,
+ [RelativeTimeout|TimeoutsR], Postpone,
CallEnter, StateCall, Actions);
- [{abs,false}] when ?relative_timeout(Time) ->
+ [] when ?relative_timeout(Time) ->
RelativeTimeout = {TimeoutType,Time,TimeoutMsg},
loop_actions_list(
P, Debug, S, Q, NextState_NewData,
@@ -1544,14 +1558,13 @@ loop_actions_timeout(
[Timeout|TimeoutsR], Postpone,
CallEnter, StateCall, Actions);
false when ?relative_timeout(Time) ->
- RelativeTimeout =
- {TimeoutType,Time,TimeoutMsg},
+ RelativeTimeout = {TimeoutType,Time,TimeoutMsg},
loop_actions_list(
P, Debug, S, Q, NextState_NewData,
NextEventsR, Hibernate,
[RelativeTimeout|TimeoutsR], Postpone,
CallEnter, StateCall, Actions);
- badarg ->
+ _ ->
terminate(
error,
{bad_action_from_state_function,Timeout},
@@ -1576,10 +1589,12 @@ loop_actions_timeout(
P, Debug, S, Q, NextState_NewData,
NextEventsR, Hibernate, TimeoutsR, Postpone,
CallEnter, StateCall, Actions,
- {TimeoutType,Time,_} = Timeout) ->
+ {TimeoutType,Time,_TimeoutMsg} = Timeout) ->
%%
case timeout_event_type(TimeoutType) of
- true when ?relative_timeout(Time) ->
+ true
+ when ?relative_timeout(Time);
+ Time =:= update ->
loop_actions_list(
P, Debug, S, Q, NextState_NewData,
NextEventsR, Hibernate,
@@ -1598,15 +1613,40 @@ loop_actions_timeout(
loop_actions_timeout(
P, Debug, S, Q, NextState_NewData,
NextEventsR, Hibernate, TimeoutsR, Postpone,
- CallEnter, StateCall, Actions, Time) ->
+ CallEnter, StateCall, Actions,
+ {TimeoutType,cancel} = Action) ->
+ %%
+ case timeout_event_type(TimeoutType) of
+ true ->
+ Timeout = {TimeoutType,infinity,undefined},
+ loop_actions_list(
+ P, Debug, S, Q, NextState_NewData,
+ NextEventsR, Hibernate,
+ [Timeout|TimeoutsR], Postpone,
+ CallEnter, StateCall, Actions);
+ false ->
+ terminate(
+ error,
+ {bad_action_from_state_function,Action},
+ ?STACKTRACE(), P, Debug,
+ S#state{
+ state_data = NextState_NewData,
+ hibernate = Hibernate},
+ Q)
+ end;
+loop_actions_timeout(
+ P, Debug, S, Q, NextState_NewData,
+ NextEventsR, Hibernate, TimeoutsR, Postpone,
+ CallEnter, StateCall, Actions,
+ Time) ->
%%
if
?relative_timeout(Time) ->
- RelativeTimeout = {timeout,Time,Time},
+ Timeout = {timeout,Time,Time},
loop_actions_list(
P, Debug, S, Q, NextState_NewData,
NextEventsR, Hibernate,
- [RelativeTimeout|TimeoutsR], Postpone,
+ [Timeout|TimeoutsR], Postpone,
CallEnter, StateCall, Actions);
true ->
terminate(
@@ -1683,23 +1723,20 @@ loop_state_transition(
%% State transition to the same state
%%
loop_keep_state(
- P, Debug, #state{timers = {TimerRefs,TimeoutTypes} = Timers} = S,
+ P, Debug, #state{timers = Timers} = S,
Events, NextState_NewData,
NextEventsR, Hibernate, TimeoutsR, Postponed) ->
%%
%% Cancel event timeout
%%
- case TimeoutTypes of
- %% Optimization
- %% - only cancel timer when there is a timer to cancel
- #{timeout := TimerRef} ->
+ case Timers of
+ #{timeout := {TimerRef,_TimeoutMsg}} ->
%% Event timeout active
loop_next_events(
P, Debug, S,
Events, NextState_NewData,
NextEventsR, Hibernate, TimeoutsR, Postponed,
- cancel_timer_by_ref_and_type(
- TimerRef, timeout, TimerRefs, TimeoutTypes));
+ cancel_timer(timeout, TimerRef, Timers));
_ ->
%% No event timeout active
loop_next_events(
@@ -1741,34 +1778,32 @@ loop_state_change(
end.
%%
loop_state_change(
- P, Debug, #state{timers = {TimerRefs,TimeoutTypes} = Timers} = S,
+ P, Debug, #state{timers = Timers} = S,
Events, NextState_NewData,
NextEventsR, Hibernate, TimeoutsR) ->
%%
%% Cancel state and event timeout
%%
- case TimeoutTypes of
+ case Timers of
%% Optimization
- %% - only cancel timeout when there is an active timeout
+ %% - only cancel timeout when it is active
%%
- #{state_timeout := TimerRef} ->
+ #{state_timeout := {TimerRef,_TimeoutMsg}} ->
%% State timeout active
%% - cancel event timeout too since it is faster than inspecting
loop_next_events(
P, Debug, S, Events, NextState_NewData,
NextEventsR, Hibernate, TimeoutsR, [],
- cancel_timer_by_type(
+ cancel_timer(
timeout,
- cancel_timer_by_ref_and_type(
- TimerRef, state_timeout, TimerRefs, TimeoutTypes)));
- #{timeout := TimerRef} ->
+ cancel_timer(state_timeout, TimerRef, Timers)));
+ #{timeout := {TimerRef,_TimeoutMsg}} ->
%% Event timeout active but not state timeout
%% - cancel event timeout only
loop_next_events(
P, Debug, S, Events, NextState_NewData,
NextEventsR, Hibernate, TimeoutsR, [],
- cancel_timer_by_ref_and_type(
- TimerRef, timeout, TimerRefs, TimeoutTypes));
+ cancel_timer(timeout, TimerRef, Timers));
_ ->
%% No state nor event timeout active.
loop_next_events(
@@ -1778,7 +1813,7 @@ loop_state_change(
end.
%% Continue state transition with processing of
-%% inserted events and timeout events
+%% timeouts and inserted events
%%
loop_next_events(
P, Debug, S,
@@ -1786,7 +1821,7 @@ loop_next_events(
NextEventsR, Hibernate, [], Postponed,
Timers) ->
%%
- %% Optimization when there are no timeout actions
+ %% Optimization when there are no timeouts
%% hence no timeout zero events to append to Events
%% - avoid loop_timeouts
loop_done(
@@ -1811,7 +1846,8 @@ loop_next_events(
NextEventsR, Hibernate, TimeoutsR, Postponed,
Timers, Seen, TimeoutEvents).
-%% Continue state transition with processing of timeout events
+%% Continue state transition with processing of timeouts
+%% and finally inserted events
%%
loop_timeouts(
P, Debug, S,
@@ -1819,6 +1855,8 @@ loop_timeouts(
NextEventsR, Hibernate, [], Postponed,
Timers, _Seen, TimeoutEvents) ->
%%
+ %% End of timeouts
+ %%
S_1 =
S#state{
state_data = NextState_NewData,
@@ -1854,37 +1892,28 @@ loop_timeouts(
NextEventsR, Hibernate, [Timeout|TimeoutsR], Postponed,
Timers, Seen, TimeoutEvents) ->
%%
- case Timeout of
- {TimeoutType,Time,TimeoutMsg} ->
- %% Relative timeout
- case Seen of
- #{TimeoutType := _} ->
- %% Type seen before - ignore
- loop_timeouts(
- P, Debug, S,
- Events, NextState_NewData,
- NextEventsR, Hibernate, TimeoutsR, Postponed,
- Timers, Seen, TimeoutEvents);
- #{} ->
- loop_timeouts(
+ TimeoutType = element(1, Timeout),
+ case Seen of
+ #{TimeoutType := _} ->
+ %% Type seen before - ignore
+ loop_timeouts(
+ P, Debug, S,
+ Events, NextState_NewData,
+ NextEventsR, Hibernate, TimeoutsR, Postponed,
+ Timers, Seen, TimeoutEvents);
+ #{} ->
+ case Timeout of
+ {_,Time,TimeoutMsg} ->
+ %% Relative timeout or update
+ loop_timeouts_start(
P, Debug, S,
Events, NextState_NewData,
NextEventsR, Hibernate, TimeoutsR, Postponed,
Timers, Seen, TimeoutEvents,
- TimeoutType, Time, TimeoutMsg, [])
- end;
- {TimeoutType,Time,TimeoutMsg,TimeoutOpts} ->
- %% Absolute timeout
- case Seen of
- #{TimeoutType := _} ->
- %% Type seen before - ignore
- loop_timeouts(
- P, Debug, S,
- Events, NextState_NewData,
- NextEventsR, Hibernate, TimeoutsR, Postponed,
- Timers, Seen, TimeoutEvents);
- #{} ->
- loop_timeouts(
+ TimeoutType, Time, TimeoutMsg, []);
+ {_,Time,TimeoutMsg,TimeoutOpts} ->
+ %% Absolute timeout
+ loop_timeouts_start(
P, Debug, S,
Events, NextState_NewData,
NextEventsR, Hibernate, TimeoutsR, Postponed,
@@ -1892,8 +1921,10 @@ loop_timeouts(
TimeoutType, Time, TimeoutMsg, listify(TimeoutOpts))
end
end.
+
+%% Loop helper to start or restart a timeout
%%
-loop_timeouts(
+loop_timeouts_start(
P, Debug, S,
Events, NextState_NewData,
NextEventsR, Hibernate, TimeoutsR, Postponed,
@@ -1920,51 +1951,79 @@ loop_timeouts(
NextEventsR, Hibernate, TimeoutsR, Postponed,
Timers, Seen, [{TimeoutType,TimeoutMsg}|TimeoutEvents],
TimeoutType);
+ update ->
+ loop_timeouts_update(
+ P, Debug, S,
+ Events, NextState_NewData,
+ NextEventsR, Hibernate, TimeoutsR, Postponed,
+ Timers, Seen, TimeoutEvents,
+ TimeoutType, TimeoutMsg);
_ ->
%% (Re)start the timer
TimerRef =
- erlang:start_timer(Time, self(), TimeoutMsg, TimeoutOpts),
- {TimerRefs,TimeoutTypes} = Timers,
- case TimeoutTypes of
- #{TimeoutType := OldTimerRef} ->
- %% Cancel the running timer,
- %% update the timeout type,
- %% insert the new timer ref,
- %% and remove the old timer ref
- Timers_1 =
- {maps:remove(
- OldTimerRef,
- TimerRefs#{TimerRef => TimeoutType}),
- TimeoutTypes#{TimeoutType := TimerRef}},
- cancel_timer(OldTimerRef),
- loop_timeouts(
- P, Debug, S,
- Events, NextState_NewData,
+ erlang:start_timer(Time, self(), TimeoutType, TimeoutOpts),
+ case Debug of
+ ?not_sys_debug ->
+ loop_timeouts_register(
+ P, Debug, S, Events, NextState_NewData,
NextEventsR, Hibernate, TimeoutsR, Postponed,
- Timers_1, Seen#{TimeoutType => true}, TimeoutEvents);
- #{} ->
- %% Insert the new timer type and ref
- Timers_1 =
- {TimerRefs#{TimerRef => TimeoutType},
- TimeoutTypes#{TimeoutType => TimerRef}},
- loop_timeouts(
- P, Debug, S,
- Events, NextState_NewData,
+ Timers, Seen, TimeoutEvents,
+ TimeoutType, TimerRef, TimeoutMsg);
+ _ ->
+ {State,_Data} = NextState_NewData,
+ Debug_1 =
+ sys_debug(
+ Debug, P#params.name,
+ {start_timer,
+ {TimeoutType,Time,TimeoutMsg,TimeoutOpts},
+ State}),
+ loop_timeouts_register(
+ P, Debug_1, S, Events, NextState_NewData,
NextEventsR, Hibernate, TimeoutsR, Postponed,
- Timers_1, Seen#{TimeoutType => true}, TimeoutEvents)
+ Timers, Seen, TimeoutEvents,
+ TimeoutType, TimerRef, TimeoutMsg)
end
end.
+%% Loop helper to register a newly started timer
+%% and to cancel any running timer
+%%
+loop_timeouts_register(
+ P, Debug, S, Events, NextState_NewData,
+ NextEventsR, Hibernate, TimeoutsR, Postponed,
+ Timers, Seen, TimeoutEvents,
+ TimeoutType, TimerRef, TimeoutMsg) ->
+ %%
+ case Timers of
+ #{TimeoutType := {OldTimerRef,_OldTimeoutMsg}} ->
+ %% Cancel the running timer,
+ %% and update timer type and ref
+ cancel_timer(OldTimerRef),
+ Timers_1 = Timers#{TimeoutType := {TimerRef,TimeoutMsg}},
+ loop_timeouts(
+ P, Debug, S,
+ Events, NextState_NewData,
+ NextEventsR, Hibernate, TimeoutsR, Postponed,
+ Timers_1, Seen#{TimeoutType => true}, TimeoutEvents);
+ #{} ->
+ %% Insert the new timer type and ref
+ Timers_1 = Timers#{TimeoutType => {TimerRef,TimeoutMsg}},
+ loop_timeouts(
+ P, Debug, S,
+ Events, NextState_NewData,
+ NextEventsR, Hibernate, TimeoutsR, Postponed,
+ Timers_1, Seen#{TimeoutType => true}, TimeoutEvents)
+ end.
+
%% Loop helper to cancel a timeout
%%
loop_timeouts_cancel(
P, Debug, S,
Events, NextState_NewData,
NextEventsR, Hibernate, TimeoutsR, Postponed,
- {TimerRefs,TimeoutTypes} = Timers, Seen, TimeoutEvents,
- TimeoutType) ->
+ Timers, Seen, TimeoutEvents, TimeoutType) ->
%% This function body should have been:
- %% Timers_1 = cancel_timer_by_type(TimeoutType, Timers),
+ %% Timers_1 = cancel_timer(TimeoutType, Timers),
%% loop_timeouts(
%% P, Debug, S,
%% Events, NextState_NewData,
@@ -1974,14 +2033,12 @@ loop_timeouts_cancel(
%% Explicitly separate cases to get separate code paths for when
%% the map key exists vs. not, since otherwise the external call
%% to erlang:cancel_timer/1 and to map:remove/2 within
- %% cancel_timer_by_type/2 would cause all live registers
+ %% cancel_timer/2 would cause all live registers
%% to be saved to and restored from the stack also for
%% the case when the map key TimeoutType does not exist
- case TimeoutTypes of
- #{TimeoutType := TimerRef} ->
- Timers_1 =
- cancel_timer_by_ref_and_type(
- TimerRef, TimeoutType, TimerRefs, TimeoutTypes),
+ case Timers of
+ #{TimeoutType := {TimerRef,_TimeoutMsg}} ->
+ Timers_1 = cancel_timer(TimeoutType, TimerRef, Timers),
loop_timeouts(
P, Debug, S,
Events, NextState_NewData,
@@ -1995,6 +2052,36 @@ loop_timeouts_cancel(
Timers, Seen#{TimeoutType => true}, TimeoutEvents)
end.
+%% Loop helper to update the timeout message,
+%% or insert an event if no timer is running
+%%
+loop_timeouts_update(
+ P, Debug, S,
+ Events, NextState_NewData,
+ NextEventsR, Hibernate, TimeoutsR, Postponed,
+ Timers, Seen, TimeoutEvents,
+ TimeoutType, TimeoutMsg) ->
+ %%
+ case Timers of
+ #{TimeoutType := {TimerRef,_OldTimeoutMsg}} ->
+ Timers_1 = Timers#{TimeoutType := {TimerRef,TimeoutMsg}},
+ loop_timeouts(
+ P, Debug, S,
+ Events, NextState_NewData,
+ NextEventsR, Hibernate, TimeoutsR, Postponed,
+ Timers_1, Seen#{TimeoutType => true},
+ TimeoutEvents);
+ #{} ->
+ TimeoutEvents_1 =
+ [{TimeoutType,TimeoutMsg}|TimeoutEvents],
+ loop_timeouts(
+ P, Debug, S,
+ Events, NextState_NewData,
+ NextEventsR, Hibernate, TimeoutsR, Postponed,
+ Timers, Seen#{TimeoutType => true},
+ TimeoutEvents_1)
+ end.
+
%% Continue state transition with prepending timeout zero events
%% before event queue reversal i.e appending timeout zero events
%%
@@ -2056,13 +2143,13 @@ parse_timeout_opts_abs(Opts, Abs) ->
%% Enqueue immediate timeout events (timeout 0 events)
%%
-%% Event timer timeout 0 events gets special treatment since
-%% an event timer is cancelled by any received event,
-%% so if there are enqueued events before the event timer
-%% timeout 0 event - the event timer is cancelled hence no event.
+%% Event timeout 0 events gets special treatment since
+%% an event timeout is cancelled by any received event,
+%% so if there are enqueued events before the event
+%% timeout 0 event - the event timeout is cancelled hence no event.
%%
%% Other (state_timeout and {timeout,Name}) timeout 0 events
-%% that are after an event timer timeout 0 event are considered to
+%% that occur after an event timer timeout 0 event are considered to
%% belong to timers that were started after the event timer
%% timeout 0 event fired, so they do not cancel the event timer.
%%
@@ -2079,7 +2166,8 @@ prepend_timeout_events(
{State,_Data} = S#state.state_data,
Debug_1 =
sys_debug(
- Debug, P#params.name, {in,TimeoutEvent,State}),
+ Debug, P#params.name,
+ {insert_timeout,TimeoutEvent,State}),
prepend_timeout_events(
P, Debug_1, S, TimeoutEvents, [TimeoutEvent])
end;
@@ -2099,7 +2187,8 @@ prepend_timeout_events(
{State,_Data} = S#state.state_data,
Debug_1 =
sys_debug(
- Debug, P#params.name, {in,TimeoutEvent,State}),
+ Debug, P#params.name,
+ {insert_timeout,TimeoutEvent,State}),
prepend_timeout_events(
P, Debug_1, S, TimeoutEvents, [TimeoutEvent|EventsR])
end.
@@ -2190,7 +2279,9 @@ error_info(
name = Name,
callback_mode = CallbackMode,
state_enter = StateEnter} = P,
- #state{postponed = Postponed} = S,
+ #state{
+ postponed = Postponed,
+ timers = Timers} = S,
Q) ->
Log = sys:get_log(Debug),
?LOG_ERROR(#{label=>{gen_statem,terminate},
@@ -2200,6 +2291,7 @@ error_info(
callback_mode=>CallbackMode,
state_enter=>StateEnter,
state=>format_status(terminate, get(), P, S),
+ timeouts=>list_timeouts(Timers),
log=>Log,
reason=>{Class,Reason,Stacktrace},
client_info=>client_stacktrace(Q)},
@@ -2238,6 +2330,7 @@ format_log(#{label:={gen_statem,terminate},
callback_mode:=CallbackMode,
state_enter:=StateEnter,
state:=FmtData,
+ timeouts:=Timeouts,
log:=Log,
reason:={Class,Reason,Stacktrace},
client_info:=ClientInfo}) ->
@@ -2293,6 +2386,10 @@ format_log(#{label:={gen_statem,terminate},
[] -> "";
_ -> "** Stacktrace =~n** ~tp~n"
end ++
+ case Timeouts of
+ {0,_} -> "";
+ _ -> "** Time-outs: ~p~n"
+ end ++
case Log of
[] -> "";
_ -> "** Log =~n** ~tp~n"
@@ -2317,6 +2414,10 @@ format_log(#{label:={gen_statem,terminate},
[] -> [];
_ -> [error_logger:limit_term(FixedStacktrace)]
end ++
+ case Timeouts of
+ {0,_} -> [];
+ _ -> [error_logger:limit_term(Timeouts)]
+ end ++
case Log of
[] -> [];
_ -> [[error_logger:limit_term(T) || T <- Log]]
@@ -2370,40 +2471,55 @@ listify(Item) ->
[Item].
--define(cancel_timer(TimerRef),
- case erlang:cancel_timer(TimerRef) of
- false ->
- %% No timer found and we have not seen the timeout message
- receive
- {timeout,(TimerRef),_} ->
- ok
- end;
- _ ->
- %% Timer was running
- ok
- end).
-
+-define(
+ cancel_timer(TimerRef),
+ case erlang:cancel_timer(TimerRef) of
+ false ->
+ %% No timer found and we have not seen the timeout message
+ receive
+ {timeout,(TimerRef),_} ->
+ ok
+ end;
+ _ ->
+ %% Timer was running
+ ok
+ end).
+%%
+%% Cancel timer and consume timeout message
+%%
-compile({inline, [cancel_timer/1]}).
cancel_timer(TimerRef) ->
?cancel_timer(TimerRef).
+-define(
+ cancel_timer(TimeoutType, TimerRef, Timers),
+ begin
+ ?cancel_timer(TimerRef),
+ maps:remove(begin TimeoutType end, begin Timers end)
+ end).
+%%
+%% Cancel timer and remove from Timers
+%%
+-compile({inline, [cancel_timer/3]}).
+cancel_timer(TimeoutType, TimerRef, Timers) ->
+ ?cancel_timer(TimeoutType, TimerRef, Timers).
+
%% Cancel timer if running, otherwise no op
%%
%% Remove the timer from Timers
--compile({inline, [cancel_timer_by_type/2]}).
-cancel_timer_by_type(TimeoutType, {TimerRefs,TimeoutTypes} = Timers) ->
- case TimeoutTypes of
- #{TimeoutType := TimerRef} ->
- ?cancel_timer(TimerRef),
- {maps:remove(TimerRef, TimerRefs),
- maps:remove(TimeoutType, TimeoutTypes)};
+-compile({inline, [cancel_timer/2]}).
+cancel_timer(TimeoutType, Timers) ->
+ case Timers of
+ #{TimeoutType := {TimerRef, _TimeoutMsg}} ->
+ ?cancel_timer(TimeoutType, TimerRef, Timers);
#{} ->
Timers
end.
--compile({inline, [cancel_timer_by_ref_and_type/4]}).
-cancel_timer_by_ref_and_type(
- TimerRef, TimeoutType, TimerRefs, TimeoutTypes) ->
- ?cancel_timer(TimerRef),
- {maps:remove(TimerRef, TimerRefs),
- maps:remove(TimeoutType, TimeoutTypes)}.
+%% Return a list of all pending timeouts
+list_timeouts(Timers) ->
+ {maps:size(Timers),
+ maps:fold(
+ fun(TimeoutType, {_TimerRef,TimeoutMsg}, Acc) ->
+ [{TimeoutType,TimeoutMsg}|Acc]
+ end, [], Timers)}.
diff --git a/lib/stdlib/src/io.erl b/lib/stdlib/src/io.erl
index 63c9a6bddf..1848aa3628 100644
--- a/lib/stdlib/src/io.erl
+++ b/lib/stdlib/src/io.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -106,7 +106,6 @@ nl() ->
IoDevice :: device().
nl(Io) ->
-% o_request(Io, {put_chars,io_lib:nl()}).
o_request(Io, nl, nl).
-spec columns() -> {'ok', pos_integer()} | {'error', 'enotsup'}.
@@ -255,8 +254,6 @@ read(Io, Prompt) ->
case request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[1]}) of
{ok,Toks,_EndLine} ->
erl_parse:parse_term(Toks);
-% {error, Reason} when atom(Reason) ->
-% erlang:error(conv_reason(read, Reason), [Io, Prompt]);
{error,E,_EndLine} ->
{error,E};
{eof,_EndLine} ->
@@ -352,12 +349,7 @@ fread(Prompt, Format) ->
| server_no_data().
fread(Io, Prompt, Format) ->
- case request(Io, {fread,Prompt,Format}) of
-% {error, Reason} when atom(Reason) ->
-% erlang:error(conv_reason(fread, Reason), [Io, Prompt, Format]);
- Other ->
- Other
- end.
+ request(Io, {fread,Prompt,Format}).
-spec format(Format) -> 'ok' when
Format :: format().
diff --git a/lib/stdlib/src/io_lib.erl b/lib/stdlib/src/io_lib.erl
index 21d66c5529..e2823b70f2 100644
--- a/lib/stdlib/src/io_lib.erl
+++ b/lib/stdlib/src/io_lib.erl
@@ -78,7 +78,7 @@
%% Utilities for collecting characters.
-export([collect_chars/3, collect_chars/4,
- collect_line/2, collect_line/3, collect_line/4,
+ collect_line/3, collect_line/4,
get_until/3, get_until/4]).
%% The following functions were used by Yecc's include-file.
@@ -851,6 +851,7 @@ collect_chars({binary,Stack,N}, Data,latin1, _) ->
end;
collect_chars({list,Stack,N}, Data, _,_) ->
collect_chars_list(Stack, N, Data);
+
%% collect_chars(Continuation, MoreChars, Count)
%% Returns:
%% {done,Result,RestChars}
@@ -881,32 +882,6 @@ collect_chars_list(Stack, N, []) ->
collect_chars_list(Stack,N, [H|T]) ->
collect_chars_list([H|Stack], N-1, T).
-%% collect_line(Continuation, MoreChars)
-%% Returns:
-%% {done,Result,RestChars}
-%% {more,Continuation}
-%%
-%% XXX Can be removed when compatibility with pre-R12B-5 nodes
-%% is no longer required.
-%%
-collect_line([], Chars) ->
- collect_line1(Chars, []);
-collect_line({SoFar}, More) ->
- collect_line1(More, SoFar).
-
-collect_line1([$\r, $\n|Rest], Stack) ->
- collect_line1([$\n|Rest], Stack);
-collect_line1([$\n|Rest], Stack) ->
- {done,lists:reverse([$\n|Stack], []),Rest};
-collect_line1([C|Rest], Stack) ->
- collect_line1(Rest, [C|Stack]);
-collect_line1(eof, []) ->
- {done,eof,[]};
-collect_line1(eof, Stack) ->
- {done,lists:reverse(Stack, []),[]};
-collect_line1([], Stack) ->
- {more,{Stack}}.
-
%% collect_line(State, Data, _). New in R9C.
%% Returns:
%% {stop,Result,RestData}
diff --git a/lib/stdlib/src/io_lib_pretty.erl b/lib/stdlib/src/io_lib_pretty.erl
index 77f02eafe0..838d412d0c 100644
--- a/lib/stdlib/src/io_lib_pretty.erl
+++ b/lib/stdlib/src/io_lib_pretty.erl
@@ -895,9 +895,6 @@ write_string(S, _Uni) ->
io_lib:write_string(S, $"). %"
expand({_, _, _Dots=0, no_more} = If, _T, _Dd) -> If;
-%% expand({{list,L}, _Len, _, no_more}, T, Dd) ->
-%% {NL, NLen, NDots} = expand_list(L, T, Dd, 2),
-%% {{list,NL}, NLen, NDots, no_more};
expand({{tuple,IsTagged,L}, _Len, _, no_more}, T, Dd) ->
{NL, NLen, NDots} = expand_list(L, T, Dd, 2),
{{tuple,IsTagged,NL}, NLen, NDots, no_more};
diff --git a/lib/stdlib/src/ms_transform.erl b/lib/stdlib/src/ms_transform.erl
index 97ec785c62..74efe5c513 100644
--- a/lib/stdlib/src/ms_transform.erl
+++ b/lib/stdlib/src/ms_transform.erl
@@ -1100,6 +1100,8 @@ normalise({bin,_,Fs}) ->
B;
normalise({cons,_,Head,Tail}) ->
[normalise(Head)|normalise(Tail)];
+normalise({op,_,'++',A,B}) ->
+ normalise(A) ++ normalise(B);
normalise({tuple,_,Args}) ->
list_to_tuple(normalise_list(Args));
normalise({map,_,Pairs0}) ->
diff --git a/lib/stdlib/src/stdlib.app.src b/lib/stdlib/src/stdlib.app.src
index e6c42b9aac..4c4fb4349e 100644
--- a/lib/stdlib/src/stdlib.app.src
+++ b/lib/stdlib/src/stdlib.app.src
@@ -108,7 +108,6 @@
dets]},
{applications, [kernel]},
{env, []},
- {runtime_dependencies, ["sasl-3.0","kernel-6.0","erts-@OTP-15831:OTP-15836:OTP-15889@","crypto-3.3",
+ {runtime_dependencies, ["sasl-3.0","kernel-6.0","erts-10.5","crypto-3.3",
"compiler-5.0"]}
]}.
-
diff --git a/lib/stdlib/src/stdlib.appup.src b/lib/stdlib/src/stdlib.appup.src
index 0c270e9dd5..0f87d1e52a 100644
--- a/lib/stdlib/src/stdlib.appup.src
+++ b/lib/stdlib/src/stdlib.appup.src
@@ -41,7 +41,8 @@
{<<"^3\\.8\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
{<<"^3\\.9$">>,[restart_new_emulator]},
{<<"^3\\.9\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
- {<<"^3\\.9\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}],
+ {<<"^3\\.9\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
+ {<<"^3\\.9\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]}],
[{<<"^3\\.5$">>,[restart_new_emulator]},
{<<"^3\\.5\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
{<<"^3\\.5\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
@@ -56,4 +57,5 @@
{<<"^3\\.8\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
{<<"^3\\.9$">>,[restart_new_emulator]},
{<<"^3\\.9\\.0(?:\\.[0-9]+)+$">>,[restart_new_emulator]},
- {<<"^3\\.9\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]}]}.
+ {<<"^3\\.9\\.1(?:\\.[0-9]+)*$">>,[restart_new_emulator]},
+ {<<"^3\\.9\\.2(?:\\.[0-9]+)*$">>,[restart_new_emulator]}]}.
diff --git a/lib/stdlib/src/sys.erl b/lib/stdlib/src/sys.erl
index 6ff9aa33b4..93bf4743d2 100644
--- a/lib/stdlib/src/sys.erl
+++ b/lib/stdlib/src/sys.erl
@@ -51,6 +51,8 @@
| {'code_change', Event :: _, State :: _}
| {'postpone', Event :: _, State :: _, NextState :: _}
| {'consume', Event :: _, State :: _, NextState :: _}
+ | {'start_timer', Action :: _, State :: _}
+ | {'insert_timeout', Event :: _, State :: _}
| {'enter', State :: _}
| {'terminate', Reason :: _, State :: _}
| term().
diff --git a/lib/stdlib/test/binary_module_SUITE.erl b/lib/stdlib/test/binary_module_SUITE.erl
index 9b2033ec4a..be8ab3b98e 100644
--- a/lib/stdlib/test/binary_module_SUITE.erl
+++ b/lib/stdlib/test/binary_module_SUITE.erl
@@ -716,22 +716,22 @@ referenced(Config) when is_list(Config) ->
badarg = ?MASK_ERROR(binary:referenced_byte_size(apa)),
badarg = ?MASK_ERROR(binary:referenced_byte_size({})),
badarg = ?MASK_ERROR(binary:referenced_byte_size(1)),
- A = <<1,2,3>>,
- B = binary:copy(A,1000),
- 3 = binary:referenced_byte_size(A),
- 3000 = binary:referenced_byte_size(B),
- <<_:8,C:2/binary>> = A,
- 3 = binary:referenced_byte_size(C),
- 2 = binary:referenced_byte_size(binary:copy(C)),
- <<_:7,D:2/binary,_:1>> = A,
- 2 = binary:referenced_byte_size(binary:copy(D)),
- 3 = binary:referenced_byte_size(D),
- <<_:8,E:2/binary,_/binary>> = B,
- 3000 = binary:referenced_byte_size(E),
- 2 = binary:referenced_byte_size(binary:copy(E)),
- <<_:7,F:2/binary,_:1,_/binary>> = B,
- 2 = binary:referenced_byte_size(binary:copy(F)),
- 3000 = binary:referenced_byte_size(F),
+ A = <<0:(1024 * 8)>>,
+ B = binary:copy(A, 1000),
+ 1024 = binary:referenced_byte_size(A),
+ 1024000 = binary:referenced_byte_size(B),
+ <<_:8,C:1023/binary>> = A,
+ 1024 = binary:referenced_byte_size(C),
+ 1023 = binary:referenced_byte_size(binary:copy(C)),
+ <<_:7,D:1023/binary,_:1>> = A,
+ 1023 = binary:referenced_byte_size(binary:copy(D)),
+ 1024 = binary:referenced_byte_size(D),
+ <<_:8,E:128/binary,_/binary>> = B,
+ 1024000 = binary:referenced_byte_size(E),
+ 128 = binary:referenced_byte_size(binary:copy(E)),
+ <<_:7,F:128/binary,_:1,_/binary>> = B,
+ 128 = binary:referenced_byte_size(binary:copy(F)),
+ 1024000 = binary:referenced_byte_size(F),
ok.
diff --git a/lib/stdlib/test/escript_SUITE.erl b/lib/stdlib/test/escript_SUITE.erl
index 0b9106a99c..8ffd01f2b2 100644
--- a/lib/stdlib/test/escript_SUITE.erl
+++ b/lib/stdlib/test/escript_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -38,7 +38,8 @@
foldl/1,
overflow/1,
verify_sections/3,
- unicode/1
+ unicode/1,
+ bad_io_server/1
]).
-include_lib("common_test/include/ct.hrl").
@@ -53,7 +54,7 @@ all() ->
emulator_flags_no_shebang, two_lines,
module_script, beam_script, archive_script, epp,
create_and_extract, foldl, overflow,
- archive_script_file_access, unicode].
+ archive_script_file_access, unicode, bad_io_server].
groups() ->
[].
@@ -950,6 +951,16 @@ overflow(Config) when is_list(Config) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% OTP-16006, ERL-992
+bad_io_server(Config) when is_list(Config) ->
+ Data = proplists:get_value(data_dir, Config),
+ Dir = filename:absname(Data), %Get rid of trailing slash.
+ run(Dir, "bad_io_server",
+ [<<"\"escript: exception error: an error occurred when evaluating"
+ " an arithmetic expression\\n in operator '/'/2\\n "
+ "called as '\\x{400}' / 0\\n\"\r\nExitCode:127">>]),
+ ok.
+
run(Dir, Cmd0, Expected0) ->
Expected = iolist_to_binary(expected_output(Expected0, Dir)),
Cmd = case os:type() of
diff --git a/lib/stdlib/test/escript_SUITE_data/bad_io_server b/lib/stdlib/test/escript_SUITE_data/bad_io_server
new file mode 100755
index 0000000000..4a6e81c935
--- /dev/null
+++ b/lib/stdlib/test/escript_SUITE_data/bad_io_server
@@ -0,0 +1,11 @@
+#!/usr/bin/env escript
+%% -*- erlang -*-
+
+-export([main/1]).
+
+main(_) ->
+ ok = io:setopts([{encoding,unicode}]),
+ _D = erlang:system_flag(backtrace_depth, 0),
+ group_leader(spawn(fun() -> ok end), self()),
+ _ = '\x{400}'/0,
+ ok.
diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl
index 05893a92b0..b1813052d2 100644
--- a/lib/stdlib/test/ets_SUITE.erl
+++ b/lib/stdlib/test/ets_SUITE.erl
@@ -33,6 +33,7 @@
-export([slot/1]).
-export([match1/1, match2/1, match_object/1, match_object2/1]).
-export([dups/1, misc1/1, safe_fixtable/1, info/1, tab2list/1]).
+-export([info_binary_stress/1]).
-export([tab2file/1, tab2file2/1, tabfile_ext1/1,
tabfile_ext2/1, tabfile_ext3/1, tabfile_ext4/1, badfile/1]).
-export([heavy_lookup/1, heavy_lookup_element/1, heavy_concurrent/1]).
@@ -180,7 +181,7 @@ groups() ->
{match, [],
[match1, match2, match_object, match_object2]},
{misc, [],
- [misc1, safe_fixtable, info, dups, tab2list]},
+ [misc1, safe_fixtable, info, info_binary_stress, dups, tab2list]},
{files, [],
[tab2file, tab2file2, tabfile_ext1,
tabfile_ext2, tabfile_ext3, tabfile_ext4, badfile]},
@@ -961,7 +962,7 @@ t_delete_all_objects_do(Opts) ->
%% Test delete_all_objects is atomic
T2 = ets_new(t_delete_all_objects, [public | Opts]),
Self = self(),
- Inserters = [spawn_link(fun() -> inserter(T2, 100*1000, 1, Self) end) || _ <- [1,2,3,4]],
+ Inserters = [spawn_link(fun() -> inserter(T2, 1, Self) end) || _ <- [1,2,3,4]],
[receive {Ipid, running} -> ok end || Ipid <- Inserters],
ets:delete_all_objects(T2),
@@ -992,23 +993,26 @@ t_delete_all_objects_do(Opts) ->
ets:delete(T2).
-inserter(_, 0, _, _) ->
- ok;
-inserter(T, N, Next, Papa) ->
- case Next of
- 10*1000 ->
- Papa ! {self(), running};
- _ ->
- ok
- end,
+inserter(T, Next, Papa) ->
+ Wait = case Next of
+ 10*1000 ->
+ Papa ! {self(), running},
+ 0;
+ 100*1000 -> %% We most often don't reach this far
+ io:format("Inserter ~p reached ~p objects\n",
+ [self(), Next]),
+ infinity;
+ _ ->
+ 0
+ end,
ets:insert(T, {{Next, self()}}),
receive
stop ->
Papa ! {self(), stopped, Next},
ok
- after 0 ->
- inserter(T, N-1, Next+1, Papa)
+ after Wait ->
+ inserter(T, Next+1, Papa)
end.
@@ -4509,9 +4513,14 @@ safe_fixtable_do(Opts) ->
end,
ok.
+-define(ets_info(Tab,Item,SlavePid), ets_info(Tab, Item, SlavePid, ?LINE)).
+
%% Tests ets:info result for required tuples.
info(Config) when is_list(Config) ->
- repeat_for_opts_all_table_types(fun info_do/1).
+ repeat_for_opts(fun info_do/1,
+ [[void, set, bag, duplicate_bag, ordered_set],
+ [void, private, protected, public],
+ write_concurrency, read_concurrency, compressed]).
info_do(Opts) ->
EtsMem = etsmem(),
@@ -4551,6 +4560,15 @@ info_do(Opts) ->
ThisNode=node(),
Tab = ets_new(foobar, [{keypos, 2} | Opts]),
+ %% Start slave to also do ets:info from a process not owning the table.
+ SlavePid = spawn_link(fun Slave() ->
+ receive
+ {Master, Item} ->
+ Master ! {self(), Item, ets:info(Tab, Item)}
+ end,
+ Slave()
+ end),
+
%% Note: ets:info/1 used to return a tuple, but from R11B onwards it
%% returns a list.
Res = ets:info(Tab),
@@ -4565,6 +4583,32 @@ info_do(Opts) ->
{value, {protection, Protection}} =
lists:keysearch(protection, 1, Res),
{value, {id, Tab}} = lists:keysearch(id, 1, Res),
+
+ %% Test 'binary'
+ [] = ?ets_info(Tab, binary, SlavePid),
+ BinSz = 100,
+ RefcBin = list_to_binary(lists:seq(1,BinSz)),
+ ets:insert(Tab, {RefcBin,key}),
+ [{BinPtr,BinSz,2}] = ?ets_info(Tab,binary, SlavePid),
+ ets:insert(Tab, {RefcBin,key2}),
+ [{BinPtr,BinSz,3}, {BinPtr,BinSz,3}] = ?ets_info(Tab,binary,SlavePid),
+ ets:delete(Tab, key),
+ [{BinPtr,BinSz,2}] = ?ets_info(Tab,binary, SlavePid),
+ case TableType of
+ ordered_set ->
+ ets:delete(Tab, key2);
+ _ ->
+ ets:safe_fixtable(Tab, true),
+ ets:delete(Tab, key2),
+ [{BinPtr,BinSz,2}] = ?ets_info(Tab,binary, SlavePid),
+ ets:safe_fixtable(Tab, false)
+ end,
+ [] = ?ets_info(Tab,binary, SlavePid),
+ RefcBin = id(RefcBin), % keep alive
+
+ unlink(SlavePid),
+ exit(SlavePid,kill),
+
true = ets:delete(Tab),
undefined = ets:info(non_existing_table_xxyy),
undefined = ets:info(non_existing_table_xxyy,type),
@@ -4574,6 +4618,79 @@ info_do(Opts) ->
undefined = ets:info(non_existing_table_xxyy,safe_fixed),
verify_etsmem(EtsMem).
+ets_info(Tab, Item, SlavePid, _Line) ->
+ R = ets:info(Tab, Item),
+ %%io:format("~p: ets:info(~p) -> ~p\n", [_Line, Item, R]),
+ SlavePid ! {self(), Item},
+ {SlavePid, Item, R} = receive M -> M end,
+ R.
+
+
+
+info_binary_stress(_Config) ->
+ repeat_for_opts(fun info_binary_stress_do/1,
+ [[set,bag,duplicate_bag,ordered_set],
+ compressed]).
+
+info_binary_stress_do(Opts) ->
+ Tab = ets_new(info_binary_stress, [public, {write_concurrency,true} | Opts]),
+
+ KeyRange = 1000,
+ ValueRange = 3,
+ RefcBin = list_to_binary(lists:seq(1,100)),
+ InitF = fun (_) -> #{insert => 0, delete => 0, delete_object => 0}
+ end,
+ ExecF = fun (Counters) ->
+ Key = rand:uniform(KeyRange),
+ Value = rand:uniform(ValueRange),
+ Op = element(rand:uniform(4),{insert,insert,delete,delete_object}),
+ case Op of
+ insert ->
+ ets:insert(Tab, {Key,Value,RefcBin});
+ delete ->
+ ets:delete(Tab, Key);
+ delete_object ->
+ ets:delete_object(Tab, {Key,Value,RefcBin})
+ end,
+ Acc = incr_counter(Op, Counters),
+
+ receive stop ->
+ [end_of_work | Acc]
+ after 0 ->
+ Acc
+ end
+ end,
+ FiniF = fun (Acc) -> Acc end,
+ Pids = run_sched_workers(InitF, ExecF, FiniF, infinite),
+ timer:send_after(500, stop),
+
+ Rounds = fun Loop(N, Fix) ->
+ ets:info(Tab, binary),
+ ets:safe_fixtable(Tab, Fix),
+ receive
+ stop ->
+ ets:safe_fixtable(Tab, false),
+ false = ets:info(Tab, fixed),
+ N
+ after 0 ->
+ Loop(N+1, not Fix)
+ end
+ end (1, true),
+ [P ! stop || P <- Pids],
+ Results = wait_pids(Pids),
+ Size = ets:info(Tab,size),
+ io:format("Ops = ~p\n", [maps_sum(Results)]),
+ io:format("Size = ~p\n", [Size]),
+ io:format("Stats = ~p\n", [ets:info(Tab,stats)]),
+ io:format("Rounds = ~p\n", [Rounds]),
+ Size = length(ets:info(Tab, binary)),
+
+ ets:delete_all_objects(Tab),
+ [] = ets:info(Tab, binary),
+ true = ets:delete(Tab),
+ ok.
+
+
size_loop(_T, 0, _, _) ->
ok;
size_loop(T, I, PrevSize, WhatToTest) ->
@@ -4984,7 +5101,7 @@ tabfile_ext4(Config) when is_list(Config) ->
{error,Y} = ets:file2tab(FName,[{verify,true}]),
ets:tab2file(TL,FName,[{extended_info,[md5sum]}]),
{X,Y}
- end || N <- lists:seq(500,600)],
+ end || N <- lists:seq(700,800)],
io:format("~p~n",[Res]),
file:delete(FName)
end),
@@ -8144,7 +8261,7 @@ repeat_for_opts_atom2list(all_non_stim_types) -> [set,ordered_set,cat_ord_set,ba
repeat_for_opts_atom2list(all_non_stim_set_types) -> [set,ordered_set,cat_ord_set];
repeat_for_opts_atom2list(write_concurrency) -> [{write_concurrency,false},{write_concurrency,true}];
repeat_for_opts_atom2list(read_concurrency) -> [{read_concurrency,false},{read_concurrency,true}];
-repeat_for_opts_atom2list(compressed) -> [compressed,void].
+repeat_for_opts_atom2list(compressed) -> [void,compressed].
is_redundant_opts_combo(Opts) ->
(lists:member(stim_cat_ord_set, Opts) orelse
diff --git a/lib/stdlib/test/filelib_SUITE.erl b/lib/stdlib/test/filelib_SUITE.erl
index 7403d52881..527d083eaa 100644
--- a/lib/stdlib/test/filelib_SUITE.erl
+++ b/lib/stdlib/test/filelib_SUITE.erl
@@ -79,9 +79,11 @@ wildcard_one(Config) when is_list(Config) ->
do_wildcard_1(Dir,
fun(Wc) ->
L = filelib:wildcard(Wc),
+ L = filelib:wildcard(disable_prefix_opt(Wc)),
L = filelib:wildcard(Wc, erl_prim_loader),
L = filelib:wildcard(Wc, "."),
L = filelib:wildcard(Wc, Dir),
+ L = filelib:wildcard(disable_prefix_opt(Wc), Dir),
L = filelib:wildcard(Wc, Dir++"/.")
end),
file:set_cwd(OldCwd),
@@ -119,6 +121,14 @@ wcc(Wc, Error) ->
{'EXIT',{{badpattern,Error},
[{filelib,wildcard,2,_}|_]}} = (catch filelib:wildcard(Wc, ".")).
+disable_prefix_opt([C|Wc]) when $a =< C, C =< $z; C =:= $@ ->
+ %% There is an optimization for patterns that have a literal prefix
+ %% (such as "lib/compiler/ebin/*"). Test that we'll get the same result
+ %% if we disable that optimization.
+ [$[, C, $] | Wc];
+disable_prefix_opt(Wc) ->
+ Wc.
+
do_wildcard_1(Dir, Wcf0) ->
do_wildcard_2(Dir, Wcf0),
Wcf = fun(Wc0) ->
@@ -300,6 +310,30 @@ do_wildcard_10(Dir, Wcf) ->
end,
del(Files),
+ wildcard_11(Dir, Wcf).
+
+%% ERL-ERL-1029/OTP-15987: Fix problems with "@/.." and ".." in general.
+wildcard_11(Dir, Wcf) ->
+ Dirs0 = ["@","@dir","dir@"],
+ Dirs = [filename:join(Dir, D) || D <- Dirs0],
+ _ = [ok = file:make_dir(D) || D <- Dirs],
+ Files0 = ["@a","b@","x","y","z"],
+ Files = mkfiles(Files0, Dir),
+
+ ["@","@a","@dir","b@","dir@","x","y","z"] = Wcf("*"),
+ ["@"] = Wcf("@"),
+ ["@","@a","@dir"] = Wcf("@*"),
+ ["@/..","@dir/.."] = Wcf("@*/.."),
+ ["@/../@","@/../@a","@/../@dir",
+ "@dir/../@","@dir/../@a","@dir/../@dir"] = Wcf("@*/../@*"),
+
+ %% Non-directories followed by "/.." should not match any files.
+ [] = Wcf("@a/.."),
+ [] = Wcf("x/.."),
+
+ %% Cleanup.
+ del(Files),
+ [ok = file:del_dir(D) || D <- Dirs],
ok.
fold_files(Config) when is_list(Config) ->
diff --git a/lib/stdlib/test/gen_statem_SUITE.erl b/lib/stdlib/test/gen_statem_SUITE.erl
index 16cf8f43f9..aa4d258cbf 100644
--- a/lib/stdlib/test/gen_statem_SUITE.erl
+++ b/lib/stdlib/test/gen_statem_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2016-2018. All Rights Reserved.
+%% Copyright Ericsson AB 2016-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -28,7 +28,7 @@
suite() ->
[{ct_hooks,[ts_install_cth]},
- {timetrap,{minutes,1}}].
+ {timetrap,{seconds,10}}].
all() ->
[{group, start},
@@ -38,7 +38,8 @@ all() ->
{group, abnormal},
{group, abnormal_handle_event},
shutdown, stop_and_reply, state_enter, event_order,
- state_timeout, event_types, generic_timers, code_change,
+ state_timeout, timeout_cancel_and_update,
+ event_types, generic_timers, code_change,
{group, sys},
hibernate, auto_hibernate, enter_loop, {group, undef_callbacks},
undef_in_terminate].
@@ -518,10 +519,11 @@ abnormal2(Config) ->
?MODULE, start_arg(Config, []), [{debug,[log]}]),
%% bad return value in the gen_statem loop
- {{{bad_return_from_state_function,badreturn},_},_} =
+ Cause = bad_return_from_state_function,
+ {{{Cause,badreturn},_},_} =
?EXPECT_FAILURE(gen_statem:call(Pid, badreturn), Reason),
receive
- {'EXIT',Pid,{{bad_return_from_state_function,badreturn},_}} -> ok
+ {'EXIT',Pid,{{Cause,badreturn},_}} -> ok
after 5000 ->
ct:fail(gen_statem_did_not_die)
end,
@@ -538,10 +540,11 @@ abnormal3(Config) ->
?MODULE, start_arg(Config, []), [{debug,[log]}]),
%% bad return value in the gen_statem loop
- {{{bad_action_from_state_function,badaction},_},_} =
+ Cause = bad_action_from_state_function,
+ {{{Cause,badaction},_},_} =
?EXPECT_FAILURE(gen_statem:call(Pid, badaction), Reason),
receive
- {'EXIT',Pid,{{bad_action_from_state_function,badaction},_}} -> ok
+ {'EXIT',Pid,{{Cause,badaction},_}} -> ok
after 5000 ->
ct:fail(gen_statem_did_not_die)
end,
@@ -559,10 +562,11 @@ abnormal4(Config) ->
%% bad return value in the gen_statem loop
BadTimeout = {badtimeout,4711,ouch},
- {{{bad_action_from_state_function,BadTimeout},_},_} =
- ?EXPECT_FAILURE(gen_statem:call(Pid, BadTimeout), Reason),
+ Cause = bad_action_from_state_function,
+ {{{Cause,BadTimeout},_},_} =
+ ?EXPECT_FAILURE(gen_statem:call(Pid, {badtimeout,BadTimeout}), Reason),
receive
- {'EXIT',Pid,{{bad_action_from_state_function,BadTimeout},_}} -> ok
+ {'EXIT',Pid,{{Cause,BadTimeout},_}} -> ok
after 5000 ->
ct:fail(gen_statem_did_not_die)
end,
@@ -822,7 +826,8 @@ state_timeout(_Config) ->
self() ! message_to_self,
{next_state, state1, {Time,From},
%% Verify that internal events goes before external
- [{state_timeout,Time,1},
+ [{timeout,Time,1}, % Exercise different cancel code path
+ {state_timeout,Time,1},
{next_event,internal,1}]}
end,
state1 =>
@@ -872,8 +877,9 @@ state_timeout(_Config) ->
{reply,From,ok}}
end},
- {ok,STM} = gen_statem:start_link(?MODULE, {map_statem,Machine,[]}, []),
- sys:trace(STM, true),
+ {ok,STM} =
+ gen_statem:start_link(
+ ?MODULE, {map_statem,Machine,[]}, [{debug,[trace]}]),
TRef = erlang:start_timer(1000, self(), kull),
ok = gen_statem:call(STM, {go,500}),
ok = gen_statem:call(STM, check),
@@ -899,6 +905,88 @@ state_timeout(_Config) ->
+timeout_cancel_and_update(_Config) ->
+ process_flag(trap_exit, true),
+ %%
+ Machine =
+ #{init =>
+ fun () ->
+ {ok,start,0}
+ end,
+ start =>
+ fun
+ ({call,From}, test, 0) ->
+ self() ! message_to_self,
+ {next_state, state1, From,
+ %% Verify that internal events goes before external
+ [{state_timeout,17,1},
+ {next_event,internal,1}]}
+ end,
+ state1 =>
+ fun
+ (internal, 1, _) ->
+ {keep_state_and_data,
+ [{state_timeout,cancel},
+ {{timeout,a},17,1}]};
+ (info, message_to_self, _) ->
+ {keep_state_and_data,
+ [{{timeout,a},update,a}]};
+ ({timeout,a}, a, Data) ->
+ {next_state,state2,Data,
+ [{state_timeout,17,2},
+ {next_event,internal,2}]}
+ end,
+ state2 =>
+ fun
+ (internal, 2, _) ->
+ receive after 50 -> ok end,
+ %% Now state_timeout 17 should have triggered
+ {keep_state_and_data,
+ [{state_timeout,update,b},
+ {timeout,17,2}]};
+ (state_timeout, b, From) ->
+ {next_state,state3,3,
+ [{reply,From,ok},
+ 17000]}
+ end,
+ state3 =>
+ fun
+ ({call,From}, stop, 3) ->
+ {stop_and_reply, normal,
+ [{reply,From,ok}]}
+ end
+ },
+ %%
+ {ok,STM} =
+ gen_statem:start_link(
+ ?MODULE, {map_statem,Machine,[]}, [{debug,[trace]}]),
+ ok = gen_statem:call(STM, test),
+ {status, STM, {module,gen_statem}, Info} = sys:get_status(STM),
+ ct:log("Status info: ~p~n", [Info]),
+ {_,Timeouts} = dig_data_tuple(Info),
+ {_, {1,[{timeout,17000}]}} = lists:keyfind("Time-outs", 1, Timeouts),
+ %%
+ ok = gen_statem:call(STM, stop),
+ receive
+ {'EXIT',STM,normal} ->
+ ok
+ after 500 ->
+ ct:fail(did_not_stop)
+ end,
+ %%
+ verify_empty_msgq().
+
+dig_data_tuple([{data,_} = DataTuple|_]) -> DataTuple;
+dig_data_tuple([H|T]) when is_list(H) ->
+ case dig_data_tuple(H) of
+ false -> dig_data_tuple(T);
+ DataTuple -> DataTuple
+ end;
+dig_data_tuple([_|T]) -> dig_data_tuple(T);
+dig_data_tuple([]) -> false.
+
+
+
%% Test that all event types can be sent with {next_event,EventType,_}
event_types(_Config) ->
process_flag(trap_exit, true),
@@ -1042,7 +1130,8 @@ generic_timers(_Config) ->
sys1(Config) ->
{ok,Pid} = gen_statem:start(?MODULE, start_arg(Config, []), []),
- {status, Pid, {module,gen_statem}, _} = sys:get_status(Pid),
+ {status, Pid, {module,gen_statem}, Info} = sys:get_status(Pid),
+ ct:log("Status info: ~p~n", [Info]),
sys:suspend(Pid),
Parent = self(),
Tag = make_ref(),
@@ -1892,7 +1981,7 @@ idle({call,_From}, badreturn, _Data) ->
badreturn;
idle({call,_From}, badaction, Data) ->
{keep_state, Data, [badaction]};
-idle({call,_From}, {badtimeout,_,_} = BadTimeout, Data) ->
+idle({call,_From}, {badtimeout,BadTimeout}, Data) ->
{keep_state, Data, BadTimeout};
idle({call,From}, {delayed_answer,T}, Data) ->
receive
diff --git a/lib/stdlib/test/io_proto_SUITE.erl b/lib/stdlib/test/io_proto_SUITE.erl
index e497b2fb5d..df6958cfa9 100644
--- a/lib/stdlib/test/io_proto_SUITE.erl
+++ b/lib/stdlib/test/io_proto_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2009-2017. All Rights Reserved.
+%% Copyright Ericsson AB 2009-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -1568,10 +1568,6 @@ request({put_chars, Encoding, Chars}, State) ->
request({put_chars, Encoding, Module, Function, Args}, State) ->
{ok, ok, State#state{q=[{put_chars, Encoding, Module, Function, Args} |
State#state.q ]}};
-request({put_chars,Chars}, State) ->
- {ok, ok, State#state{q=[{put_chars, Chars} | State#state.q ]}};
-request({put_chars,M,F,As}, State) ->
- {ok, ok, State#state{q=[{put_chars, M,F,As} | State#state.q ]}};
request({get_until, Encoding, Prompt, M, F, As}, State) ->
{ok, convert(State#state.nxt, Encoding, State#state.mode), State#state{nxt = eof, q = [{get_until, Encoding, Prompt, M, F, As} | State#state.q]}};
request({get_chars, Encoding, Prompt, N}, State) ->
@@ -1583,20 +1579,6 @@ request({get_line, Encoding, Prompt}, State) ->
State#state{nxt = eof,
q = [{get_line, Encoding, Prompt} |
State#state.q]}};
-request({get_until, Prompt, M, F, As}, State) ->
- {ok, convert(State#state.nxt, latin1, State#state.mode),
- State#state{nxt = eof,
- q = [{get_until, Prompt, M, F, As} | State#state.q]}};
-request({get_chars, Prompt, N}, State) ->
- {ok, convert(State#state.nxt, latin1, State#state.mode),
- State#state{nxt = eof,
- q = [{get_chars, Prompt, N} |
- State#state.q]}};
-request({get_line, Prompt}, State) ->
- {ok, convert(State#state.nxt, latin1, State#state.mode),
- State#state{nxt = eof,
- q = [{get_line, Prompt} |
- State#state.q]}};
request({get_geomentry,_}, State) ->
{error, {error,enotsup}, State};
request({setopts, Opts}, State) when Opts =:= [{binary, false}]; Opts =:= [list] ->
diff --git a/lib/stdlib/test/ms_transform_SUITE.erl b/lib/stdlib/test/ms_transform_SUITE.erl
index d1e6faf863..29423ed032 100644
--- a/lib/stdlib/test/ms_transform_SUITE.erl
+++ b/lib/stdlib/test/ms_transform_SUITE.erl
@@ -281,6 +281,8 @@ basic_ets(Config) when is_list(Config) ->
compile_and_run(<<"ets:fun2ms(fun({A,B}) -> {B,A} end)">>),
[{{'$1','$2'},[],[['$2','$1']]}] =
compile_and_run(<<"ets:fun2ms(fun({A,B}) -> [B,A] end)">>),
+ [{{"foo" ++ '_','$1'},[],['$1']}] =
+ compile_and_run(<<"ets:fun2ms(fun({\"foo\" ++ _, X}) -> X end)">>),
ok.
%% Tests basic ets:fun2ms.
@@ -313,6 +315,8 @@ from_shell(Config) when is_list(Config) ->
[{[a,b],[],[{message,banan},{return_trace}]}] =
do_eval(
"dbg:fun2ms(fun([a,b]) -> message(banan), return_trace() end)"),
+ [{{"foo" ++ '_','$1'},[],['$1']}] =
+ do_eval("ets:fun2ms(fun({\"foo\" ++ _, X}) -> X end)"),
ok.
%% Tests expansion of records in fun2ms.
diff --git a/lib/stdlib/test/rand_SUITE.erl b/lib/stdlib/test/rand_SUITE.erl
index 7685c17967..b3d8a7e5d3 100644
--- a/lib/stdlib/test/rand_SUITE.erl
+++ b/lib/stdlib/test/rand_SUITE.erl
@@ -1257,12 +1257,17 @@ short_jump(Config) when is_list(Config) ->
fun ({Alg,AlgState}) ->
{Alg,rand:exro928_jump_2pow20(AlgState)}
end),
- short_jump(
- crypto:rand_seed_alg_s(crypto_aes, integer_to_list(Seed)),
- fun ({Alg,AlgState}) ->
- {Alg,crypto:rand_plugin_aes_jump_2pow20(AlgState)}
- end),
- ok.
+ try crypto:strong_rand_bytes(1) of
+ _ ->
+ short_jump(
+ crypto:rand_seed_alg_s(crypto_aes, integer_to_list(Seed)),
+ fun ({Alg,AlgState}) ->
+ {Alg,crypto:rand_plugin_aes_jump_2pow20(AlgState)}
+ end),
+ ok
+ catch error:undef ->
+ {skip,no_crypto}
+ end.
short_jump({#{bits := Bits},_} = State_0, Jump2Pow20) ->
Range = 1 bsl Bits,
diff --git a/lib/stdlib/test/shell_SUITE.erl b/lib/stdlib/test/shell_SUITE.erl
index cdb6031b07..4d85e1f04b 100644
--- a/lib/stdlib/test/shell_SUITE.erl
+++ b/lib/stdlib/test/shell_SUITE.erl
@@ -3141,25 +3141,16 @@ io_request({get_geometry,columns}, S) ->
{ok,80,S};
io_request({get_geometry,rows}, S) ->
{ok,24,S};
-io_request({put_chars,Chars}, S) ->
- {ok,ok,S#state{reply = [S#state.reply | Chars]}};
io_request({put_chars,latin1,Chars}, S) ->
{ok,ok,S#state{reply = [S#state.reply | Chars]}};
io_request({put_chars,unicode,Chars0}, S) ->
Chars = unicode:characters_to_list(Chars0),
{ok,ok,S#state{reply = [S#state.reply | Chars]}};
-io_request({put_chars,Mod,Func,Args}, S) ->
- case catch apply(Mod, Func, Args) of
- Chars when is_list(Chars) ->
- io_request({put_chars,Chars}, S)
- end;
io_request({put_chars,Enc,Mod,Func,Args}, S) ->
case catch apply(Mod, Func, Args) of
Chars when is_list(Chars) ->
io_request({put_chars,Enc,Chars}, S)
end;
-io_request({get_until,_Prompt,Mod,Func,ExtraArgs}, S) ->
- get_until(Mod, Func, ExtraArgs, S, latin1);
io_request({get_until,Enc,_Prompt,Mod,Func,ExtraArgs}, S) ->
get_until(Mod, Func, ExtraArgs, S, Enc).
diff --git a/lib/stdlib/test/supervisor_SUITE.erl b/lib/stdlib/test/supervisor_SUITE.erl
index 9370067910..36751c641b 100644
--- a/lib/stdlib/test/supervisor_SUITE.erl
+++ b/lib/stdlib/test/supervisor_SUITE.erl
@@ -2380,10 +2380,10 @@ scale_start_stop_many_children() ->
[N2, StartT2 div 1000, StopT2 div 1000]),
%% Scaling should be more or less linear, but allowing a bit more
- %% to avoid false alarms
+ %% to avoid false alarms (add 1 to avoid div zero)
ScaleLimit = (N2 div N1) * 10,
- StartScale = StartT2 div StartT1,
- StopScale = StopT2 div StopT1,
+ StartScale = StartT2 div (StartT1+1),
+ StopScale = StopT2 div (StopT1+1),
ct:log("Scale limit: ~w~nStart scale: ~w~nStop scale: ~w",
[ScaleLimit, StartScale, StopScale]),
diff --git a/lib/stdlib/test/unicode_util_SUITE_data/GraphemeBreakTest.txt b/lib/stdlib/test/unicode_util_SUITE_data/GraphemeBreakTest.txt
index 6847953c23..fb4fec9fff 100644
--- a/lib/stdlib/test/unicode_util_SUITE_data/GraphemeBreakTest.txt
+++ b/lib/stdlib/test/unicode_util_SUITE_data/GraphemeBreakTest.txt
@@ -1,6 +1,6 @@
-# GraphemeBreakTest-11.0.0.txt
-# Date: 2018-03-18, 13:30:33 GMT
-# © 2018 Unicode®, Inc.
+# GraphemeBreakTest-12.1.0.txt
+# Date: 2019-03-10, 10:53:12 GMT
+# © 2019 Unicode®, Inc.
# Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
# For terms of use, see http://www.unicode.org/terms_of_use.html
#
@@ -56,8 +56,6 @@
÷ 0020 × 0308 × 200D ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
÷ 0020 ÷ 0378 ÷ # ÷ [0.2] SPACE (Other) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
÷ 0020 × 0308 ÷ 0378 ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 0020 ÷ D800 ÷ # ÷ [0.2] SPACE (Other) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ 0020 × 0308 ÷ D800 ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
÷ 000D ÷ 0020 ÷ # ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] SPACE (Other) ÷ [0.3]
÷ 000D ÷ 0308 ÷ 0020 ÷ # ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 000D ÷ 000D ÷ # ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
@@ -92,8 +90,6 @@
÷ 000D ÷ 0308 × 200D ÷ # ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
÷ 000D ÷ 0378 ÷ # ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] <reserved-0378> (Other) ÷ [0.3]
÷ 000D ÷ 0308 ÷ 0378 ÷ # ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 000D ÷ D800 ÷ # ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ 000D ÷ 0308 ÷ D800 ÷ # ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
÷ 000A ÷ 0020 ÷ # ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] SPACE (Other) ÷ [0.3]
÷ 000A ÷ 0308 ÷ 0020 ÷ # ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 000A ÷ 000D ÷ # ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
@@ -128,8 +124,6 @@
÷ 000A ÷ 0308 × 200D ÷ # ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
÷ 000A ÷ 0378 ÷ # ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] <reserved-0378> (Other) ÷ [0.3]
÷ 000A ÷ 0308 ÷ 0378 ÷ # ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 000A ÷ D800 ÷ # ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ 000A ÷ 0308 ÷ D800 ÷ # ÷ [0.2] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
÷ 0001 ÷ 0020 ÷ # ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] SPACE (Other) ÷ [0.3]
÷ 0001 ÷ 0308 ÷ 0020 ÷ # ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 0001 ÷ 000D ÷ # ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
@@ -164,8 +158,6 @@
÷ 0001 ÷ 0308 × 200D ÷ # ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
÷ 0001 ÷ 0378 ÷ # ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] <reserved-0378> (Other) ÷ [0.3]
÷ 0001 ÷ 0308 ÷ 0378 ÷ # ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 0001 ÷ D800 ÷ # ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ 0001 ÷ 0308 ÷ D800 ÷ # ÷ [0.2] <START OF HEADING> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
÷ 034F ÷ 0020 ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 034F × 0308 ÷ 0020 ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 034F ÷ 000D ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
@@ -200,8 +192,6 @@
÷ 034F × 0308 × 200D ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
÷ 034F ÷ 0378 ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
÷ 034F × 0308 ÷ 0378 ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 034F ÷ D800 ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ 034F × 0308 ÷ D800 ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
÷ 1F1E6 ÷ 0020 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 1F1E6 × 0308 ÷ 0020 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 1F1E6 ÷ 000D ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
@@ -236,8 +226,6 @@
÷ 1F1E6 × 0308 × 200D ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
÷ 1F1E6 ÷ 0378 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
÷ 1F1E6 × 0308 ÷ 0378 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 1F1E6 ÷ D800 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ 1F1E6 × 0308 ÷ D800 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
÷ 0600 × 0020 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] SPACE (Other) ÷ [0.3]
÷ 0600 × 0308 ÷ 0020 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 0600 ÷ 000D ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
@@ -272,8 +260,6 @@
÷ 0600 × 0308 × 200D ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
÷ 0600 × 0378 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] <reserved-0378> (Other) ÷ [0.3]
÷ 0600 × 0308 ÷ 0378 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 0600 ÷ D800 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ 0600 × 0308 ÷ D800 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
÷ 0903 ÷ 0020 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 0903 × 0308 ÷ 0020 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 0903 ÷ 000D ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
@@ -308,8 +294,6 @@
÷ 0903 × 0308 × 200D ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
÷ 0903 ÷ 0378 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
÷ 0903 × 0308 ÷ 0378 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 0903 ÷ D800 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ 0903 × 0308 ÷ D800 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
÷ 1100 ÷ 0020 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 1100 × 0308 ÷ 0020 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 1100 ÷ 000D ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
@@ -344,8 +328,6 @@
÷ 1100 × 0308 × 200D ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
÷ 1100 ÷ 0378 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
÷ 1100 × 0308 ÷ 0378 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 1100 ÷ D800 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ 1100 × 0308 ÷ D800 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
÷ 1160 ÷ 0020 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 1160 × 0308 ÷ 0020 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 1160 ÷ 000D ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
@@ -380,8 +362,6 @@
÷ 1160 × 0308 × 200D ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
÷ 1160 ÷ 0378 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
÷ 1160 × 0308 ÷ 0378 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 1160 ÷ D800 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ 1160 × 0308 ÷ D800 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
÷ 11A8 ÷ 0020 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 11A8 × 0308 ÷ 0020 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 11A8 ÷ 000D ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
@@ -416,8 +396,6 @@
÷ 11A8 × 0308 × 200D ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
÷ 11A8 ÷ 0378 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
÷ 11A8 × 0308 ÷ 0378 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 11A8 ÷ D800 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ 11A8 × 0308 ÷ D800 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
÷ AC00 ÷ 0020 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ AC00 × 0308 ÷ 0020 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ AC00 ÷ 000D ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
@@ -452,8 +430,6 @@
÷ AC00 × 0308 × 200D ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
÷ AC00 ÷ 0378 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
÷ AC00 × 0308 ÷ 0378 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ AC00 ÷ D800 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ AC00 × 0308 ÷ D800 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
÷ AC01 ÷ 0020 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ AC01 × 0308 ÷ 0020 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ AC01 ÷ 000D ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
@@ -488,8 +464,6 @@
÷ AC01 × 0308 × 200D ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
÷ AC01 ÷ 0378 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
÷ AC01 × 0308 ÷ 0378 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ AC01 ÷ D800 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ AC01 × 0308 ÷ D800 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
÷ 231A ÷ 0020 ÷ # ÷ [0.2] WATCH (ExtPict) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 231A × 0308 ÷ 0020 ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 231A ÷ 000D ÷ # ÷ [0.2] WATCH (ExtPict) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
@@ -524,8 +498,6 @@
÷ 231A × 0308 × 200D ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
÷ 231A ÷ 0378 ÷ # ÷ [0.2] WATCH (ExtPict) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
÷ 231A × 0308 ÷ 0378 ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 231A ÷ D800 ÷ # ÷ [0.2] WATCH (ExtPict) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ 231A × 0308 ÷ D800 ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
÷ 0300 ÷ 0020 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 0300 × 0308 ÷ 0020 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 0300 ÷ 000D ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
@@ -560,8 +532,6 @@
÷ 0300 × 0308 × 200D ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
÷ 0300 ÷ 0378 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
÷ 0300 × 0308 ÷ 0378 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 0300 ÷ D800 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ 0300 × 0308 ÷ D800 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
÷ 200D ÷ 0020 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 200D × 0308 ÷ 0020 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 200D ÷ 000D ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
@@ -596,8 +566,6 @@
÷ 200D × 0308 × 200D ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
÷ 200D ÷ 0378 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
÷ 200D × 0308 ÷ 0378 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 200D ÷ D800 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ 200D × 0308 ÷ D800 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
÷ 0378 ÷ 0020 ÷ # ÷ [0.2] <reserved-0378> (Other) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 0378 × 0308 ÷ 0020 ÷ # ÷ [0.2] <reserved-0378> (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
÷ 0378 ÷ 000D ÷ # ÷ [0.2] <reserved-0378> (Other) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
@@ -632,44 +600,6 @@
÷ 0378 × 0308 × 200D ÷ # ÷ [0.2] <reserved-0378> (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
÷ 0378 ÷ 0378 ÷ # ÷ [0.2] <reserved-0378> (Other) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
÷ 0378 × 0308 ÷ 0378 ÷ # ÷ [0.2] <reserved-0378> (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ 0378 ÷ D800 ÷ # ÷ [0.2] <reserved-0378> (Other) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ 0378 × 0308 ÷ D800 ÷ # ÷ [0.2] <reserved-0378> (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ D800 ÷ 0020 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] SPACE (Other) ÷ [0.3]
-÷ D800 ÷ 0308 ÷ 0020 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3]
-÷ D800 ÷ 000D ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
-÷ D800 ÷ 0308 ÷ 000D ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <CARRIAGE RETURN (CR)> (CR) ÷ [0.3]
-÷ D800 ÷ 000A ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ D800 ÷ 0308 ÷ 000A ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [0.3]
-÷ D800 ÷ 0001 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ D800 ÷ 0308 ÷ 0001 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <START OF HEADING> (Control) ÷ [0.3]
-÷ D800 ÷ 034F ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]
-÷ D800 ÷ 0308 × 034F ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3]
-÷ D800 ÷ 1F1E6 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ D800 ÷ 0308 ÷ 1F1E6 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3]
-÷ D800 ÷ 0600 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ D800 ÷ 0308 ÷ 0600 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3]
-÷ D800 ÷ 0903 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]
-÷ D800 ÷ 0308 × 0903 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3]
-÷ D800 ÷ 1100 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
-÷ D800 ÷ 0308 ÷ 1100 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3]
-÷ D800 ÷ 1160 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
-÷ D800 ÷ 0308 ÷ 1160 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3]
-÷ D800 ÷ 11A8 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
-÷ D800 ÷ 0308 ÷ 11A8 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3]
-÷ D800 ÷ AC00 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
-÷ D800 ÷ 0308 ÷ AC00 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3]
-÷ D800 ÷ AC01 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ D800 ÷ 0308 ÷ AC01 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3]
-÷ D800 ÷ 231A ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] WATCH (ExtPict) ÷ [0.3]
-÷ D800 ÷ 0308 ÷ 231A ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3]
-÷ D800 ÷ 0300 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ D800 ÷ 0308 × 0300 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3]
-÷ D800 ÷ 200D ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ D800 ÷ 0308 × 200D ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3]
-÷ D800 ÷ 0378 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] <reserved-0378> (Other) ÷ [0.3]
-÷ D800 ÷ 0308 ÷ 0378 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] <reserved-0378> (Other) ÷ [0.3]
-÷ D800 ÷ D800 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] <surrogate-D800> (Control) ÷ [0.3]
-÷ D800 ÷ 0308 ÷ D800 ÷ # ÷ [0.2] <surrogate-D800> (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] <surrogate-D800> (Control) ÷ [0.3]
÷ 000D × 000A ÷ 0061 ÷ 000A ÷ 0308 ÷ # ÷ [0.2] <CARRIAGE RETURN (CR)> (CR) × [3.0] <LINE FEED (LF)> (LF) ÷ [4.0] LATIN SMALL LETTER A (Other) ÷ [5.0] <LINE FEED (LF)> (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [0.3]
÷ 0061 × 0308 ÷ # ÷ [0.2] LATIN SMALL LETTER A (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [0.3]
÷ 0020 × 200D ÷ 0646 ÷ # ÷ [0.2] SPACE (Other) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] ARABIC LETTER NOON (Other) ÷ [0.3]
@@ -695,6 +625,6 @@
÷ 2701 × 200D × 2701 ÷ # ÷ [0.2] UPPER BLADE SCISSORS (Other) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [11.0] UPPER BLADE SCISSORS (Other) ÷ [0.3]
÷ 0061 × 200D ÷ 2701 ÷ # ÷ [0.2] LATIN SMALL LETTER A (Other) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] UPPER BLADE SCISSORS (Other) ÷ [0.3]
#
-# Lines: 672
+# Lines: 602
#
# EOF
diff --git a/lib/stdlib/test/unicode_util_SUITE_data/LineBreakTest.txt b/lib/stdlib/test/unicode_util_SUITE_data/LineBreakTest.txt
index 0e9e678a85..eb056990a0 100644
--- a/lib/stdlib/test/unicode_util_SUITE_data/LineBreakTest.txt
+++ b/lib/stdlib/test/unicode_util_SUITE_data/LineBreakTest.txt
@@ -1,6 +1,6 @@
-# LineBreakTest-11.0.0.txt
-# Date: 2018-05-20, 09:03:09 GMT
-# © 2018 Unicode®, Inc.
+# LineBreakTest-12.1.0.txt
+# Date: 2019-03-10, 10:53:14 GMT
+# © 2019 Unicode®, Inc.
# Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
# For terms of use, see http://www.unicode.org/terms_of_use.html
#
diff --git a/lib/stdlib/test/unicode_util_SUITE_data/NormalizationTest.txt b/lib/stdlib/test/unicode_util_SUITE_data/NormalizationTest.txt
index 72a31bcdf1..54f43bdf4d 100644
--- a/lib/stdlib/test/unicode_util_SUITE_data/NormalizationTest.txt
+++ b/lib/stdlib/test/unicode_util_SUITE_data/NormalizationTest.txt
@@ -1,6 +1,6 @@
-# NormalizationTest-11.0.0.txt
-# Date: 2018-02-19, 18:33:08 GMT
-# © 2018 Unicode®, Inc.
+# NormalizationTest-12.1.0.txt
+# Date: 2019-04-01, 09:10:28 GMT
+# © 2019 Unicode®, Inc.
# Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
# For terms of use, see http://www.unicode.org/terms_of_use.html
#
@@ -2149,6 +2149,7 @@
32FC;32FC;32FC;30F0;30F0; # (㋼; ㋼; ㋼; ヰ; ヰ; ) CIRCLED KATAKANA WI
32FD;32FD;32FD;30F1;30F1; # (㋽; ㋽; ㋽; ヱ; ヱ; ) CIRCLED KATAKANA WE
32FE;32FE;32FE;30F2;30F2; # (㋾; ㋾; ㋾; ヲ; ヲ; ) CIRCLED KATAKANA WO
+32FF;32FF;32FF;4EE4 548C;4EE4 548C; # (㋿; ㋿; ㋿; 令和; 令和; ) SQUARE ERA NAME REIWA
3300;3300;3300;30A2 30D1 30FC 30C8;30A2 30CF 309A 30FC 30C8; # (㌀; ㌀; ㌀; アパート; アハ◌゚ート; ) SQUARE APAATO
3301;3301;3301;30A2 30EB 30D5 30A1;30A2 30EB 30D5 30A1; # (㌁; ㌁; ㌁; アルファ; アルファ; ) SQUARE ARUHUA
3302;3302;3302;30A2 30F3 30DA 30A2;30A2 30F3 30D8 309A 30A2; # (㌂; ㌂; ㌂; アンペア; アンヘ◌゚ア; ) SQUARE ANPEA
@@ -16363,6 +16364,7 @@ FFEE;FFEE;FFEE;25CB;25CB; # (○; ○; ○; ○; ○; ) HALFWIDTH WHITE CIRCLE
1F14F;1F14F;1F14F;0057 0043;0057 0043; # (🅏; 🅏; 🅏; WC; WC; ) SQUARED WC
1F16A;1F16A;1F16A;004D 0043;004D 0043; # (🅪; 🅪; 🅪; MC; MC; ) RAISED MC SIGN
1F16B;1F16B;1F16B;004D 0044;004D 0044; # (🅫; 🅫; 🅫; MD; MD; ) RAISED MD SIGN
+1F16C;1F16C;1F16C;004D 0052;004D 0052; # (🅬; 🅬; 🅬; MR; MR; ) RAISED MR SIGN
1F190;1F190;1F190;0044 004A;0044 004A; # (🆐; 🆐; 🆐; DJ; DJ; ) SQUARE DJ
1F200;1F200;1F200;307B 304B;307B 304B; # (🈀; 🈀; 🈀; ほか; ほか; ) SQUARE HIRAGANA HOKA
1F201;1F201;1F201;30B3 30B3;30B3 30B3; # (🈁; 🈁; 🈁; ココ; ココ; ) SQUARED KATAKANA KOKO
@@ -17685,6 +17687,8 @@ FFEE;FFEE;FFEE;25CB;25CB; # (○; ○; ○; ○; ○; ) HALFWIDTH WHITE CIRCLE
0061 0EB8 0EC8 0EB8 0E48 0062;0061 0E48 0EB8 0EB8 0EC8 0062;0061 0E48 0EB8 0EB8 0EC8 0062;0061 0E48 0EB8 0EB8 0EC8 0062;0061 0E48 0EB8 0EB8 0EC8 0062; # (a◌ຸ◌່◌ຸ◌่b; a◌่◌ຸ◌ຸ◌່b; a◌่◌ຸ◌ຸ◌່b; a◌่◌ຸ◌ຸ◌່b; a◌่◌ຸ◌ຸ◌່b; ) LATIN SMALL LETTER A, LAO VOWEL SIGN U, LAO TONE MAI EK, LAO VOWEL SIGN U, THAI CHARACTER MAI EK, LATIN SMALL LETTER B
0061 0EC8 0EB8 0E48 0EB9 0062;0061 0E48 0EB8 0EB9 0EC8 0062;0061 0E48 0EB8 0EB9 0EC8 0062;0061 0E48 0EB8 0EB9 0EC8 0062;0061 0E48 0EB8 0EB9 0EC8 0062; # (a◌່◌ຸ◌่◌ູb; a◌่◌ຸ◌ູ◌່b; a◌่◌ຸ◌ູ◌່b; a◌่◌ຸ◌ູ◌່b; a◌่◌ຸ◌ູ◌່b; ) LATIN SMALL LETTER A, LAO TONE MAI EK, LAO VOWEL SIGN U, THAI CHARACTER MAI EK, LAO VOWEL SIGN UU, LATIN SMALL LETTER B
0061 0EB9 0EC8 0EB8 0E48 0062;0061 0E48 0EB9 0EB8 0EC8 0062;0061 0E48 0EB9 0EB8 0EC8 0062;0061 0E48 0EB9 0EB8 0EC8 0062;0061 0E48 0EB9 0EB8 0EC8 0062; # (a◌ູ◌່◌ຸ◌่b; a◌่◌ູ◌ຸ◌່b; a◌่◌ູ◌ຸ◌່b; a◌่◌ູ◌ຸ◌່b; a◌่◌ູ◌ຸ◌່b; ) LATIN SMALL LETTER A, LAO VOWEL SIGN UU, LAO TONE MAI EK, LAO VOWEL SIGN U, THAI CHARACTER MAI EK, LATIN SMALL LETTER B
+0061 05B0 094D 3099 0EBA 0062;0061 3099 094D 0EBA 05B0 0062;0061 3099 094D 0EBA 05B0 0062;0061 3099 094D 0EBA 05B0 0062;0061 3099 094D 0EBA 05B0 0062; # (a◌ְ◌्◌゙◌຺b; a◌゙◌्◌຺◌ְb; a◌゙◌्◌຺◌ְb; a◌゙◌्◌຺◌ְb; a◌゙◌्◌຺◌ְb; ) LATIN SMALL LETTER A, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, LAO SIGN PALI VIRAMA, LATIN SMALL LETTER B
+0061 0EBA 05B0 094D 3099 0062;0061 3099 0EBA 094D 05B0 0062;0061 3099 0EBA 094D 05B0 0062;0061 3099 0EBA 094D 05B0 0062;0061 3099 0EBA 094D 05B0 0062; # (a◌຺◌ְ◌्◌゙b; a◌゙◌຺◌्◌ְb; a◌゙◌຺◌्◌ְb; a◌゙◌຺◌्◌ְb; a◌゙◌຺◌्◌ְb; ) LATIN SMALL LETTER A, LAO SIGN PALI VIRAMA, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, LATIN SMALL LETTER B
0061 0F71 0EC8 0EB8 0EC8 0062;0061 0EB8 0EC8 0EC8 0F71 0062;0061 0EB8 0EC8 0EC8 0F71 0062;0061 0EB8 0EC8 0EC8 0F71 0062;0061 0EB8 0EC8 0EC8 0F71 0062; # (a◌ཱ◌່◌ຸ◌່b; a◌ຸ◌່◌່◌ཱb; a◌ຸ◌່◌່◌ཱb; a◌ຸ◌່◌່◌ཱb; a◌ຸ◌່◌່◌ཱb; ) LATIN SMALL LETTER A, TIBETAN VOWEL SIGN AA, LAO TONE MAI EK, LAO VOWEL SIGN U, LAO TONE MAI EK, LATIN SMALL LETTER B
0061 0EC8 0F71 0EC8 0EB8 0062;0061 0EB8 0EC8 0EC8 0F71 0062;0061 0EB8 0EC8 0EC8 0F71 0062;0061 0EB8 0EC8 0EC8 0F71 0062;0061 0EB8 0EC8 0EC8 0F71 0062; # (a◌່◌ཱ◌່◌ຸb; a◌ຸ◌່◌່◌ཱb; a◌ຸ◌່◌່◌ཱb; a◌ຸ◌່◌່◌ཱb; a◌ຸ◌່◌່◌ཱb; ) LATIN SMALL LETTER A, LAO TONE MAI EK, TIBETAN VOWEL SIGN AA, LAO TONE MAI EK, LAO VOWEL SIGN U, LATIN SMALL LETTER B
0061 0F71 0EC8 0EB8 0EC9 0062;0061 0EB8 0EC8 0EC9 0F71 0062;0061 0EB8 0EC8 0EC9 0F71 0062;0061 0EB8 0EC8 0EC9 0F71 0062;0061 0EB8 0EC8 0EC9 0F71 0062; # (a◌ཱ◌່◌ຸ◌້b; a◌ຸ◌່◌້◌ཱb; a◌ຸ◌່◌້◌ཱb; a◌ຸ◌່◌້◌ཱb; a◌ຸ◌່◌້◌ཱb; ) LATIN SMALL LETTER A, TIBETAN VOWEL SIGN AA, LAO TONE MAI EK, LAO VOWEL SIGN U, LAO TONE MAI THO, LATIN SMALL LETTER B
@@ -18453,6 +18457,8 @@ FFEE;FFEE;FFEE;25CB;25CB; # (○; ○; ○; ○; ○; ) HALFWIDTH WHITE CIRCLE
0061 11839 05B0 094D 3099 0062;0061 3099 11839 094D 05B0 0062;0061 3099 11839 094D 05B0 0062;0061 3099 11839 094D 05B0 0062;0061 3099 11839 094D 05B0 0062; # (a◌𑠹◌ְ◌्◌゙b; a◌゙◌𑠹◌्◌ְb; a◌゙◌𑠹◌्◌ְb; a◌゙◌𑠹◌्◌ְb; a◌゙◌𑠹◌्◌ְb; ) LATIN SMALL LETTER A, DOGRA SIGN VIRAMA, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, LATIN SMALL LETTER B
0061 3099 093C 0334 1183A 0062;0061 0334 093C 1183A 3099 0062;0061 0334 093C 1183A 3099 0062;0061 0334 093C 1183A 3099 0062;0061 0334 093C 1183A 3099 0062; # (a◌゙◌़◌̴◌𑠺b; a◌̴◌़◌𑠺◌゙b; a◌̴◌़◌𑠺◌゙b; a◌̴◌़◌𑠺◌゙b; a◌̴◌़◌𑠺◌゙b; ) LATIN SMALL LETTER A, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, DOGRA SIGN NUKTA, LATIN SMALL LETTER B
0061 1183A 3099 093C 0334 0062;0061 0334 1183A 093C 3099 0062;0061 0334 1183A 093C 3099 0062;0061 0334 1183A 093C 3099 0062;0061 0334 1183A 093C 3099 0062; # (a◌𑠺◌゙◌़◌̴b; a◌̴◌𑠺◌़◌゙b; a◌̴◌𑠺◌़◌゙b; a◌̴◌𑠺◌़◌゙b; a◌̴◌𑠺◌़◌゙b; ) LATIN SMALL LETTER A, DOGRA SIGN NUKTA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, DEVANAGARI SIGN NUKTA, COMBINING TILDE OVERLAY, LATIN SMALL LETTER B
+0061 05B0 094D 3099 119E0 0062;0061 3099 094D 119E0 05B0 0062;0061 3099 094D 119E0 05B0 0062;0061 3099 094D 119E0 05B0 0062;0061 3099 094D 119E0 05B0 0062; # (a◌ְ◌्◌゙◌𑧠b; a◌゙◌्◌𑧠◌ְb; a◌゙◌्◌𑧠◌ְb; a◌゙◌्◌𑧠◌ְb; a◌゙◌्◌𑧠◌ְb; ) LATIN SMALL LETTER A, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, NANDINAGARI SIGN VIRAMA, LATIN SMALL LETTER B
+0061 119E0 05B0 094D 3099 0062;0061 3099 119E0 094D 05B0 0062;0061 3099 119E0 094D 05B0 0062;0061 3099 119E0 094D 05B0 0062;0061 3099 119E0 094D 05B0 0062; # (a◌𑧠◌ְ◌्◌゙b; a◌゙◌𑧠◌्◌ְb; a◌゙◌𑧠◌्◌ְb; a◌゙◌𑧠◌्◌ְb; a◌゙◌𑧠◌्◌ְb; ) LATIN SMALL LETTER A, NANDINAGARI SIGN VIRAMA, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, LATIN SMALL LETTER B
0061 05B0 094D 3099 11A34 0062;0061 3099 094D 11A34 05B0 0062;0061 3099 094D 11A34 05B0 0062;0061 3099 094D 11A34 05B0 0062;0061 3099 094D 11A34 05B0 0062; # (a◌ְ◌्◌゙◌𑨴b; a◌゙◌्◌𑨴◌ְb; a◌゙◌्◌𑨴◌ְb; a◌゙◌्◌𑨴◌ְb; a◌゙◌्◌𑨴◌ְb; ) LATIN SMALL LETTER A, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, ZANABAZAR SQUARE SIGN VIRAMA, LATIN SMALL LETTER B
0061 11A34 05B0 094D 3099 0062;0061 3099 11A34 094D 05B0 0062;0061 3099 11A34 094D 05B0 0062;0061 3099 11A34 094D 05B0 0062;0061 3099 11A34 094D 05B0 0062; # (a◌𑨴◌ְ◌्◌゙b; a◌゙◌𑨴◌्◌ְb; a◌゙◌𑨴◌्◌ְb; a◌゙◌𑨴◌्◌ְb; a◌゙◌𑨴◌्◌ְb; ) LATIN SMALL LETTER A, ZANABAZAR SQUARE SIGN VIRAMA, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, LATIN SMALL LETTER B
0061 05B0 094D 3099 11A47 0062;0061 3099 094D 11A47 05B0 0062;0061 3099 094D 11A47 05B0 0062;0061 3099 094D 11A47 05B0 0062;0061 3099 094D 11A47 05B0 0062; # (a◌ְ◌्◌゙◌𑩇b; a◌゙◌्◌𑩇◌ְb; a◌゙◌्◌𑩇◌ְb; a◌゙◌्◌𑩇◌ְb; a◌゙◌्◌𑩇◌ְb; ) LATIN SMALL LETTER A, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, ZANABAZAR SQUARE SUBJOINER, LATIN SMALL LETTER B
@@ -18637,6 +18643,28 @@ FFEE;FFEE;FFEE;25CB;25CB; # (○; ○; ○; ○; ○; ) HALFWIDTH WHITE CIRCLE
0061 1E029 0315 0300 05AE 0062;0061 05AE 1E029 0300 0315 0062;0061 05AE 1E029 0300 0315 0062;0061 05AE 1E029 0300 0315 0062;0061 05AE 1E029 0300 0315 0062; # (a◌𞀩◌̕◌̀◌֮b; a◌֮◌𞀩◌̀◌̕b; a◌֮◌𞀩◌̀◌̕b; a◌֮◌𞀩◌̀◌̕b; a◌֮◌𞀩◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING GLAGOLITIC LETTER IOTATED BIG YUS, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
0061 0315 0300 05AE 1E02A 0062;00E0 05AE 1E02A 0315 0062;0061 05AE 0300 1E02A 0315 0062;00E0 05AE 1E02A 0315 0062;0061 05AE 0300 1E02A 0315 0062; # (a◌̕◌̀◌֮◌𞀪b; à◌֮◌𞀪◌̕b; a◌֮◌̀◌𞀪◌̕b; à◌֮◌𞀪◌̕b; a◌֮◌̀◌𞀪◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, COMBINING GLAGOLITIC LETTER FITA, LATIN SMALL LETTER B
0061 1E02A 0315 0300 05AE 0062;0061 05AE 1E02A 0300 0315 0062;0061 05AE 1E02A 0300 0315 0062;0061 05AE 1E02A 0300 0315 0062;0061 05AE 1E02A 0300 0315 0062; # (a◌𞀪◌̕◌̀◌֮b; a◌֮◌𞀪◌̀◌̕b; a◌֮◌𞀪◌̀◌̕b; a◌֮◌𞀪◌̀◌̕b; a◌֮◌𞀪◌̀◌̕b; ) LATIN SMALL LETTER A, COMBINING GLAGOLITIC LETTER FITA, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 1E130 0062;00E0 05AE 1E130 0315 0062;0061 05AE 0300 1E130 0315 0062;00E0 05AE 1E130 0315 0062;0061 05AE 0300 1E130 0315 0062; # (a◌̕◌̀◌֮◌𞄰b; à◌֮◌𞄰◌̕b; a◌֮◌̀◌𞄰◌̕b; à◌֮◌𞄰◌̕b; a◌֮◌̀◌𞄰◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, NYIAKENG PUACHUE HMONG TONE-B, LATIN SMALL LETTER B
+0061 1E130 0315 0300 05AE 0062;0061 05AE 1E130 0300 0315 0062;0061 05AE 1E130 0300 0315 0062;0061 05AE 1E130 0300 0315 0062;0061 05AE 1E130 0300 0315 0062; # (a◌𞄰◌̕◌̀◌֮b; a◌֮◌𞄰◌̀◌̕b; a◌֮◌𞄰◌̀◌̕b; a◌֮◌𞄰◌̀◌̕b; a◌֮◌𞄰◌̀◌̕b; ) LATIN SMALL LETTER A, NYIAKENG PUACHUE HMONG TONE-B, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 1E131 0062;00E0 05AE 1E131 0315 0062;0061 05AE 0300 1E131 0315 0062;00E0 05AE 1E131 0315 0062;0061 05AE 0300 1E131 0315 0062; # (a◌̕◌̀◌֮◌𞄱b; à◌֮◌𞄱◌̕b; a◌֮◌̀◌𞄱◌̕b; à◌֮◌𞄱◌̕b; a◌֮◌̀◌𞄱◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, NYIAKENG PUACHUE HMONG TONE-M, LATIN SMALL LETTER B
+0061 1E131 0315 0300 05AE 0062;0061 05AE 1E131 0300 0315 0062;0061 05AE 1E131 0300 0315 0062;0061 05AE 1E131 0300 0315 0062;0061 05AE 1E131 0300 0315 0062; # (a◌𞄱◌̕◌̀◌֮b; a◌֮◌𞄱◌̀◌̕b; a◌֮◌𞄱◌̀◌̕b; a◌֮◌𞄱◌̀◌̕b; a◌֮◌𞄱◌̀◌̕b; ) LATIN SMALL LETTER A, NYIAKENG PUACHUE HMONG TONE-M, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 1E132 0062;00E0 05AE 1E132 0315 0062;0061 05AE 0300 1E132 0315 0062;00E0 05AE 1E132 0315 0062;0061 05AE 0300 1E132 0315 0062; # (a◌̕◌̀◌֮◌𞄲b; à◌֮◌𞄲◌̕b; a◌֮◌̀◌𞄲◌̕b; à◌֮◌𞄲◌̕b; a◌֮◌̀◌𞄲◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, NYIAKENG PUACHUE HMONG TONE-J, LATIN SMALL LETTER B
+0061 1E132 0315 0300 05AE 0062;0061 05AE 1E132 0300 0315 0062;0061 05AE 1E132 0300 0315 0062;0061 05AE 1E132 0300 0315 0062;0061 05AE 1E132 0300 0315 0062; # (a◌𞄲◌̕◌̀◌֮b; a◌֮◌𞄲◌̀◌̕b; a◌֮◌𞄲◌̀◌̕b; a◌֮◌𞄲◌̀◌̕b; a◌֮◌𞄲◌̀◌̕b; ) LATIN SMALL LETTER A, NYIAKENG PUACHUE HMONG TONE-J, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 1E133 0062;00E0 05AE 1E133 0315 0062;0061 05AE 0300 1E133 0315 0062;00E0 05AE 1E133 0315 0062;0061 05AE 0300 1E133 0315 0062; # (a◌̕◌̀◌֮◌𞄳b; à◌֮◌𞄳◌̕b; a◌֮◌̀◌𞄳◌̕b; à◌֮◌𞄳◌̕b; a◌֮◌̀◌𞄳◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, NYIAKENG PUACHUE HMONG TONE-V, LATIN SMALL LETTER B
+0061 1E133 0315 0300 05AE 0062;0061 05AE 1E133 0300 0315 0062;0061 05AE 1E133 0300 0315 0062;0061 05AE 1E133 0300 0315 0062;0061 05AE 1E133 0300 0315 0062; # (a◌𞄳◌̕◌̀◌֮b; a◌֮◌𞄳◌̀◌̕b; a◌֮◌𞄳◌̀◌̕b; a◌֮◌𞄳◌̀◌̕b; a◌֮◌𞄳◌̀◌̕b; ) LATIN SMALL LETTER A, NYIAKENG PUACHUE HMONG TONE-V, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 1E134 0062;00E0 05AE 1E134 0315 0062;0061 05AE 0300 1E134 0315 0062;00E0 05AE 1E134 0315 0062;0061 05AE 0300 1E134 0315 0062; # (a◌̕◌̀◌֮◌𞄴b; à◌֮◌𞄴◌̕b; a◌֮◌̀◌𞄴◌̕b; à◌֮◌𞄴◌̕b; a◌֮◌̀◌𞄴◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, NYIAKENG PUACHUE HMONG TONE-S, LATIN SMALL LETTER B
+0061 1E134 0315 0300 05AE 0062;0061 05AE 1E134 0300 0315 0062;0061 05AE 1E134 0300 0315 0062;0061 05AE 1E134 0300 0315 0062;0061 05AE 1E134 0300 0315 0062; # (a◌𞄴◌̕◌̀◌֮b; a◌֮◌𞄴◌̀◌̕b; a◌֮◌𞄴◌̀◌̕b; a◌֮◌𞄴◌̀◌̕b; a◌֮◌𞄴◌̀◌̕b; ) LATIN SMALL LETTER A, NYIAKENG PUACHUE HMONG TONE-S, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 1E135 0062;00E0 05AE 1E135 0315 0062;0061 05AE 0300 1E135 0315 0062;00E0 05AE 1E135 0315 0062;0061 05AE 0300 1E135 0315 0062; # (a◌̕◌̀◌֮◌𞄵b; à◌֮◌𞄵◌̕b; a◌֮◌̀◌𞄵◌̕b; à◌֮◌𞄵◌̕b; a◌֮◌̀◌𞄵◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, NYIAKENG PUACHUE HMONG TONE-G, LATIN SMALL LETTER B
+0061 1E135 0315 0300 05AE 0062;0061 05AE 1E135 0300 0315 0062;0061 05AE 1E135 0300 0315 0062;0061 05AE 1E135 0300 0315 0062;0061 05AE 1E135 0300 0315 0062; # (a◌𞄵◌̕◌̀◌֮b; a◌֮◌𞄵◌̀◌̕b; a◌֮◌𞄵◌̀◌̕b; a◌֮◌𞄵◌̀◌̕b; a◌֮◌𞄵◌̀◌̕b; ) LATIN SMALL LETTER A, NYIAKENG PUACHUE HMONG TONE-G, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 1E136 0062;00E0 05AE 1E136 0315 0062;0061 05AE 0300 1E136 0315 0062;00E0 05AE 1E136 0315 0062;0061 05AE 0300 1E136 0315 0062; # (a◌̕◌̀◌֮◌𞄶b; à◌֮◌𞄶◌̕b; a◌֮◌̀◌𞄶◌̕b; à◌֮◌𞄶◌̕b; a◌֮◌̀◌𞄶◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, NYIAKENG PUACHUE HMONG TONE-D, LATIN SMALL LETTER B
+0061 1E136 0315 0300 05AE 0062;0061 05AE 1E136 0300 0315 0062;0061 05AE 1E136 0300 0315 0062;0061 05AE 1E136 0300 0315 0062;0061 05AE 1E136 0300 0315 0062; # (a◌𞄶◌̕◌̀◌֮b; a◌֮◌𞄶◌̀◌̕b; a◌֮◌𞄶◌̀◌̕b; a◌֮◌𞄶◌̀◌̕b; a◌֮◌𞄶◌̀◌̕b; ) LATIN SMALL LETTER A, NYIAKENG PUACHUE HMONG TONE-D, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 1E2EC 0062;00E0 05AE 1E2EC 0315 0062;0061 05AE 0300 1E2EC 0315 0062;00E0 05AE 1E2EC 0315 0062;0061 05AE 0300 1E2EC 0315 0062; # (a◌̕◌̀◌֮◌𞋬b; à◌֮◌𞋬◌̕b; a◌֮◌̀◌𞋬◌̕b; à◌֮◌𞋬◌̕b; a◌֮◌̀◌𞋬◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, WANCHO TONE TUP, LATIN SMALL LETTER B
+0061 1E2EC 0315 0300 05AE 0062;0061 05AE 1E2EC 0300 0315 0062;0061 05AE 1E2EC 0300 0315 0062;0061 05AE 1E2EC 0300 0315 0062;0061 05AE 1E2EC 0300 0315 0062; # (a◌𞋬◌̕◌̀◌֮b; a◌֮◌𞋬◌̀◌̕b; a◌֮◌𞋬◌̀◌̕b; a◌֮◌𞋬◌̀◌̕b; a◌֮◌𞋬◌̀◌̕b; ) LATIN SMALL LETTER A, WANCHO TONE TUP, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 1E2ED 0062;00E0 05AE 1E2ED 0315 0062;0061 05AE 0300 1E2ED 0315 0062;00E0 05AE 1E2ED 0315 0062;0061 05AE 0300 1E2ED 0315 0062; # (a◌̕◌̀◌֮◌𞋭b; à◌֮◌𞋭◌̕b; a◌֮◌̀◌𞋭◌̕b; à◌֮◌𞋭◌̕b; a◌֮◌̀◌𞋭◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, WANCHO TONE TUPNI, LATIN SMALL LETTER B
+0061 1E2ED 0315 0300 05AE 0062;0061 05AE 1E2ED 0300 0315 0062;0061 05AE 1E2ED 0300 0315 0062;0061 05AE 1E2ED 0300 0315 0062;0061 05AE 1E2ED 0300 0315 0062; # (a◌𞋭◌̕◌̀◌֮b; a◌֮◌𞋭◌̀◌̕b; a◌֮◌𞋭◌̀◌̕b; a◌֮◌𞋭◌̀◌̕b; a◌֮◌𞋭◌̀◌̕b; ) LATIN SMALL LETTER A, WANCHO TONE TUPNI, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 1E2EE 0062;00E0 05AE 1E2EE 0315 0062;0061 05AE 0300 1E2EE 0315 0062;00E0 05AE 1E2EE 0315 0062;0061 05AE 0300 1E2EE 0315 0062; # (a◌̕◌̀◌֮◌𞋮b; à◌֮◌𞋮◌̕b; a◌֮◌̀◌𞋮◌̕b; à◌֮◌𞋮◌̕b; a◌֮◌̀◌𞋮◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, WANCHO TONE KOI, LATIN SMALL LETTER B
+0061 1E2EE 0315 0300 05AE 0062;0061 05AE 1E2EE 0300 0315 0062;0061 05AE 1E2EE 0300 0315 0062;0061 05AE 1E2EE 0300 0315 0062;0061 05AE 1E2EE 0300 0315 0062; # (a◌𞋮◌̕◌̀◌֮b; a◌֮◌𞋮◌̀◌̕b; a◌֮◌𞋮◌̀◌̕b; a◌֮◌𞋮◌̀◌̕b; a◌֮◌𞋮◌̀◌̕b; ) LATIN SMALL LETTER A, WANCHO TONE KOI, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 1E2EF 0062;00E0 05AE 1E2EF 0315 0062;0061 05AE 0300 1E2EF 0315 0062;00E0 05AE 1E2EF 0315 0062;0061 05AE 0300 1E2EF 0315 0062; # (a◌̕◌̀◌֮◌𞋯b; à◌֮◌𞋯◌̕b; a◌֮◌̀◌𞋯◌̕b; à◌֮◌𞋯◌̕b; a◌֮◌̀◌𞋯◌̕b; ) LATIN SMALL LETTER A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, WANCHO TONE KOINI, LATIN SMALL LETTER B
+0061 1E2EF 0315 0300 05AE 0062;0061 05AE 1E2EF 0300 0315 0062;0061 05AE 1E2EF 0300 0315 0062;0061 05AE 1E2EF 0300 0315 0062;0061 05AE 1E2EF 0300 0315 0062; # (a◌𞋯◌̕◌̀◌֮b; a◌֮◌𞋯◌̀◌̕b; a◌֮◌𞋯◌̀◌̕b; a◌֮◌𞋯◌̀◌̕b; a◌֮◌𞋯◌̀◌̕b; ) LATIN SMALL LETTER A, WANCHO TONE KOINI, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
0061 059A 0316 302A 1E8D0 0062;0061 302A 0316 1E8D0 059A 0062;0061 302A 0316 1E8D0 059A 0062;0061 302A 0316 1E8D0 059A 0062;0061 302A 0316 1E8D0 059A 0062; # (a◌֚◌̖◌〪◌𞣐b; a◌〪◌̖◌𞣐◌֚b; a◌〪◌̖◌𞣐◌֚b; a◌〪◌̖◌𞣐◌֚b; a◌〪◌̖◌𞣐◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, MENDE KIKAKUI COMBINING NUMBER TEENS, LATIN SMALL LETTER B
0061 1E8D0 059A 0316 302A 0062;0061 302A 1E8D0 0316 059A 0062;0061 302A 1E8D0 0316 059A 0062;0061 302A 1E8D0 0316 059A 0062;0061 302A 1E8D0 0316 059A 0062; # (a◌𞣐◌֚◌̖◌〪b; a◌〪◌𞣐◌̖◌֚b; a◌〪◌𞣐◌̖◌֚b; a◌〪◌𞣐◌̖◌֚b; a◌〪◌𞣐◌̖◌֚b; ) LATIN SMALL LETTER A, MENDE KIKAKUI COMBINING NUMBER TEENS, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, LATIN SMALL LETTER B
0061 059A 0316 302A 1E8D1 0062;0061 302A 0316 1E8D1 059A 0062;0061 302A 0316 1E8D1 059A 0062;0061 302A 0316 1E8D1 059A 0062;0061 302A 0316 1E8D1 059A 0062; # (a◌֚◌̖◌〪◌𞣑b; a◌〪◌̖◌𞣑◌֚b; a◌〪◌̖◌𞣑◌֚b; a◌〪◌̖◌𞣑◌֚b; a◌〪◌̖◌𞣑◌֚b; ) LATIN SMALL LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, IDEOGRAPHIC LEVEL TONE MARK, MENDE KIKAKUI COMBINING NUMBER TENS, LATIN SMALL LETTER B
diff --git a/lib/stdlib/uc_spec/CaseFolding.txt b/lib/stdlib/uc_spec/CaseFolding.txt
index cce350f49c..7eeb915abf 100644
--- a/lib/stdlib/uc_spec/CaseFolding.txt
+++ b/lib/stdlib/uc_spec/CaseFolding.txt
@@ -1,6 +1,6 @@
-# CaseFolding-11.0.0.txt
-# Date: 2018-01-31, 08:20:09 GMT
-# © 2018 Unicode®, Inc.
+# CaseFolding-12.1.0.txt
+# Date: 2019-03-10, 10:53:00 GMT
+# © 2019 Unicode®, Inc.
# Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
# For terms of use, see http://www.unicode.org/terms_of_use.html
#
@@ -1227,6 +1227,13 @@ A7B3; C; AB53; # LATIN CAPITAL LETTER CHI
A7B4; C; A7B5; # LATIN CAPITAL LETTER BETA
A7B6; C; A7B7; # LATIN CAPITAL LETTER OMEGA
A7B8; C; A7B9; # LATIN CAPITAL LETTER U WITH STROKE
+A7BA; C; A7BB; # LATIN CAPITAL LETTER GLOTTAL A
+A7BC; C; A7BD; # LATIN CAPITAL LETTER GLOTTAL I
+A7BE; C; A7BF; # LATIN CAPITAL LETTER GLOTTAL U
+A7C2; C; A7C3; # LATIN CAPITAL LETTER ANGLICANA W
+A7C4; C; A794; # LATIN CAPITAL LETTER C WITH PALATAL HOOK
+A7C5; C; 0282; # LATIN CAPITAL LETTER S WITH HOOK
+A7C6; C; 1D8E; # LATIN CAPITAL LETTER Z WITH PALATAL HOOK
AB70; C; 13A0; # CHEROKEE SMALL LETTER A
AB71; C; 13A1; # CHEROKEE SMALL LETTER E
AB72; C; 13A2; # CHEROKEE SMALL LETTER I
diff --git a/lib/stdlib/uc_spec/CompositionExclusions.txt b/lib/stdlib/uc_spec/CompositionExclusions.txt
index ea63595bd3..aa654974be 100644
--- a/lib/stdlib/uc_spec/CompositionExclusions.txt
+++ b/lib/stdlib/uc_spec/CompositionExclusions.txt
@@ -1,6 +1,6 @@
-# CompositionExclusions-11.0.0.txt
-# Date: 2017-12-06, 00:00:00 GMT [KW, LI]
-# © 2017 Unicode®, Inc.
+# CompositionExclusions-12.1.0.txt
+# Date: 2019-03-08, 23:59:00 GMT [KW, LI]
+# © 2019 Unicode®, Inc.
# For terms of use, see http://www.unicode.org/terms_of_use.html
#
# Unicode Character Database
diff --git a/lib/stdlib/uc_spec/GraphemeBreakProperty.txt b/lib/stdlib/uc_spec/GraphemeBreakProperty.txt
index 52052e6e33..b75b201f97 100644
--- a/lib/stdlib/uc_spec/GraphemeBreakProperty.txt
+++ b/lib/stdlib/uc_spec/GraphemeBreakProperty.txt
@@ -1,6 +1,6 @@
-# GraphemeBreakProperty-11.0.0.txt
-# Date: 2018-03-16, 20:34:02 GMT
-# © 2018 Unicode®, Inc.
+# GraphemeBreakProperty-12.1.0.txt
+# Date: 2019-03-10, 10:53:12 GMT
+# © 2019 Unicode®, Inc.
# Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
# For terms of use, see http://www.unicode.org/terms_of_use.html
#
@@ -27,10 +27,10 @@
110CD ; Prepend # Cf KAITHI NUMBER SIGN ABOVE
111C2..111C3 ; Prepend # Lo [2] SHARADA SIGN JIHVAMULIYA..SHARADA SIGN UPADHMANIYA
11A3A ; Prepend # Lo ZANABAZAR SQUARE CLUSTER-INITIAL LETTER RA
-11A86..11A89 ; Prepend # Lo [4] SOYOMBO CLUSTER-INITIAL LETTER RA..SOYOMBO CLUSTER-INITIAL LETTER SA
+11A84..11A89 ; Prepend # Lo [6] SOYOMBO SIGN JIHVAMULIYA..SOYOMBO CLUSTER-INITIAL LETTER SA
11D46 ; Prepend # Lo MASARAM GONDI REPHA
-# Total code points: 20
+# Total code points: 22
# ================================================
@@ -61,10 +61,10 @@
2060..2064 ; Control # Cf [5] WORD JOINER..INVISIBLE PLUS
2065 ; Control # Cn <reserved-2065>
2066..206F ; Control # Cf [10] LEFT-TO-RIGHT ISOLATE..NOMINAL DIGIT SHAPES
-D800..DFFF ; Control # Cs [2048] <surrogate-D800>..<surrogate-DFFF>
FEFF ; Control # Cf ZERO WIDTH NO-BREAK SPACE
FFF0..FFF8 ; Control # Cn [9] <reserved-FFF0>..<reserved-FFF8>
FFF9..FFFB ; Control # Cf [3] INTERLINEAR ANNOTATION ANCHOR..INTERLINEAR ANNOTATION TERMINATOR
+13430..13438 ; Control # Cf [9] EGYPTIAN HIEROGLYPH VERTICAL JOINER..EGYPTIAN HIEROGLYPH END SEGMENT
1BCA0..1BCA3 ; Control # Cf [4] SHORTHAND FORMAT LETTER OVERLAP..SHORTHAND FORMAT UP STEP
1D173..1D17A ; Control # Cf [8] MUSICAL SYMBOL BEGIN BEAM..MUSICAL SYMBOL END PHRASE
E0000 ; Control # Cn <reserved-E0000>
@@ -73,7 +73,7 @@ E0002..E001F ; Control # Cn [30] <reserved-E0002>..<reserved-E001F>
E0080..E00FF ; Control # Cn [128] <reserved-E0080>..<reserved-E00FF>
E01F0..E0FFF ; Control # Cn [3600] <reserved-E01F0>..<reserved-E0FFF>
-# Total code points: 5925
+# Total code points: 3886
# ================================================
@@ -178,8 +178,7 @@ E01F0..E0FFF ; Control # Cn [3600] <reserved-E01F0>..<reserved-E0FFF>
0E34..0E3A ; Extend # Mn [7] THAI CHARACTER SARA I..THAI CHARACTER PHINTHU
0E47..0E4E ; Extend # Mn [8] THAI CHARACTER MAITAIKHU..THAI CHARACTER YAMAKKAN
0EB1 ; Extend # Mn LAO VOWEL SIGN MAI KAN
-0EB4..0EB9 ; Extend # Mn [6] LAO VOWEL SIGN I..LAO VOWEL SIGN UU
-0EBB..0EBC ; Extend # Mn [2] LAO VOWEL SIGN MAI KON..LAO SEMIVOWEL SIGN LO
+0EB4..0EBC ; Extend # Mn [9] LAO VOWEL SIGN I..LAO SEMIVOWEL SIGN LO
0EC8..0ECD ; Extend # Mn [6] LAO TONE MAI EK..LAO NIGGAHITA
0F18..0F19 ; Extend # Mn [2] TIBETAN ASTROLOGICAL SIGN -KHYUD PA..TIBETAN ASTROLOGICAL SIGN SDONG TSHUGS
0F35 ; Extend # Mn TIBETAN MARK NGAS BZUNG NYI ZLA
@@ -232,6 +231,7 @@ E01F0..E0FFF ; Control # Cn [3600] <reserved-E01F0>..<reserved-E0FFF>
1ABE ; Extend # Me COMBINING PARENTHESES OVERLAY
1B00..1B03 ; Extend # Mn [4] BALINESE SIGN ULU RICEM..BALINESE SIGN SURANG
1B34 ; Extend # Mn BALINESE SIGN REREKAN
+1B35 ; Extend # Mc BALINESE VOWEL SIGN TEDUNG
1B36..1B3A ; Extend # Mn [5] BALINESE VOWEL SIGN ULU..BALINESE VOWEL SIGN RA REPA
1B3C ; Extend # Mn BALINESE VOWEL SIGN LA LENGA
1B42 ; Extend # Mn BALINESE VOWEL SIGN PEPET
@@ -283,7 +283,7 @@ A947..A951 ; Extend # Mn [11] REJANG VOWEL SIGN I..REJANG CONSONANT SIGN R
A980..A982 ; Extend # Mn [3] JAVANESE SIGN PANYANGGA..JAVANESE SIGN LAYAR
A9B3 ; Extend # Mn JAVANESE SIGN CECAK TELU
A9B6..A9B9 ; Extend # Mn [4] JAVANESE VOWEL SIGN WULU..JAVANESE VOWEL SIGN SUKU MENDUT
-A9BC ; Extend # Mn JAVANESE VOWEL SIGN PEPET
+A9BC..A9BD ; Extend # Mn [2] JAVANESE VOWEL SIGN PEPET..JAVANESE CONSONANT SIGN KERET
A9E5 ; Extend # Mn MYANMAR SIGN SHAN SAW
AA29..AA2E ; Extend # Mn [6] CHAM VOWEL SIGN AA..CHAM VOWEL SIGN OE
AA31..AA32 ; Extend # Mn [2] CHAM VOWEL SIGN AU..CHAM VOWEL SIGN UE
@@ -368,6 +368,9 @@ FF9E..FF9F ; Extend # Lm [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDT
11727..1172B ; Extend # Mn [5] AHOM VOWEL SIGN AW..AHOM SIGN KILLER
1182F..11837 ; Extend # Mn [9] DOGRA VOWEL SIGN U..DOGRA SIGN ANUSVARA
11839..1183A ; Extend # Mn [2] DOGRA SIGN VIRAMA..DOGRA SIGN NUKTA
+119D4..119D7 ; Extend # Mn [4] NANDINAGARI VOWEL SIGN U..NANDINAGARI VOWEL SIGN VOCALIC RR
+119DA..119DB ; Extend # Mn [2] NANDINAGARI VOWEL SIGN E..NANDINAGARI VOWEL SIGN AI
+119E0 ; Extend # Mn NANDINAGARI SIGN VIRAMA
11A01..11A0A ; Extend # Mn [10] ZANABAZAR SQUARE VOWEL SIGN I..ZANABAZAR SQUARE VOWEL LENGTH MARK
11A33..11A38 ; Extend # Mn [6] ZANABAZAR SQUARE FINAL CONSONANT MARK..ZANABAZAR SQUARE SIGN ANUSVARA
11A3B..11A3E ; Extend # Mn [4] ZANABAZAR SQUARE CLUSTER-FINAL LETTER YA..ZANABAZAR SQUARE CLUSTER-FINAL LETTER VA
@@ -394,6 +397,7 @@ FF9E..FF9F ; Extend # Lm [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDT
11EF3..11EF4 ; Extend # Mn [2] MAKASAR VOWEL SIGN I..MAKASAR VOWEL SIGN U
16AF0..16AF4 ; Extend # Mn [5] BASSA VAH COMBINING HIGH TONE..BASSA VAH COMBINING HIGH-LOW TONE
16B30..16B36 ; Extend # Mn [7] PAHAWH HMONG MARK CIM TUB..PAHAWH HMONG MARK CIM TAUM
+16F4F ; Extend # Mn MIAO SIGN CONSONANT MODIFIER BAR
16F8F..16F92 ; Extend # Mn [4] MIAO TONE RIGHT..MIAO TONE BELOW
1BC9D..1BC9E ; Extend # Mn [2] DUPLOYAN THICK LETTER SELECTOR..DUPLOYAN DOUBLE MARK
1D165 ; Extend # Mc MUSICAL SYMBOL COMBINING STEM
@@ -414,13 +418,15 @@ FF9E..FF9F ; Extend # Lm [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDT
1E01B..1E021 ; Extend # Mn [7] COMBINING GLAGOLITIC LETTER SHTA..COMBINING GLAGOLITIC LETTER YATI
1E023..1E024 ; Extend # Mn [2] COMBINING GLAGOLITIC LETTER YU..COMBINING GLAGOLITIC LETTER SMALL YUS
1E026..1E02A ; Extend # Mn [5] COMBINING GLAGOLITIC LETTER YO..COMBINING GLAGOLITIC LETTER FITA
+1E130..1E136 ; Extend # Mn [7] NYIAKENG PUACHUE HMONG TONE-B..NYIAKENG PUACHUE HMONG TONE-D
+1E2EC..1E2EF ; Extend # Mn [4] WANCHO TONE TUP..WANCHO TONE KOINI
1E8D0..1E8D6 ; Extend # Mn [7] MENDE KIKAKUI COMBINING NUMBER TEENS..MENDE KIKAKUI COMBINING NUMBER MILLIONS
1E944..1E94A ; Extend # Mn [7] ADLAM ALIF LENGTHENER..ADLAM NUKTA
1F3FB..1F3FF ; Extend # Sk [5] EMOJI MODIFIER FITZPATRICK TYPE-1-2..EMOJI MODIFIER FITZPATRICK TYPE-6
E0020..E007F ; Extend # Cf [96] TAG SPACE..CANCEL TAG
E0100..E01EF ; Extend # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256
-# Total code points: 1948
+# Total code points: 1970
# ================================================
@@ -489,7 +495,6 @@ E0100..E01EF ; Extend # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256
1A57 ; SpacingMark # Mc TAI THAM CONSONANT SIGN LA TANG LAI
1A6D..1A72 ; SpacingMark # Mc [6] TAI THAM VOWEL SIGN OY..TAI THAM VOWEL SIGN THAM AI
1B04 ; SpacingMark # Mc BALINESE SIGN BISAH
-1B35 ; SpacingMark # Mc BALINESE VOWEL SIGN TEDUNG
1B3B ; SpacingMark # Mc BALINESE VOWEL SIGN RA REPA TEDUNG
1B3D..1B41 ; SpacingMark # Mc [5] BALINESE VOWEL SIGN LA LENGA TEDUNG..BALINESE VOWEL SIGN TALING REPA TEDUNG
1B43..1B44 ; SpacingMark # Mc [2] BALINESE VOWEL SIGN PEPET TEDUNG..BALINESE ADEG ADEG
@@ -504,7 +509,6 @@ E0100..E01EF ; Extend # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256
1C24..1C2B ; SpacingMark # Mc [8] LEPCHA SUBJOINED LETTER YA..LEPCHA VOWEL SIGN UU
1C34..1C35 ; SpacingMark # Mc [2] LEPCHA CONSONANT SIGN NYIN-DO..LEPCHA CONSONANT SIGN KANG
1CE1 ; SpacingMark # Mc VEDIC TONE ATHARVAVEDIC INDEPENDENT SVARITA
-1CF2..1CF3 ; SpacingMark # Mc [2] VEDIC SIGN ARDHAVISARGA..VEDIC SIGN ROTATED ARDHAVISARGA
1CF7 ; SpacingMark # Mc VEDIC SIGN ATIKRAMA
A823..A824 ; SpacingMark # Mc [2] SYLOTI NAGRI VOWEL SIGN A..SYLOTI NAGRI VOWEL SIGN I
A827 ; SpacingMark # Mc SYLOTI NAGRI VOWEL SIGN OO
@@ -514,7 +518,7 @@ A952..A953 ; SpacingMark # Mc [2] REJANG CONSONANT SIGN H..REJANG VIRAMA
A983 ; SpacingMark # Mc JAVANESE SIGN WIGNYAN
A9B4..A9B5 ; SpacingMark # Mc [2] JAVANESE VOWEL SIGN TARUNG..JAVANESE VOWEL SIGN TOLONG
A9BA..A9BB ; SpacingMark # Mc [2] JAVANESE VOWEL SIGN TALING..JAVANESE VOWEL SIGN DIRGA MURE
-A9BD..A9C0 ; SpacingMark # Mc [4] JAVANESE CONSONANT SIGN KERET..JAVANESE PANGKON
+A9BE..A9C0 ; SpacingMark # Mc [3] JAVANESE CONSONANT SIGN PENGKAL..JAVANESE PANGKON
AA2F..AA30 ; SpacingMark # Mc [2] CHAM VOWEL SIGN O..CHAM VOWEL SIGN AI
AA33..AA34 ; SpacingMark # Mc [2] CHAM CONSONANT SIGN YA..CHAM CONSONANT SIGN RA
AA4D ; SpacingMark # Mc CHAM CONSONANT SIGN FINAL H
@@ -566,6 +570,9 @@ ABEC ; SpacingMark # Mc MEETEI MAYEK LUM IYEK
11726 ; SpacingMark # Mc AHOM VOWEL SIGN E
1182C..1182E ; SpacingMark # Mc [3] DOGRA VOWEL SIGN AA..DOGRA VOWEL SIGN II
11838 ; SpacingMark # Mc DOGRA SIGN VISARGA
+119D1..119D3 ; SpacingMark # Mc [3] NANDINAGARI VOWEL SIGN AA..NANDINAGARI VOWEL SIGN II
+119DC..119DF ; SpacingMark # Mc [4] NANDINAGARI VOWEL SIGN O..NANDINAGARI SIGN VISARGA
+119E4 ; SpacingMark # Mc NANDINAGARI VOWEL SIGN PRISHTHAMATRA E
11A39 ; SpacingMark # Mc ZANABAZAR SQUARE SIGN VISARGA
11A57..11A58 ; SpacingMark # Mc [2] SOYOMBO VOWEL SIGN AI..SOYOMBO VOWEL SIGN AU
11A97 ; SpacingMark # Mc SOYOMBO SIGN VISARGA
@@ -578,11 +585,11 @@ ABEC ; SpacingMark # Mc MEETEI MAYEK LUM IYEK
11D93..11D94 ; SpacingMark # Mc [2] GUNJALA GONDI VOWEL SIGN OO..GUNJALA GONDI VOWEL SIGN AU
11D96 ; SpacingMark # Mc GUNJALA GONDI SIGN VISARGA
11EF5..11EF6 ; SpacingMark # Mc [2] MAKASAR VOWEL SIGN E..MAKASAR VOWEL SIGN O
-16F51..16F7E ; SpacingMark # Mc [46] MIAO SIGN ASPIRATION..MIAO VOWEL SIGN NG
+16F51..16F87 ; SpacingMark # Mc [55] MIAO SIGN ASPIRATION..MIAO VOWEL SIGN UI
1D166 ; SpacingMark # Mc MUSICAL SYMBOL COMBINING SPRECHGESANG STEM
1D16D ; SpacingMark # Mc MUSICAL SYMBOL COMBINING AUGMENTATION DOT
-# Total code points: 362
+# Total code points: 375
# ================================================
diff --git a/lib/stdlib/uc_spec/PropList.txt b/lib/stdlib/uc_spec/PropList.txt
index ef86795abe..4394602fea 100644
--- a/lib/stdlib/uc_spec/PropList.txt
+++ b/lib/stdlib/uc_spec/PropList.txt
@@ -1,6 +1,6 @@
-# PropList-11.0.0.txt
-# Date: 2018-03-15, 04:28:35 GMT
-# © 2018 Unicode®, Inc.
+# PropList-12.1.0.txt
+# Date: 2019-03-10, 10:53:16 GMT
+# © 2019 Unicode®, Inc.
# Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
# For terms of use, see http://www.unicode.org/terms_of_use.html
#
@@ -138,7 +138,7 @@ FF63 ; Quotation_Mark # Pe HALFWIDTH RIGHT CORNER BRACKET
0F0D..0F12 ; Terminal_Punctuation # Po [6] TIBETAN MARK SHAD..TIBETAN MARK RGYA GRAM SHAD
104A..104B ; Terminal_Punctuation # Po [2] MYANMAR SIGN LITTLE SECTION..MYANMAR SIGN SECTION
1361..1368 ; Terminal_Punctuation # Po [8] ETHIOPIC WORDSPACE..ETHIOPIC PARAGRAPH SEPARATOR
-166D..166E ; Terminal_Punctuation # Po [2] CANADIAN SYLLABICS CHI SIGN..CANADIAN SYLLABICS FULL STOP
+166E ; Terminal_Punctuation # Po CANADIAN SYLLABICS FULL STOP
16EB..16ED ; Terminal_Punctuation # Po [3] RUNIC SINGLE PUNCTUATION..RUNIC CROSS PUNCTUATION
1735..1736 ; Terminal_Punctuation # Po [2] PHILIPPINE SINGLE PUNCTUATION..PHILIPPINE DOUBLE PUNCTUATION
17D4..17D6 ; Terminal_Punctuation # Po [3] KHMER SIGN KHAN..KHMER SIGN CAMNUC PII KUUH
@@ -157,7 +157,7 @@ FF63 ; Quotation_Mark # Pe HALFWIDTH RIGHT CORNER BRACKET
2E3C ; Terminal_Punctuation # Po STENOGRAPHIC FULL STOP
2E41 ; Terminal_Punctuation # Po REVERSED COMMA
2E4C ; Terminal_Punctuation # Po MEDIEVAL COMMA
-2E4E ; Terminal_Punctuation # Po PUNCTUS ELEVATUS MARK
+2E4E..2E4F ; Terminal_Punctuation # Po [2] PUNCTUS ELEVATUS MARK..CORNISH VERSE DIVIDER
3001..3002 ; Terminal_Punctuation # Po [2] IDEOGRAPHIC COMMA..IDEOGRAPHIC FULL STOP
A4FE..A4FF ; Terminal_Punctuation # Po [2] LISU PUNCTUATION COMMA..LISU PUNCTUATION FULL STOP
A60D..A60F ; Terminal_Punctuation # Po [3] VAI COMMA..VAI QUESTION MARK
@@ -553,15 +553,17 @@ FF41..FF46 ; Hex_Digit # L& [6] FULLWIDTH LATIN SMALL LETTER A..FULLWIDTH L
1056..1057 ; Other_Alphabetic # Mc [2] MYANMAR VOWEL SIGN VOCALIC R..MYANMAR VOWEL SIGN VOCALIC RR
1058..1059 ; Other_Alphabetic # Mn [2] MYANMAR VOWEL SIGN VOCALIC L..MYANMAR VOWEL SIGN VOCALIC LL
105E..1060 ; Other_Alphabetic # Mn [3] MYANMAR CONSONANT SIGN MON MEDIAL NA..MYANMAR CONSONANT SIGN MON MEDIAL LA
-1062 ; Other_Alphabetic # Mc MYANMAR VOWEL SIGN SGAW KAREN EU
-1067..1068 ; Other_Alphabetic # Mc [2] MYANMAR VOWEL SIGN WESTERN PWO KAREN EU..MYANMAR VOWEL SIGN WESTERN PWO KAREN UE
+1062..1064 ; Other_Alphabetic # Mc [3] MYANMAR VOWEL SIGN SGAW KAREN EU..MYANMAR TONE MARK SGAW KAREN KE PHO
+1067..106D ; Other_Alphabetic # Mc [7] MYANMAR VOWEL SIGN WESTERN PWO KAREN EU..MYANMAR SIGN WESTERN PWO KAREN TONE-5
1071..1074 ; Other_Alphabetic # Mn [4] MYANMAR VOWEL SIGN GEBA KAREN I..MYANMAR VOWEL SIGN KAYAH EE
1082 ; Other_Alphabetic # Mn MYANMAR CONSONANT SIGN SHAN MEDIAL WA
1083..1084 ; Other_Alphabetic # Mc [2] MYANMAR VOWEL SIGN SHAN AA..MYANMAR VOWEL SIGN SHAN E
1085..1086 ; Other_Alphabetic # Mn [2] MYANMAR VOWEL SIGN SHAN E ABOVE..MYANMAR VOWEL SIGN SHAN FINAL Y
-109C ; Other_Alphabetic # Mc MYANMAR VOWEL SIGN AITON A
+1087..108C ; Other_Alphabetic # Mc [6] MYANMAR SIGN SHAN TONE-2..MYANMAR SIGN SHAN COUNCIL TONE-3
+108D ; Other_Alphabetic # Mn MYANMAR SIGN SHAN COUNCIL EMPHATIC TONE
+108F ; Other_Alphabetic # Mc MYANMAR SIGN RUMAI PALAUNG TONE-5
+109A..109C ; Other_Alphabetic # Mc [3] MYANMAR SIGN KHAMTI TONE-1..MYANMAR VOWEL SIGN AITON A
109D ; Other_Alphabetic # Mn MYANMAR VOWEL SIGN AITON AI
-135F ; Other_Alphabetic # Mn ETHIOPIC COMBINING GEMINATION MARK
1712..1713 ; Other_Alphabetic # Mn [2] TAGALOG VOWEL SIGN I..TAGALOG VOWEL SIGN U
1732..1733 ; Other_Alphabetic # Mn [2] HANUNOO VOWEL SIGN I..HANUNOO VOWEL SIGN U
1752..1753 ; Other_Alphabetic # Mn [2] BUHID VOWEL SIGN I..BUHID VOWEL SIGN U
@@ -618,18 +620,21 @@ FF41..FF46 ; Hex_Digit # L& [6] FULLWIDTH LATIN SMALL LETTER A..FULLWIDTH L
1C24..1C2B ; Other_Alphabetic # Mc [8] LEPCHA SUBJOINED LETTER YA..LEPCHA VOWEL SIGN UU
1C2C..1C33 ; Other_Alphabetic # Mn [8] LEPCHA VOWEL SIGN E..LEPCHA CONSONANT SIGN T
1C34..1C35 ; Other_Alphabetic # Mc [2] LEPCHA CONSONANT SIGN NYIN-DO..LEPCHA CONSONANT SIGN KANG
-1CF2..1CF3 ; Other_Alphabetic # Mc [2] VEDIC SIGN ARDHAVISARGA..VEDIC SIGN ROTATED ARDHAVISARGA
+1C36 ; Other_Alphabetic # Mn LEPCHA SIGN RAN
1DE7..1DF4 ; Other_Alphabetic # Mn [14] COMBINING LATIN SMALL LETTER ALPHA..COMBINING LATIN SMALL LETTER U WITH DIAERESIS
24B6..24E9 ; Other_Alphabetic # So [52] CIRCLED LATIN CAPITAL LETTER A..CIRCLED LATIN SMALL LETTER Z
2DE0..2DFF ; Other_Alphabetic # Mn [32] COMBINING CYRILLIC LETTER BE..COMBINING CYRILLIC LETTER IOTIFIED BIG YUS
A674..A67B ; Other_Alphabetic # Mn [8] COMBINING CYRILLIC LETTER UKRAINIAN IE..COMBINING CYRILLIC LETTER OMEGA
A69E..A69F ; Other_Alphabetic # Mn [2] COMBINING CYRILLIC LETTER EF..COMBINING CYRILLIC LETTER IOTIFIED E
+A802 ; Other_Alphabetic # Mn SYLOTI NAGRI SIGN DVISVARA
+A80B ; Other_Alphabetic # Mn SYLOTI NAGRI SIGN ANUSVARA
A823..A824 ; Other_Alphabetic # Mc [2] SYLOTI NAGRI VOWEL SIGN A..SYLOTI NAGRI VOWEL SIGN I
A825..A826 ; Other_Alphabetic # Mn [2] SYLOTI NAGRI VOWEL SIGN U..SYLOTI NAGRI VOWEL SIGN E
A827 ; Other_Alphabetic # Mc SYLOTI NAGRI VOWEL SIGN OO
A880..A881 ; Other_Alphabetic # Mc [2] SAURASHTRA SIGN ANUSVARA..SAURASHTRA SIGN VISARGA
A8B4..A8C3 ; Other_Alphabetic # Mc [16] SAURASHTRA CONSONANT SIGN HAARU..SAURASHTRA VOWEL SIGN AU
A8C5 ; Other_Alphabetic # Mn SAURASHTRA SIGN CANDRABINDU
+A8FF ; Other_Alphabetic # Mn DEVANAGARI VOWEL SIGN AY
A926..A92A ; Other_Alphabetic # Mn [5] KAYAH LI VOWEL UE..KAYAH LI VOWEL O
A947..A951 ; Other_Alphabetic # Mn [11] REJANG VOWEL SIGN I..REJANG CONSONANT SIGN R
A952 ; Other_Alphabetic # Mc REJANG CONSONANT SIGN H
@@ -638,8 +643,9 @@ A983 ; Other_Alphabetic # Mc JAVANESE SIGN WIGNYAN
A9B4..A9B5 ; Other_Alphabetic # Mc [2] JAVANESE VOWEL SIGN TARUNG..JAVANESE VOWEL SIGN TOLONG
A9B6..A9B9 ; Other_Alphabetic # Mn [4] JAVANESE VOWEL SIGN WULU..JAVANESE VOWEL SIGN SUKU MENDUT
A9BA..A9BB ; Other_Alphabetic # Mc [2] JAVANESE VOWEL SIGN TALING..JAVANESE VOWEL SIGN DIRGA MURE
-A9BC ; Other_Alphabetic # Mn JAVANESE VOWEL SIGN PEPET
-A9BD..A9BF ; Other_Alphabetic # Mc [3] JAVANESE CONSONANT SIGN KERET..JAVANESE CONSONANT SIGN CAKRA
+A9BC..A9BD ; Other_Alphabetic # Mn [2] JAVANESE VOWEL SIGN PEPET..JAVANESE CONSONANT SIGN KERET
+A9BE..A9BF ; Other_Alphabetic # Mc [2] JAVANESE CONSONANT SIGN PENGKAL..JAVANESE CONSONANT SIGN CAKRA
+A9E5 ; Other_Alphabetic # Mn MYANMAR SIGN SHAN SAW
AA29..AA2E ; Other_Alphabetic # Mn [6] CHAM VOWEL SIGN AA..CHAM VOWEL SIGN OE
AA2F..AA30 ; Other_Alphabetic # Mc [2] CHAM VOWEL SIGN O..CHAM VOWEL SIGN AI
AA31..AA32 ; Other_Alphabetic # Mn [2] CHAM VOWEL SIGN AU..CHAM VOWEL SIGN UE
@@ -648,6 +654,9 @@ AA35..AA36 ; Other_Alphabetic # Mn [2] CHAM CONSONANT SIGN LA..CHAM CONSONA
AA43 ; Other_Alphabetic # Mn CHAM CONSONANT SIGN FINAL NG
AA4C ; Other_Alphabetic # Mn CHAM CONSONANT SIGN FINAL M
AA4D ; Other_Alphabetic # Mc CHAM CONSONANT SIGN FINAL H
+AA7B ; Other_Alphabetic # Mc MYANMAR SIGN PAO KAREN TONE
+AA7C ; Other_Alphabetic # Mn MYANMAR SIGN TAI LAING TONE-2
+AA7D ; Other_Alphabetic # Mc MYANMAR SIGN TAI LAING TONE-5
AAB0 ; Other_Alphabetic # Mn TAI VIET MAI KANG
AAB2..AAB4 ; Other_Alphabetic # Mn [3] TAI VIET VOWEL I..TAI VIET VOWEL U
AAB7..AAB8 ; Other_Alphabetic # Mn [2] TAI VIET MAI KHIT..TAI VIET VOWEL IA
@@ -740,6 +749,11 @@ FB1E ; Other_Alphabetic # Mn HEBREW POINT JUDEO-SPANISH VARIKA
1182C..1182E ; Other_Alphabetic # Mc [3] DOGRA VOWEL SIGN AA..DOGRA VOWEL SIGN II
1182F..11837 ; Other_Alphabetic # Mn [9] DOGRA VOWEL SIGN U..DOGRA SIGN ANUSVARA
11838 ; Other_Alphabetic # Mc DOGRA SIGN VISARGA
+119D1..119D3 ; Other_Alphabetic # Mc [3] NANDINAGARI VOWEL SIGN AA..NANDINAGARI VOWEL SIGN II
+119D4..119D7 ; Other_Alphabetic # Mn [4] NANDINAGARI VOWEL SIGN U..NANDINAGARI VOWEL SIGN VOCALIC RR
+119DA..119DB ; Other_Alphabetic # Mn [2] NANDINAGARI VOWEL SIGN E..NANDINAGARI VOWEL SIGN AI
+119DC..119DF ; Other_Alphabetic # Mc [4] NANDINAGARI VOWEL SIGN O..NANDINAGARI SIGN VISARGA
+119E4 ; Other_Alphabetic # Mc NANDINAGARI VOWEL SIGN PRISHTHAMATRA E
11A01..11A0A ; Other_Alphabetic # Mn [10] ZANABAZAR SQUARE VOWEL SIGN I..ZANABAZAR SQUARE VOWEL LENGTH MARK
11A35..11A38 ; Other_Alphabetic # Mn [4] ZANABAZAR SQUARE SIGN CANDRABINDU..ZANABAZAR SQUARE SIGN ANUSVARA
11A39 ; Other_Alphabetic # Mc ZANABAZAR SQUARE SIGN VISARGA
@@ -773,8 +787,9 @@ FB1E ; Other_Alphabetic # Mn HEBREW POINT JUDEO-SPANISH VARIKA
11D96 ; Other_Alphabetic # Mc GUNJALA GONDI SIGN VISARGA
11EF3..11EF4 ; Other_Alphabetic # Mn [2] MAKASAR VOWEL SIGN I..MAKASAR VOWEL SIGN U
11EF5..11EF6 ; Other_Alphabetic # Mc [2] MAKASAR VOWEL SIGN E..MAKASAR VOWEL SIGN O
-16B30..16B36 ; Other_Alphabetic # Mn [7] PAHAWH HMONG MARK CIM TUB..PAHAWH HMONG MARK CIM TAUM
-16F51..16F7E ; Other_Alphabetic # Mc [46] MIAO SIGN ASPIRATION..MIAO VOWEL SIGN NG
+16F4F ; Other_Alphabetic # Mn MIAO SIGN CONSONANT MODIFIER BAR
+16F51..16F87 ; Other_Alphabetic # Mc [55] MIAO SIGN ASPIRATION..MIAO VOWEL SIGN UI
+16F8F..16F92 ; Other_Alphabetic # Mn [4] MIAO TONE RIGHT..MIAO TONE BELOW
1BC9E ; Other_Alphabetic # Mn DUPLOYAN DOUBLE MARK
1E000..1E006 ; Other_Alphabetic # Mn [7] COMBINING GLAGOLITIC LETTER AZU..COMBINING GLAGOLITIC LETTER ZHIVETE
1E008..1E018 ; Other_Alphabetic # Mn [17] COMBINING GLAGOLITIC LETTER ZEMLJA..COMBINING GLAGOLITIC LETTER HERU
@@ -786,7 +801,7 @@ FB1E ; Other_Alphabetic # Mn HEBREW POINT JUDEO-SPANISH VARIKA
1F150..1F169 ; Other_Alphabetic # So [26] NEGATIVE CIRCLED LATIN CAPITAL LETTER A..NEGATIVE CIRCLED LATIN CAPITAL LETTER Z
1F170..1F189 ; Other_Alphabetic # So [26] NEGATIVE SQUARED LATIN CAPITAL LETTER A..NEGATIVE SQUARED LATIN CAPITAL LETTER Z
-# Total code points: 1334
+# Total code points: 1377
# ================================================
@@ -798,7 +813,7 @@ FB1E ; Other_Alphabetic # Mn HEBREW POINT JUDEO-SPANISH VARIKA
4E00..9FEF ; Ideographic # Lo [20976] CJK UNIFIED IDEOGRAPH-4E00..CJK UNIFIED IDEOGRAPH-9FEF
F900..FA6D ; Ideographic # Lo [366] CJK COMPATIBILITY IDEOGRAPH-F900..CJK COMPATIBILITY IDEOGRAPH-FA6D
FA70..FAD9 ; Ideographic # Lo [106] CJK COMPATIBILITY IDEOGRAPH-FA70..CJK COMPATIBILITY IDEOGRAPH-FAD9
-17000..187F1 ; Ideographic # Lo [6130] TANGUT IDEOGRAPH-17000..TANGUT IDEOGRAPH-187F1
+17000..187F7 ; Ideographic # Lo [6136] TANGUT IDEOGRAPH-17000..TANGUT IDEOGRAPH-187F7
18800..18AF2 ; Ideographic # Lo [755] TANGUT COMPONENT-001..TANGUT COMPONENT-755
1B170..1B2FB ; Ideographic # Lo [396] NUSHU CHARACTER-1B170..NUSHU CHARACTER-1B2FB
20000..2A6D6 ; Ideographic # Lo [42711] CJK UNIFIED IDEOGRAPH-20000..CJK UNIFIED IDEOGRAPH-2A6D6
@@ -808,7 +823,7 @@ FA70..FAD9 ; Ideographic # Lo [106] CJK COMPATIBILITY IDEOGRAPH-FA70..CJK COM
2CEB0..2EBE0 ; Ideographic # Lo [7473] CJK UNIFIED IDEOGRAPH-2CEB0..CJK UNIFIED IDEOGRAPH-2EBE0
2F800..2FA1D ; Ideographic # Lo [542] CJK COMPATIBILITY IDEOGRAPH-2F800..CJK COMPATIBILITY IDEOGRAPH-2FA1D
-# Total code points: 96184
+# Total code points: 96190
# ================================================
@@ -876,6 +891,7 @@ FA70..FAD9 ; Ideographic # Lo [106] CJK COMPATIBILITY IDEOGRAPH-FA70..CJK COM
0DCA ; Diacritic # Mn SINHALA SIGN AL-LAKUNA
0E47..0E4C ; Diacritic # Mn [6] THAI CHARACTER MAITAIKHU..THAI CHARACTER THANTHAKHAT
0E4E ; Diacritic # Mn THAI CHARACTER YAMAKKAN
+0EBA ; Diacritic # Mn LAO SIGN PALI VIRAMA
0EC8..0ECC ; Diacritic # Mn [5] LAO TONE MAI EK..LAO CANCELLATION MARK
0F18..0F19 ; Diacritic # Mn [2] TIBETAN ASTROLOGICAL SIGN -KHYUD PA..TIBETAN ASTROLOGICAL SIGN SDONG TSHUGS
0F35 ; Diacritic # Mn TIBETAN MARK NGAS BZUNG NYI ZLA
@@ -887,10 +903,13 @@ FA70..FAD9 ; Ideographic # Lo [106] CJK COMPATIBILITY IDEOGRAPH-FA70..CJK COM
0FC6 ; Diacritic # Mn TIBETAN SYMBOL PADMA GDAN
1037 ; Diacritic # Mn MYANMAR SIGN DOT BELOW
1039..103A ; Diacritic # Mn [2] MYANMAR SIGN VIRAMA..MYANMAR SIGN ASAT
+1063..1064 ; Diacritic # Mc [2] MYANMAR TONE MARK SGAW KAREN HATHI..MYANMAR TONE MARK SGAW KAREN KE PHO
+1069..106D ; Diacritic # Mc [5] MYANMAR SIGN WESTERN PWO KAREN TONE-1..MYANMAR SIGN WESTERN PWO KAREN TONE-5
1087..108C ; Diacritic # Mc [6] MYANMAR SIGN SHAN TONE-2..MYANMAR SIGN SHAN COUNCIL TONE-3
108D ; Diacritic # Mn MYANMAR SIGN SHAN COUNCIL EMPHATIC TONE
108F ; Diacritic # Mc MYANMAR SIGN RUMAI PALAUNG TONE-5
109A..109B ; Diacritic # Mc [2] MYANMAR SIGN KHAMTI TONE-1..MYANMAR SIGN KHAMTI TONE-3
+135D..135F ; Diacritic # Mn [3] ETHIOPIC COMBINING GEMINATION AND VOWEL LENGTH MARK..ETHIOPIC COMBINING GEMINATION MARK
17C9..17D3 ; Diacritic # Mn [11] KHMER SIGN MUUSIKATOAN..KHMER SIGN BATHAMASAT
17DD ; Diacritic # Mn KHMER SIGN ATTHACAN
1939..193B ; Diacritic # Mn [3] LIMBU SIGN MUKPHRENG..LIMBU SIGN SA-I
@@ -935,9 +954,11 @@ A67C..A67D ; Diacritic # Mn [2] COMBINING CYRILLIC KAVYKA..COMBINING CYRILL
A67F ; Diacritic # Lm CYRILLIC PAYEROK
A69C..A69D ; Diacritic # Lm [2] MODIFIER LETTER CYRILLIC HARD SIGN..MODIFIER LETTER CYRILLIC SOFT SIGN
A6F0..A6F1 ; Diacritic # Mn [2] BAMUM COMBINING MARK KOQNDON..BAMUM COMBINING MARK TUKWENTIS
+A700..A716 ; Diacritic # Sk [23] MODIFIER LETTER CHINESE TONE YIN PING..MODIFIER LETTER EXTRA-LOW LEFT-STEM TONE BAR
A717..A71F ; Diacritic # Lm [9] MODIFIER LETTER DOT VERTICAL BAR..MODIFIER LETTER LOW INVERTED EXCLAMATION MARK
A720..A721 ; Diacritic # Sk [2] MODIFIER LETTER STRESS AND HIGH TONE..MODIFIER LETTER STRESS AND LOW TONE
A788 ; Diacritic # Lm MODIFIER LETTER LOW CIRCUMFLEX ACCENT
+A789..A78A ; Diacritic # Sk [2] MODIFIER LETTER COLON..MODIFIER LETTER SHORT EQUALS SIGN
A7F8..A7F9 ; Diacritic # Lm [2] MODIFIER LETTER CAPITAL H WITH STROKE..MODIFIER LETTER SMALL LIGATURE OE
A8C4 ; Diacritic # Mn SAURASHTRA SIGN VIRAMA
A8E0..A8F1 ; Diacritic # Mn [18] COMBINING DEVANAGARI DIGIT ZERO..COMBINING DEVANAGARI SIGN AVAGRAHA
@@ -992,6 +1013,7 @@ FFE3 ; Diacritic # Sk FULLWIDTH MACRON
116B7 ; Diacritic # Mn TAKRI SIGN NUKTA
1172B ; Diacritic # Mn AHOM SIGN KILLER
11839..1183A ; Diacritic # Mn [2] DOGRA SIGN VIRAMA..DOGRA SIGN NUKTA
+119E0 ; Diacritic # Mn NANDINAGARI SIGN VIRAMA
11A34 ; Diacritic # Mn ZANABAZAR SQUARE SIGN VIRAMA
11A47 ; Diacritic # Mn ZANABAZAR SQUARE SUBJOINER
11A99 ; Diacritic # Mn SOYOMBO SUBJOINER
@@ -1000,6 +1022,7 @@ FFE3 ; Diacritic # Sk FULLWIDTH MACRON
11D44..11D45 ; Diacritic # Mn [2] MASARAM GONDI SIGN HALANTA..MASARAM GONDI VIRAMA
11D97 ; Diacritic # Mn GUNJALA GONDI VIRAMA
16AF0..16AF4 ; Diacritic # Mn [5] BASSA VAH COMBINING HIGH TONE..BASSA VAH COMBINING HIGH-LOW TONE
+16B30..16B36 ; Diacritic # Mn [7] PAHAWH HMONG MARK CIM TUB..PAHAWH HMONG MARK CIM TAUM
16F8F..16F92 ; Diacritic # Mn [4] MIAO TONE RIGHT..MIAO TONE BELOW
16F93..16F9F ; Diacritic # Lm [13] MIAO LETTER TONE-2..MIAO LETTER REFORMED TONE-8
1D167..1D169 ; Diacritic # Mn [3] MUSICAL SYMBOL COMBINING TREMOLO-1..MUSICAL SYMBOL COMBINING TREMOLO-3
@@ -1007,11 +1030,13 @@ FFE3 ; Diacritic # Sk FULLWIDTH MACRON
1D17B..1D182 ; Diacritic # Mn [8] MUSICAL SYMBOL COMBINING ACCENT..MUSICAL SYMBOL COMBINING LOURE
1D185..1D18B ; Diacritic # Mn [7] MUSICAL SYMBOL COMBINING DOIT..MUSICAL SYMBOL COMBINING TRIPLE TONGUE
1D1AA..1D1AD ; Diacritic # Mn [4] MUSICAL SYMBOL COMBINING DOWN BOW..MUSICAL SYMBOL COMBINING SNAP PIZZICATO
+1E130..1E136 ; Diacritic # Mn [7] NYIAKENG PUACHUE HMONG TONE-B..NYIAKENG PUACHUE HMONG TONE-D
+1E2EC..1E2EF ; Diacritic # Mn [4] WANCHO TONE TUP..WANCHO TONE KOINI
1E8D0..1E8D6 ; Diacritic # Mn [7] MENDE KIKAKUI COMBINING NUMBER TEENS..MENDE KIKAKUI COMBINING NUMBER MILLIONS
1E944..1E946 ; Diacritic # Mn [3] ADLAM ALIF LENGTHENER..ADLAM GEMINATION MARK
1E948..1E94A ; Diacritic # Mn [3] ADLAM CONSONANT MODIFIER..ADLAM NUKTA
-# Total code points: 818
+# Total code points: 873
# ================================================
@@ -1043,9 +1068,11 @@ FF70 ; Extender # Lm HALFWIDTH KATAKANA-HIRAGANA PROLONGED SOUND
11A98 ; Extender # Mn SOYOMBO GEMINATION MARK
16B42..16B43 ; Extender # Lm [2] PAHAWH HMONG SIGN VOS NRUA..PAHAWH HMONG SIGN IB YAM
16FE0..16FE1 ; Extender # Lm [2] TANGUT ITERATION MARK..NUSHU ITERATION MARK
+16FE3 ; Extender # Lm OLD CHINESE ITERATION MARK
+1E13C..1E13D ; Extender # Lm [2] NYIAKENG PUACHUE HMONG SIGN XW XW..NYIAKENG PUACHUE HMONG SYLLABLE LENGTHENER
1E944..1E946 ; Extender # Mn [3] ADLAM ALIF LENGTHENER..ADLAM GEMINATION MARK
-# Total code points: 44
+# Total code points: 47
# ================================================
@@ -1119,6 +1146,7 @@ FFFFE..FFFFF ; Noncharacter_Code_Point # Cn [2] <noncharacter-FFFFE>..<noncha
0D57 ; Other_Grapheme_Extend # Mc MALAYALAM AU LENGTH MARK
0DCF ; Other_Grapheme_Extend # Mc SINHALA VOWEL SIGN AELA-PILLA
0DDF ; Other_Grapheme_Extend # Mc SINHALA VOWEL SIGN GAYANUKITTA
+1B35 ; Other_Grapheme_Extend # Mc BALINESE VOWEL SIGN TEDUNG
200C ; Other_Grapheme_Extend # Cf ZERO WIDTH NON-JOINER
302E..302F ; Other_Grapheme_Extend # Mc [2] HANGUL SINGLE DOT TONE MARK..HANGUL DOUBLE DOT TONE MARK
FF9E..FF9F ; Other_Grapheme_Extend # Lm [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK
@@ -1131,7 +1159,7 @@ FF9E..FF9F ; Other_Grapheme_Extend # Lm [2] HALFWIDTH KATAKANA VOICED SOUND
1D16E..1D172 ; Other_Grapheme_Extend # Mc [5] MUSICAL SYMBOL COMBINING FLAG-1..MUSICAL SYMBOL COMBINING FLAG-5
E0020..E007F ; Other_Grapheme_Extend # Cf [96] TAG SPACE..CANCEL TAG
-# Total code points: 125
+# Total code points: 126
# ================================================
@@ -1547,10 +1575,7 @@ E0100..E01EF ; Variation_Selector # Mn [240] VARIATION SELECTOR-17..VARIATION S
2B74..2B75 ; Pattern_Syntax # Cn [2] <reserved-2B74>..<reserved-2B75>
2B76..2B95 ; Pattern_Syntax # So [32] NORTH WEST TRIANGLE-HEADED ARROW TO BAR..RIGHTWARDS BLACK ARROW
2B96..2B97 ; Pattern_Syntax # Cn [2] <reserved-2B96>..<reserved-2B97>
-2B98..2BC8 ; Pattern_Syntax # So [49] THREE-D TOP-LIGHTED LEFTWARDS EQUILATERAL ARROWHEAD..BLACK MEDIUM RIGHT-POINTING TRIANGLE CENTRED
-2BC9 ; Pattern_Syntax # Cn <reserved-2BC9>
-2BCA..2BFE ; Pattern_Syntax # So [53] TOP HALF BLACK CIRCLE..REVERSED RIGHT ANGLE
-2BFF ; Pattern_Syntax # Cn <reserved-2BFF>
+2B98..2BFF ; Pattern_Syntax # So [104] THREE-D TOP-LIGHTED LEFTWARDS EQUILATERAL ARROWHEAD..HELLSCHREIBER PAUSE SYMBOL
2E00..2E01 ; Pattern_Syntax # Po [2] RIGHT ANGLE SUBSTITUTION MARKER..RIGHT ANGLE DOTTED SUBSTITUTION MARKER
2E02 ; Pattern_Syntax # Pi LEFT SUBSTITUTION BRACKET
2E03 ; Pattern_Syntax # Pf RIGHT SUBSTITUTION BRACKET
@@ -1588,8 +1613,8 @@ E0100..E01EF ; Variation_Selector # Mn [240] VARIATION SELECTOR-17..VARIATION S
2E40 ; Pattern_Syntax # Pd DOUBLE HYPHEN
2E41 ; Pattern_Syntax # Po REVERSED COMMA
2E42 ; Pattern_Syntax # Ps DOUBLE LOW-REVERSED-9 QUOTATION MARK
-2E43..2E4E ; Pattern_Syntax # Po [12] DASH WITH LEFT UPTURN..PUNCTUS ELEVATUS MARK
-2E4F..2E7F ; Pattern_Syntax # Cn [49] <reserved-2E4F>..<reserved-2E7F>
+2E43..2E4F ; Pattern_Syntax # Po [13] DASH WITH LEFT UPTURN..CORNISH VERSE DIVIDER
+2E50..2E7F ; Pattern_Syntax # Cn [48] <reserved-2E50>..<reserved-2E7F>
3001..3003 ; Pattern_Syntax # Po [3] IDEOGRAPHIC COMMA..DITTO MARK
3008 ; Pattern_Syntax # Ps LEFT ANGLE BRACKET
3009 ; Pattern_Syntax # Pe RIGHT ANGLE BRACKET
diff --git a/lib/stdlib/uc_spec/README-UPDATE.txt b/lib/stdlib/uc_spec/README-UPDATE.txt
index e1f5c8fcd0..73274e512a 100644
--- a/lib/stdlib/uc_spec/README-UPDATE.txt
+++ b/lib/stdlib/uc_spec/README-UPDATE.txt
@@ -2,12 +2,10 @@ When updating the unicode version copy the necessary files to this
directory.
And update the test files in stdlib/test/unicode_util_SUITE_data/*
-Unicode 11 was updated from:
-https://www.unicode.org/Public/11.0.0/ucd/
-https://www.unicode.org/Public/11.0.0/ucd/auxiliary/
-https://www.unicode.org/Public/emoji/11.0/
+Unicode 12.1 was updated from:
+https://www.unicode.org/Public/12.1.0/ucd/
+https://www.unicode.org/Public/12.1.0/ucd/auxiliary/
+https://www.unicode.org/Public/emoji/12.0/
Update the spec_version(..) function in the generator,
gen_unicode_mod.escript
-
-
diff --git a/lib/stdlib/uc_spec/SpecialCasing.txt b/lib/stdlib/uc_spec/SpecialCasing.txt
index c90d09acb3..1c04aacf97 100644
--- a/lib/stdlib/uc_spec/SpecialCasing.txt
+++ b/lib/stdlib/uc_spec/SpecialCasing.txt
@@ -1,6 +1,6 @@
-# SpecialCasing-11.0.0.txt
-# Date: 2018-02-22, 06:16:47 GMT
-# © 2018 Unicode®, Inc.
+# SpecialCasing-12.1.0.txt
+# Date: 2019-03-10, 10:53:28 GMT
+# © 2019 Unicode®, Inc.
# Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
# For terms of use, see http://www.unicode.org/terms_of_use.html
#
diff --git a/lib/stdlib/uc_spec/UnicodeData.txt b/lib/stdlib/uc_spec/UnicodeData.txt
index ec32fafbce..e65aec52f7 100644
--- a/lib/stdlib/uc_spec/UnicodeData.txt
+++ b/lib/stdlib/uc_spec/UnicodeData.txt
@@ -640,7 +640,7 @@
027F;LATIN SMALL LETTER REVERSED R WITH FISHHOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER REVERSED FISHHOOK R;;;;
0280;LATIN LETTER SMALL CAPITAL R;Ll;0;L;;;;;N;;;01A6;;01A6
0281;LATIN LETTER SMALL CAPITAL INVERTED R;Ll;0;L;;;;;N;;;;;
-0282;LATIN SMALL LETTER S WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER S HOOK;;;;
+0282;LATIN SMALL LETTER S WITH HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER S HOOK;;A7C5;;A7C5
0283;LATIN SMALL LETTER ESH;Ll;0;L;;;;;N;;;01A9;;01A9
0284;LATIN SMALL LETTER DOTLESS J WITH STROKE AND HOOK;Ll;0;L;;;;;N;LATIN SMALL LETTER DOTLESS J BAR HOOK;;;;
0285;LATIN SMALL LETTER SQUAT REVERSED ESH;Ll;0;L;;;;;N;;;;;
@@ -2809,6 +2809,7 @@
0C6D;TELUGU DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
0C6E;TELUGU DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
0C6F;TELUGU DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+0C77;TELUGU SIGN SIDDHAM;Po;0;L;;;;;N;;;;;
0C78;TELUGU FRACTION DIGIT ZERO FOR ODD POWERS OF FOUR;No;0;ON;;;;0;N;;;;;
0C79;TELUGU FRACTION DIGIT ONE FOR ODD POWERS OF FOUR;No;0;ON;;;;1;N;;;;;
0C7A;TELUGU FRACTION DIGIT TWO FOR ODD POWERS OF FOUR;No;0;ON;;;;2;N;;;;;
@@ -3203,14 +3204,24 @@
0E81;LAO LETTER KO;Lo;0;L;;;;;N;;;;;
0E82;LAO LETTER KHO SUNG;Lo;0;L;;;;;N;;;;;
0E84;LAO LETTER KHO TAM;Lo;0;L;;;;;N;;;;;
+0E86;LAO LETTER PALI GHA;Lo;0;L;;;;;N;;;;;
0E87;LAO LETTER NGO;Lo;0;L;;;;;N;;;;;
0E88;LAO LETTER CO;Lo;0;L;;;;;N;;;;;
+0E89;LAO LETTER PALI CHA;Lo;0;L;;;;;N;;;;;
0E8A;LAO LETTER SO TAM;Lo;0;L;;;;;N;;;;;
+0E8C;LAO LETTER PALI JHA;Lo;0;L;;;;;N;;;;;
0E8D;LAO LETTER NYO;Lo;0;L;;;;;N;;;;;
+0E8E;LAO LETTER PALI NYA;Lo;0;L;;;;;N;;;;;
+0E8F;LAO LETTER PALI TTA;Lo;0;L;;;;;N;;;;;
+0E90;LAO LETTER PALI TTHA;Lo;0;L;;;;;N;;;;;
+0E91;LAO LETTER PALI DDA;Lo;0;L;;;;;N;;;;;
+0E92;LAO LETTER PALI DDHA;Lo;0;L;;;;;N;;;;;
+0E93;LAO LETTER PALI NNA;Lo;0;L;;;;;N;;;;;
0E94;LAO LETTER DO;Lo;0;L;;;;;N;;;;;
0E95;LAO LETTER TO;Lo;0;L;;;;;N;;;;;
0E96;LAO LETTER THO SUNG;Lo;0;L;;;;;N;;;;;
0E97;LAO LETTER THO TAM;Lo;0;L;;;;;N;;;;;
+0E98;LAO LETTER PALI DHA;Lo;0;L;;;;;N;;;;;
0E99;LAO LETTER NO;Lo;0;L;;;;;N;;;;;
0E9A;LAO LETTER BO;Lo;0;L;;;;;N;;;;;
0E9B;LAO LETTER PO;Lo;0;L;;;;;N;;;;;
@@ -3218,13 +3229,17 @@
0E9D;LAO LETTER FO TAM;Lo;0;L;;;;;N;;;;;
0E9E;LAO LETTER PHO TAM;Lo;0;L;;;;;N;;;;;
0E9F;LAO LETTER FO SUNG;Lo;0;L;;;;;N;;;;;
+0EA0;LAO LETTER PALI BHA;Lo;0;L;;;;;N;;;;;
0EA1;LAO LETTER MO;Lo;0;L;;;;;N;;;;;
0EA2;LAO LETTER YO;Lo;0;L;;;;;N;;;;;
0EA3;LAO LETTER LO LING;Lo;0;L;;;;;N;;;;;
0EA5;LAO LETTER LO LOOT;Lo;0;L;;;;;N;;;;;
0EA7;LAO LETTER WO;Lo;0;L;;;;;N;;;;;
+0EA8;LAO LETTER SANSKRIT SHA;Lo;0;L;;;;;N;;;;;
+0EA9;LAO LETTER SANSKRIT SSA;Lo;0;L;;;;;N;;;;;
0EAA;LAO LETTER SO SUNG;Lo;0;L;;;;;N;;;;;
0EAB;LAO LETTER HO SUNG;Lo;0;L;;;;;N;;;;;
+0EAC;LAO LETTER PALI LLA;Lo;0;L;;;;;N;;;;;
0EAD;LAO LETTER O;Lo;0;L;;;;;N;;;;;
0EAE;LAO LETTER HO TAM;Lo;0;L;;;;;N;;;;;
0EAF;LAO ELLIPSIS;Lo;0;L;;;;;N;;;;;
@@ -3238,6 +3253,7 @@
0EB7;LAO VOWEL SIGN YY;Mn;0;NSM;;;;;N;;;;;
0EB8;LAO VOWEL SIGN U;Mn;118;NSM;;;;;N;;;;;
0EB9;LAO VOWEL SIGN UU;Mn;118;NSM;;;;;N;;;;;
+0EBA;LAO SIGN PALI VIRAMA;Mn;9;NSM;;;;;N;;;;;
0EBB;LAO VOWEL SIGN MAI KON;Mn;0;NSM;;;;;N;;;;;
0EBC;LAO SEMIVOWEL SIGN LO;Mn;0;NSM;;;;;N;;;;;
0EBD;LAO SEMIVOWEL SIGN NYO;Lo;0;L;;;;;N;;;;;
@@ -5079,7 +5095,7 @@
166A;CANADIAN SYLLABICS CARRIER TTSEE;Lo;0;L;;;;;N;;;;;
166B;CANADIAN SYLLABICS CARRIER TTSI;Lo;0;L;;;;;N;;;;;
166C;CANADIAN SYLLABICS CARRIER TTSA;Lo;0;L;;;;;N;;;;;
-166D;CANADIAN SYLLABICS CHI SIGN;Po;0;L;;;;;N;;;;;
+166D;CANADIAN SYLLABICS CHI SIGN;So;0;L;;;;;N;;;;;
166E;CANADIAN SYLLABICS FULL STOP;Po;0;L;;;;;N;;;;;
166F;CANADIAN SYLLABICS QAI;Lo;0;L;;;;;N;;;;;
1670;CANADIAN SYLLABICS NGAI;Lo;0;L;;;;;N;;;;;
@@ -6488,14 +6504,15 @@
1CEF;VEDIC SIGN LONG ANUSVARA;Lo;0;L;;;;;N;;;;;
1CF0;VEDIC SIGN RTHANG LONG ANUSVARA;Lo;0;L;;;;;N;;;;;
1CF1;VEDIC SIGN ANUSVARA UBHAYATO MUKHA;Lo;0;L;;;;;N;;;;;
-1CF2;VEDIC SIGN ARDHAVISARGA;Mc;0;L;;;;;N;;;;;
-1CF3;VEDIC SIGN ROTATED ARDHAVISARGA;Mc;0;L;;;;;N;;;;;
+1CF2;VEDIC SIGN ARDHAVISARGA;Lo;0;L;;;;;N;;;;;
+1CF3;VEDIC SIGN ROTATED ARDHAVISARGA;Lo;0;L;;;;;N;;;;;
1CF4;VEDIC TONE CANDRA ABOVE;Mn;230;NSM;;;;;N;;;;;
1CF5;VEDIC SIGN JIHVAMULIYA;Lo;0;L;;;;;N;;;;;
1CF6;VEDIC SIGN UPADHMANIYA;Lo;0;L;;;;;N;;;;;
1CF7;VEDIC SIGN ATIKRAMA;Mc;0;L;;;;;N;;;;;
1CF8;VEDIC TONE RING ABOVE;Mn;230;NSM;;;;;N;;;;;
1CF9;VEDIC TONE DOUBLE RING ABOVE;Mn;230;NSM;;;;;N;;;;;
+1CFA;VEDIC SIGN DOUBLE ANUSVARA ANTARGOMUKHA;Lo;0;L;;;;;N;;;;;
1D00;LATIN LETTER SMALL CAPITAL A;Ll;0;L;;;;;N;;;;;
1D01;LATIN LETTER SMALL CAPITAL AE;Ll;0;L;;;;;N;;;;;
1D02;LATIN SMALL LETTER TURNED AE;Ll;0;L;;;;;N;;;;;
@@ -6638,7 +6655,7 @@
1D8B;LATIN SMALL LETTER ESH WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;;
1D8C;LATIN SMALL LETTER V WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;;
1D8D;LATIN SMALL LETTER X WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;;
-1D8E;LATIN SMALL LETTER Z WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;;
+1D8E;LATIN SMALL LETTER Z WITH PALATAL HOOK;Ll;0;L;;;;;N;;;A7C6;;A7C6
1D8F;LATIN SMALL LETTER A WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;;
1D90;LATIN SMALL LETTER ALPHA WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;;
1D91;LATIN SMALL LETTER D WITH HOOK AND TAIL;Ll;0;L;;;;;N;;;;;
@@ -10165,6 +10182,7 @@
2BC6;BLACK MEDIUM DOWN-POINTING TRIANGLE CENTRED;So;0;ON;;;;;N;;;;;
2BC7;BLACK MEDIUM LEFT-POINTING TRIANGLE CENTRED;So;0;ON;;;;;N;;;;;
2BC8;BLACK MEDIUM RIGHT-POINTING TRIANGLE CENTRED;So;0;ON;;;;;N;;;;;
+2BC9;NEPTUNE FORM TWO;So;0;ON;;;;;N;;;;;
2BCA;TOP HALF BLACK CIRCLE;So;0;ON;;;;;N;;;;;
2BCB;BOTTOM HALF BLACK CIRCLE;So;0;ON;;;;;N;;;;;
2BCC;LIGHT FOUR POINTED BLACK CUSP;So;0;ON;;;;;N;;;;;
@@ -10218,6 +10236,7 @@
2BFC;DOUBLED SYMBOL;So;0;ON;;;;;N;;;;;
2BFD;PASSED SYMBOL;So;0;ON;;;;;N;;;;;
2BFE;REVERSED RIGHT ANGLE;So;0;ON;;;;;Y;;;;;
+2BFF;HELLSCHREIBER PAUSE SYMBOL;So;0;ON;;;;;N;;;;;
2C00;GLAGOLITIC CAPITAL LETTER AZU;Lu;0;L;;;;;N;;;;2C30;
2C01;GLAGOLITIC CAPITAL LETTER BUKY;Lu;0;L;;;;;N;;;;2C31;
2C02;GLAGOLITIC CAPITAL LETTER VEDE;Lu;0;L;;;;;N;;;;2C32;
@@ -10756,6 +10775,7 @@
2E4C;MEDIEVAL COMMA;Po;0;ON;;;;;N;;;;;
2E4D;PARAGRAPHUS MARK;Po;0;ON;;;;;N;;;;;
2E4E;PUNCTUS ELEVATUS MARK;Po;0;ON;;;;;N;;;;;
+2E4F;CORNISH VERSE DIVIDER;Po;0;ON;;;;;N;;;;;
2E80;CJK RADICAL REPEAT;So;0;ON;;;;;N;;;;;
2E81;CJK RADICAL CLIFF;So;0;ON;;;;;N;;;;;
2E82;CJK RADICAL SECOND ONE;So;0;ON;;;;;N;;;;;
@@ -11836,6 +11856,7 @@
32FC;CIRCLED KATAKANA WI;So;0;L;<circle> 30F0;;;;N;;;;;
32FD;CIRCLED KATAKANA WE;So;0;L;<circle> 30F1;;;;N;;;;;
32FE;CIRCLED KATAKANA WO;So;0;L;<circle> 30F2;;;;N;;;;;
+32FF;SQUARE ERA NAME REIWA;So;0;L;<square> 4EE4 548C;;;;N;;;;;
3300;SQUARE APAATO;So;0;L;<square> 30A2 30D1 30FC 30C8;;;;N;SQUARED APAATO;;;;
3301;SQUARE ARUHUA;So;0;L;<square> 30A2 30EB 30D5 30A1;;;;N;SQUARED ARUHUA;;;;
3302;SQUARE ANPEA;So;0;L;<square> 30A2 30F3 30DA 30A2;;;;N;SQUARED ANPEA;;;;
@@ -14060,7 +14081,7 @@ A790;LATIN CAPITAL LETTER N WITH DESCENDER;Lu;0;L;;;;;N;;;;A791;
A791;LATIN SMALL LETTER N WITH DESCENDER;Ll;0;L;;;;;N;;;A790;;A790
A792;LATIN CAPITAL LETTER C WITH BAR;Lu;0;L;;;;;N;;;;A793;
A793;LATIN SMALL LETTER C WITH BAR;Ll;0;L;;;;;N;;;A792;;A792
-A794;LATIN SMALL LETTER C WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;;
+A794;LATIN SMALL LETTER C WITH PALATAL HOOK;Ll;0;L;;;;;N;;;A7C4;;A7C4
A795;LATIN SMALL LETTER H WITH PALATAL HOOK;Ll;0;L;;;;;N;;;;;
A796;LATIN CAPITAL LETTER B WITH FLOURISH;Lu;0;L;;;;;N;;;;A797;
A797;LATIN SMALL LETTER B WITH FLOURISH;Ll;0;L;;;;;N;;;A796;;A796
@@ -14098,6 +14119,17 @@ A7B6;LATIN CAPITAL LETTER OMEGA;Lu;0;L;;;;;N;;;;A7B7;
A7B7;LATIN SMALL LETTER OMEGA;Ll;0;L;;;;;N;;;A7B6;;A7B6
A7B8;LATIN CAPITAL LETTER U WITH STROKE;Lu;0;L;;;;;N;;;;A7B9;
A7B9;LATIN SMALL LETTER U WITH STROKE;Ll;0;L;;;;;N;;;A7B8;;A7B8
+A7BA;LATIN CAPITAL LETTER GLOTTAL A;Lu;0;L;;;;;N;;;;A7BB;
+A7BB;LATIN SMALL LETTER GLOTTAL A;Ll;0;L;;;;;N;;;A7BA;;A7BA
+A7BC;LATIN CAPITAL LETTER GLOTTAL I;Lu;0;L;;;;;N;;;;A7BD;
+A7BD;LATIN SMALL LETTER GLOTTAL I;Ll;0;L;;;;;N;;;A7BC;;A7BC
+A7BE;LATIN CAPITAL LETTER GLOTTAL U;Lu;0;L;;;;;N;;;;A7BF;
+A7BF;LATIN SMALL LETTER GLOTTAL U;Ll;0;L;;;;;N;;;A7BE;;A7BE
+A7C2;LATIN CAPITAL LETTER ANGLICANA W;Lu;0;L;;;;;N;;;;A7C3;
+A7C3;LATIN SMALL LETTER ANGLICANA W;Ll;0;L;;;;;N;;;A7C2;;A7C2
+A7C4;LATIN CAPITAL LETTER C WITH PALATAL HOOK;Lu;0;L;;;;;N;;;;A794;
+A7C5;LATIN CAPITAL LETTER S WITH HOOK;Lu;0;L;;;;;N;;;;0282;
+A7C6;LATIN CAPITAL LETTER Z WITH PALATAL HOOK;Lu;0;L;;;;;N;;;;1D8E;
A7F7;LATIN EPIGRAPHIC LETTER SIDEWAYS I;Lo;0;L;;;;;N;;;;;
A7F8;MODIFIER LETTER CAPITAL H WITH STROKE;Lm;0;L;<super> 0126;;;;N;;;;;
A7F9;MODIFIER LETTER SMALL LIGATURE OE;Lm;0;L;<super> 0153;;;;N;;;;;
@@ -14506,7 +14538,7 @@ A9B9;JAVANESE VOWEL SIGN SUKU MENDUT;Mn;0;NSM;;;;;N;;;;;
A9BA;JAVANESE VOWEL SIGN TALING;Mc;0;L;;;;;N;;;;;
A9BB;JAVANESE VOWEL SIGN DIRGA MURE;Mc;0;L;;;;;N;;;;;
A9BC;JAVANESE VOWEL SIGN PEPET;Mn;0;NSM;;;;;N;;;;;
-A9BD;JAVANESE CONSONANT SIGN KERET;Mc;0;L;;;;;N;;;;;
+A9BD;JAVANESE CONSONANT SIGN KERET;Mn;0;NSM;;;;;N;;;;;
A9BE;JAVANESE CONSONANT SIGN PENGKAL;Mc;0;L;;;;;N;;;;;
A9BF;JAVANESE CONSONANT SIGN CAKRA;Mc;0;L;;;;;N;;;;;
A9C0;JAVANESE PANGKON;Mc;9;L;;;;;N;;;;;
@@ -14863,6 +14895,8 @@ AB62;LATIN SMALL LETTER OPEN OE;Ll;0;L;;;;;N;;;;;
AB63;LATIN SMALL LETTER UO;Ll;0;L;;;;;N;;;;;
AB64;LATIN SMALL LETTER INVERTED ALPHA;Ll;0;L;;;;;N;;;;;
AB65;GREEK LETTER SMALL CAPITAL OMEGA;Ll;0;L;;;;;N;;;;;
+AB66;LATIN SMALL LETTER DZ DIGRAPH WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;;
+AB67;LATIN SMALL LETTER TS DIGRAPH WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;;
AB70;CHEROKEE SMALL LETTER A;Ll;0;L;;;;;N;;;13A0;;13A0
AB71;CHEROKEE SMALL LETTER E;Ll;0;L;;;;;N;;;13A1;;13A1
AB72;CHEROKEE SMALL LETTER I;Ll;0;L;;;;;N;;;13A2;;13A2
@@ -19105,6 +19139,29 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
10F57;SOGDIAN PUNCTUATION CIRCLE WITH DOT;Po;0;AL;;;;;N;;;;;
10F58;SOGDIAN PUNCTUATION TWO CIRCLES WITH DOTS;Po;0;AL;;;;;N;;;;;
10F59;SOGDIAN PUNCTUATION HALF CIRCLE WITH DOT;Po;0;AL;;;;;N;;;;;
+10FE0;ELYMAIC LETTER ALEPH;Lo;0;R;;;;;N;;;;;
+10FE1;ELYMAIC LETTER BETH;Lo;0;R;;;;;N;;;;;
+10FE2;ELYMAIC LETTER GIMEL;Lo;0;R;;;;;N;;;;;
+10FE3;ELYMAIC LETTER DALETH;Lo;0;R;;;;;N;;;;;
+10FE4;ELYMAIC LETTER HE;Lo;0;R;;;;;N;;;;;
+10FE5;ELYMAIC LETTER WAW;Lo;0;R;;;;;N;;;;;
+10FE6;ELYMAIC LETTER ZAYIN;Lo;0;R;;;;;N;;;;;
+10FE7;ELYMAIC LETTER HETH;Lo;0;R;;;;;N;;;;;
+10FE8;ELYMAIC LETTER TETH;Lo;0;R;;;;;N;;;;;
+10FE9;ELYMAIC LETTER YODH;Lo;0;R;;;;;N;;;;;
+10FEA;ELYMAIC LETTER KAPH;Lo;0;R;;;;;N;;;;;
+10FEB;ELYMAIC LETTER LAMEDH;Lo;0;R;;;;;N;;;;;
+10FEC;ELYMAIC LETTER MEM;Lo;0;R;;;;;N;;;;;
+10FED;ELYMAIC LETTER NUN;Lo;0;R;;;;;N;;;;;
+10FEE;ELYMAIC LETTER SAMEKH;Lo;0;R;;;;;N;;;;;
+10FEF;ELYMAIC LETTER AYIN;Lo;0;R;;;;;N;;;;;
+10FF0;ELYMAIC LETTER PE;Lo;0;R;;;;;N;;;;;
+10FF1;ELYMAIC LETTER SADHE;Lo;0;R;;;;;N;;;;;
+10FF2;ELYMAIC LETTER QOPH;Lo;0;R;;;;;N;;;;;
+10FF3;ELYMAIC LETTER RESH;Lo;0;R;;;;;N;;;;;
+10FF4;ELYMAIC LETTER SHIN;Lo;0;R;;;;;N;;;;;
+10FF5;ELYMAIC LETTER TAW;Lo;0;R;;;;;N;;;;;
+10FF6;ELYMAIC LIGATURE ZAYIN-YODH;Lo;0;R;;;;;N;;;;;
11000;BRAHMI SIGN CANDRABINDU;Mc;0;L;;;;;N;;;;;
11001;BRAHMI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;;
11002;BRAHMI SIGN VISARGA;Mc;0;L;;;;;N;;;;;
@@ -19887,6 +19944,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1145B;NEWA PLACEHOLDER MARK;Po;0;L;;;;;N;;;;;
1145D;NEWA INSERTION SIGN;Po;0;L;;;;;N;;;;;
1145E;NEWA SANDHI MARK;Mn;230;NSM;;;;;N;;;;;
+1145F;NEWA LETTER VEDIC ANUSVARA;Lo;0;L;;;;;N;;;;;
11480;TIRHUTA ANJI;Lo;0;L;;;;;N;;;;;
11481;TIRHUTA LETTER A;Lo;0;L;;;;;N;;;;;
11482;TIRHUTA LETTER AA;Lo;0;L;;;;;N;;;;;
@@ -20209,6 +20267,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
116B5;TAKRI VOWEL SIGN AU;Mn;0;NSM;;;;;N;;;;;
116B6;TAKRI SIGN VIRAMA;Mc;9;L;;;;;N;;;;;
116B7;TAKRI SIGN NUKTA;Mn;7;NSM;;;;;N;;;;;
+116B8;TAKRI LETTER ARCHAIC KHA;Lo;0;L;;;;;N;;;;;
116C0;TAKRI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
116C1;TAKRI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
116C2;TAKRI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
@@ -20421,6 +20480,71 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
118F1;WARANG CITI NUMBER EIGHTY;No;0;L;;;;80;N;;;;;
118F2;WARANG CITI NUMBER NINETY;No;0;L;;;;90;N;;;;;
118FF;WARANG CITI OM;Lo;0;L;;;;;N;;;;;
+119A0;NANDINAGARI LETTER A;Lo;0;L;;;;;N;;;;;
+119A1;NANDINAGARI LETTER AA;Lo;0;L;;;;;N;;;;;
+119A2;NANDINAGARI LETTER I;Lo;0;L;;;;;N;;;;;
+119A3;NANDINAGARI LETTER II;Lo;0;L;;;;;N;;;;;
+119A4;NANDINAGARI LETTER U;Lo;0;L;;;;;N;;;;;
+119A5;NANDINAGARI LETTER UU;Lo;0;L;;;;;N;;;;;
+119A6;NANDINAGARI LETTER VOCALIC R;Lo;0;L;;;;;N;;;;;
+119A7;NANDINAGARI LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
+119AA;NANDINAGARI LETTER E;Lo;0;L;;;;;N;;;;;
+119AB;NANDINAGARI LETTER AI;Lo;0;L;;;;;N;;;;;
+119AC;NANDINAGARI LETTER O;Lo;0;L;;;;;N;;;;;
+119AD;NANDINAGARI LETTER AU;Lo;0;L;;;;;N;;;;;
+119AE;NANDINAGARI LETTER KA;Lo;0;L;;;;;N;;;;;
+119AF;NANDINAGARI LETTER KHA;Lo;0;L;;;;;N;;;;;
+119B0;NANDINAGARI LETTER GA;Lo;0;L;;;;;N;;;;;
+119B1;NANDINAGARI LETTER GHA;Lo;0;L;;;;;N;;;;;
+119B2;NANDINAGARI LETTER NGA;Lo;0;L;;;;;N;;;;;
+119B3;NANDINAGARI LETTER CA;Lo;0;L;;;;;N;;;;;
+119B4;NANDINAGARI LETTER CHA;Lo;0;L;;;;;N;;;;;
+119B5;NANDINAGARI LETTER JA;Lo;0;L;;;;;N;;;;;
+119B6;NANDINAGARI LETTER JHA;Lo;0;L;;;;;N;;;;;
+119B7;NANDINAGARI LETTER NYA;Lo;0;L;;;;;N;;;;;
+119B8;NANDINAGARI LETTER TTA;Lo;0;L;;;;;N;;;;;
+119B9;NANDINAGARI LETTER TTHA;Lo;0;L;;;;;N;;;;;
+119BA;NANDINAGARI LETTER DDA;Lo;0;L;;;;;N;;;;;
+119BB;NANDINAGARI LETTER DDHA;Lo;0;L;;;;;N;;;;;
+119BC;NANDINAGARI LETTER NNA;Lo;0;L;;;;;N;;;;;
+119BD;NANDINAGARI LETTER TA;Lo;0;L;;;;;N;;;;;
+119BE;NANDINAGARI LETTER THA;Lo;0;L;;;;;N;;;;;
+119BF;NANDINAGARI LETTER DA;Lo;0;L;;;;;N;;;;;
+119C0;NANDINAGARI LETTER DHA;Lo;0;L;;;;;N;;;;;
+119C1;NANDINAGARI LETTER NA;Lo;0;L;;;;;N;;;;;
+119C2;NANDINAGARI LETTER PA;Lo;0;L;;;;;N;;;;;
+119C3;NANDINAGARI LETTER PHA;Lo;0;L;;;;;N;;;;;
+119C4;NANDINAGARI LETTER BA;Lo;0;L;;;;;N;;;;;
+119C5;NANDINAGARI LETTER BHA;Lo;0;L;;;;;N;;;;;
+119C6;NANDINAGARI LETTER MA;Lo;0;L;;;;;N;;;;;
+119C7;NANDINAGARI LETTER YA;Lo;0;L;;;;;N;;;;;
+119C8;NANDINAGARI LETTER RA;Lo;0;L;;;;;N;;;;;
+119C9;NANDINAGARI LETTER LA;Lo;0;L;;;;;N;;;;;
+119CA;NANDINAGARI LETTER VA;Lo;0;L;;;;;N;;;;;
+119CB;NANDINAGARI LETTER SHA;Lo;0;L;;;;;N;;;;;
+119CC;NANDINAGARI LETTER SSA;Lo;0;L;;;;;N;;;;;
+119CD;NANDINAGARI LETTER SA;Lo;0;L;;;;;N;;;;;
+119CE;NANDINAGARI LETTER HA;Lo;0;L;;;;;N;;;;;
+119CF;NANDINAGARI LETTER LLA;Lo;0;L;;;;;N;;;;;
+119D0;NANDINAGARI LETTER RRA;Lo;0;L;;;;;N;;;;;
+119D1;NANDINAGARI VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+119D2;NANDINAGARI VOWEL SIGN I;Mc;0;L;;;;;N;;;;;
+119D3;NANDINAGARI VOWEL SIGN II;Mc;0;L;;;;;N;;;;;
+119D4;NANDINAGARI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+119D5;NANDINAGARI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+119D6;NANDINAGARI VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;;
+119D7;NANDINAGARI VOWEL SIGN VOCALIC RR;Mn;0;NSM;;;;;N;;;;;
+119DA;NANDINAGARI VOWEL SIGN E;Mn;0;NSM;;;;;N;;;;;
+119DB;NANDINAGARI VOWEL SIGN AI;Mn;0;NSM;;;;;N;;;;;
+119DC;NANDINAGARI VOWEL SIGN O;Mc;0;L;;;;;N;;;;;
+119DD;NANDINAGARI VOWEL SIGN AU;Mc;0;L;;;;;N;;;;;
+119DE;NANDINAGARI SIGN ANUSVARA;Mc;0;L;;;;;N;;;;;
+119DF;NANDINAGARI SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+119E0;NANDINAGARI SIGN VIRAMA;Mn;9;NSM;;;;;N;;;;;
+119E1;NANDINAGARI SIGN AVAGRAHA;Lo;0;L;;;;;N;;;;;
+119E2;NANDINAGARI SIGN SIDDHAM;Po;0;L;;;;;N;;;;;
+119E3;NANDINAGARI HEADSTROKE;Lo;0;L;;;;;N;;;;;
+119E4;NANDINAGARI VOWEL SIGN PRISHTHAMATRA E;Mc;0;L;;;;;N;;;;;
11A00;ZANABAZAR SQUARE LETTER A;Lo;0;L;;;;;N;;;;;
11A01;ZANABAZAR SQUARE VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
11A02;ZANABAZAR SQUARE VOWEL SIGN UE;Mn;0;NSM;;;;;N;;;;;
@@ -20545,6 +20669,8 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
11A81;SOYOMBO LETTER SA;Lo;0;L;;;;;N;;;;;
11A82;SOYOMBO LETTER HA;Lo;0;L;;;;;N;;;;;
11A83;SOYOMBO LETTER KSSA;Lo;0;L;;;;;N;;;;;
+11A84;SOYOMBO SIGN JIHVAMULIYA;Lo;0;L;;;;;N;;;;;
+11A85;SOYOMBO SIGN UPADHMANIYA;Lo;0;L;;;;;N;;;;;
11A86;SOYOMBO CLUSTER-INITIAL LETTER RA;Lo;0;L;;;;;N;;;;;
11A87;SOYOMBO CLUSTER-INITIAL LETTER LA;Lo;0;L;;;;;N;;;;;
11A88;SOYOMBO CLUSTER-INITIAL LETTER SHA;Lo;0;L;;;;;N;;;;;
@@ -20959,6 +21085,57 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
11EF6;MAKASAR VOWEL SIGN O;Mc;0;L;;;;;N;;;;;
11EF7;MAKASAR PASSIMBANG;Po;0;L;;;;;N;;;;;
11EF8;MAKASAR END OF SECTION;Po;0;L;;;;;N;;;;;
+11FC0;TAMIL FRACTION ONE THREE-HUNDRED-AND-TWENTIETH;No;0;L;;;;1/320;N;;;;;
+11FC1;TAMIL FRACTION ONE ONE-HUNDRED-AND-SIXTIETH;No;0;L;;;;1/160;N;;;;;
+11FC2;TAMIL FRACTION ONE EIGHTIETH;No;0;L;;;;1/80;N;;;;;
+11FC3;TAMIL FRACTION ONE SIXTY-FOURTH;No;0;L;;;;1/64;N;;;;;
+11FC4;TAMIL FRACTION ONE FORTIETH;No;0;L;;;;1/40;N;;;;;
+11FC5;TAMIL FRACTION ONE THIRTY-SECOND;No;0;L;;;;1/32;N;;;;;
+11FC6;TAMIL FRACTION THREE EIGHTIETHS;No;0;L;;;;3/80;N;;;;;
+11FC7;TAMIL FRACTION THREE SIXTY-FOURTHS;No;0;L;;;;3/64;N;;;;;
+11FC8;TAMIL FRACTION ONE TWENTIETH;No;0;L;;;;1/20;N;;;;;
+11FC9;TAMIL FRACTION ONE SIXTEENTH-1;No;0;L;;;;1/16;N;;;;;
+11FCA;TAMIL FRACTION ONE SIXTEENTH-2;No;0;L;;;;1/16;N;;;;;
+11FCB;TAMIL FRACTION ONE TENTH;No;0;L;;;;1/10;N;;;;;
+11FCC;TAMIL FRACTION ONE EIGHTH;No;0;L;;;;1/8;N;;;;;
+11FCD;TAMIL FRACTION THREE TWENTIETHS;No;0;L;;;;3/20;N;;;;;
+11FCE;TAMIL FRACTION THREE SIXTEENTHS;No;0;L;;;;3/16;N;;;;;
+11FCF;TAMIL FRACTION ONE FIFTH;No;0;L;;;;1/5;N;;;;;
+11FD0;TAMIL FRACTION ONE QUARTER;No;0;L;;;;1/4;N;;;;;
+11FD1;TAMIL FRACTION ONE HALF-1;No;0;L;;;;1/2;N;;;;;
+11FD2;TAMIL FRACTION ONE HALF-2;No;0;L;;;;1/2;N;;;;;
+11FD3;TAMIL FRACTION THREE QUARTERS;No;0;L;;;;3/4;N;;;;;
+11FD4;TAMIL FRACTION DOWNSCALING FACTOR KIIZH;No;0;L;;;;1/320;N;;;;;
+11FD5;TAMIL SIGN NEL;So;0;ON;;;;;N;;;;;
+11FD6;TAMIL SIGN CEVITU;So;0;ON;;;;;N;;;;;
+11FD7;TAMIL SIGN AAZHAAKKU;So;0;ON;;;;;N;;;;;
+11FD8;TAMIL SIGN UZHAKKU;So;0;ON;;;;;N;;;;;
+11FD9;TAMIL SIGN MUUVUZHAKKU;So;0;ON;;;;;N;;;;;
+11FDA;TAMIL SIGN KURUNI;So;0;ON;;;;;N;;;;;
+11FDB;TAMIL SIGN PATHAKKU;So;0;ON;;;;;N;;;;;
+11FDC;TAMIL SIGN MUKKURUNI;So;0;ON;;;;;N;;;;;
+11FDD;TAMIL SIGN KAACU;Sc;0;ET;;;;;N;;;;;
+11FDE;TAMIL SIGN PANAM;Sc;0;ET;;;;;N;;;;;
+11FDF;TAMIL SIGN PON;Sc;0;ET;;;;;N;;;;;
+11FE0;TAMIL SIGN VARAAKAN;Sc;0;ET;;;;;N;;;;;
+11FE1;TAMIL SIGN PAARAM;So;0;ON;;;;;N;;;;;
+11FE2;TAMIL SIGN KUZHI;So;0;ON;;;;;N;;;;;
+11FE3;TAMIL SIGN VELI;So;0;ON;;;;;N;;;;;
+11FE4;TAMIL WET CULTIVATION SIGN;So;0;ON;;;;;N;;;;;
+11FE5;TAMIL DRY CULTIVATION SIGN;So;0;ON;;;;;N;;;;;
+11FE6;TAMIL LAND SIGN;So;0;ON;;;;;N;;;;;
+11FE7;TAMIL SALT PAN SIGN;So;0;ON;;;;;N;;;;;
+11FE8;TAMIL TRADITIONAL CREDIT SIGN;So;0;ON;;;;;N;;;;;
+11FE9;TAMIL TRADITIONAL NUMBER SIGN;So;0;ON;;;;;N;;;;;
+11FEA;TAMIL CURRENT SIGN;So;0;ON;;;;;N;;;;;
+11FEB;TAMIL AND ODD SIGN;So;0;ON;;;;;N;;;;;
+11FEC;TAMIL SPENT SIGN;So;0;ON;;;;;N;;;;;
+11FED;TAMIL TOTAL SIGN;So;0;ON;;;;;N;;;;;
+11FEE;TAMIL IN POSSESSION SIGN;So;0;ON;;;;;N;;;;;
+11FEF;TAMIL STARTING FROM SIGN;So;0;ON;;;;;N;;;;;
+11FF0;TAMIL SIGN MUTHALIYA;So;0;ON;;;;;N;;;;;
+11FF1;TAMIL SIGN VAKAIYARAA;So;0;ON;;;;;N;;;;;
+11FFF;TAMIL PUNCTUATION END OF TEXT;Po;0;L;;;;;N;;;;;
12000;CUNEIFORM SIGN A;Lo;0;L;;;;;N;;;;;
12001;CUNEIFORM SIGN A TIMES A;Lo;0;L;;;;;N;;;;;
12002;CUNEIFORM SIGN A TIMES BAD;Lo;0;L;;;;;N;;;;;
@@ -23264,6 +23441,15 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1342C;EGYPTIAN HIEROGLYPH AA030;Lo;0;L;;;;;N;;;;;
1342D;EGYPTIAN HIEROGLYPH AA031;Lo;0;L;;;;;N;;;;;
1342E;EGYPTIAN HIEROGLYPH AA032;Lo;0;L;;;;;N;;;;;
+13430;EGYPTIAN HIEROGLYPH VERTICAL JOINER;Cf;0;L;;;;;N;;;;;
+13431;EGYPTIAN HIEROGLYPH HORIZONTAL JOINER;Cf;0;L;;;;;N;;;;;
+13432;EGYPTIAN HIEROGLYPH INSERT AT TOP START;Cf;0;L;;;;;N;;;;;
+13433;EGYPTIAN HIEROGLYPH INSERT AT BOTTOM START;Cf;0;L;;;;;N;;;;;
+13434;EGYPTIAN HIEROGLYPH INSERT AT TOP END;Cf;0;L;;;;;N;;;;;
+13435;EGYPTIAN HIEROGLYPH INSERT AT BOTTOM END;Cf;0;L;;;;;N;;;;;
+13436;EGYPTIAN HIEROGLYPH OVERLAY MIDDLE;Cf;0;L;;;;;N;;;;;
+13437;EGYPTIAN HIEROGLYPH BEGIN SEGMENT;Cf;0;L;;;;;N;;;;;
+13438;EGYPTIAN HIEROGLYPH END SEGMENT;Cf;0;L;;;;;N;;;;;
14400;ANATOLIAN HIEROGLYPH A001;Lo;0;L;;;;;N;;;;;
14401;ANATOLIAN HIEROGLYPH A002;Lo;0;L;;;;;N;;;;;
14402;ANATOLIAN HIEROGLYPH A003;Lo;0;L;;;;;N;;;;;
@@ -24782,6 +24968,13 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
16F42;MIAO LETTER WA;Lo;0;L;;;;;N;;;;;
16F43;MIAO LETTER AH;Lo;0;L;;;;;N;;;;;
16F44;MIAO LETTER HHA;Lo;0;L;;;;;N;;;;;
+16F45;MIAO LETTER BRI;Lo;0;L;;;;;N;;;;;
+16F46;MIAO LETTER SYI;Lo;0;L;;;;;N;;;;;
+16F47;MIAO LETTER DZYI;Lo;0;L;;;;;N;;;;;
+16F48;MIAO LETTER TE;Lo;0;L;;;;;N;;;;;
+16F49;MIAO LETTER TSE;Lo;0;L;;;;;N;;;;;
+16F4A;MIAO LETTER RTE;Lo;0;L;;;;;N;;;;;
+16F4F;MIAO SIGN CONSONANT MODIFIER BAR;Mn;0;NSM;;;;;N;;;;;
16F50;MIAO LETTER NASALIZATION;Lo;0;L;;;;;N;;;;;
16F51;MIAO SIGN ASPIRATION;Mc;0;L;;;;;N;;;;;
16F52;MIAO SIGN REFORMED VOICING;Mc;0;L;;;;;N;;;;;
@@ -24829,6 +25022,15 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
16F7C;MIAO VOWEL SIGN OU;Mc;0;L;;;;;N;;;;;
16F7D;MIAO VOWEL SIGN N;Mc;0;L;;;;;N;;;;;
16F7E;MIAO VOWEL SIGN NG;Mc;0;L;;;;;N;;;;;
+16F7F;MIAO VOWEL SIGN UOG;Mc;0;L;;;;;N;;;;;
+16F80;MIAO VOWEL SIGN YUI;Mc;0;L;;;;;N;;;;;
+16F81;MIAO VOWEL SIGN OG;Mc;0;L;;;;;N;;;;;
+16F82;MIAO VOWEL SIGN OER;Mc;0;L;;;;;N;;;;;
+16F83;MIAO VOWEL SIGN VW;Mc;0;L;;;;;N;;;;;
+16F84;MIAO VOWEL SIGN IG;Mc;0;L;;;;;N;;;;;
+16F85;MIAO VOWEL SIGN EA;Mc;0;L;;;;;N;;;;;
+16F86;MIAO VOWEL SIGN IONG;Mc;0;L;;;;;N;;;;;
+16F87;MIAO VOWEL SIGN UI;Mc;0;L;;;;;N;;;;;
16F8F;MIAO TONE RIGHT;Mn;0;NSM;;;;;N;;;;;
16F90;MIAO TONE TOP RIGHT;Mn;0;NSM;;;;;N;;;;;
16F91;MIAO TONE ABOVE;Mn;0;NSM;;;;;N;;;;;
@@ -24848,8 +25050,10 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
16F9F;MIAO LETTER REFORMED TONE-8;Lm;0;L;;;;;N;;;;;
16FE0;TANGUT ITERATION MARK;Lm;0;L;;;;;N;;;;;
16FE1;NUSHU ITERATION MARK;Lm;0;L;;;;;N;;;;;
+16FE2;OLD CHINESE HOOK MARK;Po;0;ON;;;;;N;;;;;
+16FE3;OLD CHINESE ITERATION MARK;Lm;0;L;;;;;N;;;;;
17000;<Tangut Ideograph, First>;Lo;0;L;;;;;N;;;;;
-187F1;<Tangut Ideograph, Last>;Lo;0;L;;;;;N;;;;;
+187F7;<Tangut Ideograph, Last>;Lo;0;L;;;;;N;;;;;
18800;TANGUT COMPONENT-001;Lo;0;L;;;;;N;;;;;
18801;TANGUT COMPONENT-002;Lo;0;L;;;;;N;;;;;
18802;TANGUT COMPONENT-003;Lo;0;L;;;;;N;;;;;
@@ -25892,6 +26096,13 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1B11C;HENTAIGANA LETTER WO-7;Lo;0;L;;;;;N;;;;;
1B11D;HENTAIGANA LETTER N-MU-MO-1;Lo;0;L;;;;;N;;;;;
1B11E;HENTAIGANA LETTER N-MU-MO-2;Lo;0;L;;;;;N;;;;;
+1B150;HIRAGANA LETTER SMALL WI;Lo;0;L;;;;;N;;;;;
+1B151;HIRAGANA LETTER SMALL WE;Lo;0;L;;;;;N;;;;;
+1B152;HIRAGANA LETTER SMALL WO;Lo;0;L;;;;;N;;;;;
+1B164;KATAKANA LETTER SMALL WI;Lo;0;L;;;;;N;;;;;
+1B165;KATAKANA LETTER SMALL WE;Lo;0;L;;;;;N;;;;;
+1B166;KATAKANA LETTER SMALL WO;Lo;0;L;;;;;N;;;;;
+1B167;KATAKANA LETTER SMALL N;Lo;0;L;;;;;N;;;;;
1B170;NUSHU CHARACTER-1B170;Lo;0;L;;;;;N;;;;;
1B171;NUSHU CHARACTER-1B171;Lo;0;L;;;;;N;;;;;
1B172;NUSHU CHARACTER-1B172;Lo;0;L;;;;;N;;;;;
@@ -28820,6 +29031,136 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1E028;COMBINING GLAGOLITIC LETTER BIG YUS;Mn;230;NSM;;;;;N;;;;;
1E029;COMBINING GLAGOLITIC LETTER IOTATED BIG YUS;Mn;230;NSM;;;;;N;;;;;
1E02A;COMBINING GLAGOLITIC LETTER FITA;Mn;230;NSM;;;;;N;;;;;
+1E100;NYIAKENG PUACHUE HMONG LETTER MA;Lo;0;L;;;;;N;;;;;
+1E101;NYIAKENG PUACHUE HMONG LETTER TSA;Lo;0;L;;;;;N;;;;;
+1E102;NYIAKENG PUACHUE HMONG LETTER NTA;Lo;0;L;;;;;N;;;;;
+1E103;NYIAKENG PUACHUE HMONG LETTER TA;Lo;0;L;;;;;N;;;;;
+1E104;NYIAKENG PUACHUE HMONG LETTER HA;Lo;0;L;;;;;N;;;;;
+1E105;NYIAKENG PUACHUE HMONG LETTER NA;Lo;0;L;;;;;N;;;;;
+1E106;NYIAKENG PUACHUE HMONG LETTER XA;Lo;0;L;;;;;N;;;;;
+1E107;NYIAKENG PUACHUE HMONG LETTER NKA;Lo;0;L;;;;;N;;;;;
+1E108;NYIAKENG PUACHUE HMONG LETTER CA;Lo;0;L;;;;;N;;;;;
+1E109;NYIAKENG PUACHUE HMONG LETTER LA;Lo;0;L;;;;;N;;;;;
+1E10A;NYIAKENG PUACHUE HMONG LETTER SA;Lo;0;L;;;;;N;;;;;
+1E10B;NYIAKENG PUACHUE HMONG LETTER ZA;Lo;0;L;;;;;N;;;;;
+1E10C;NYIAKENG PUACHUE HMONG LETTER NCA;Lo;0;L;;;;;N;;;;;
+1E10D;NYIAKENG PUACHUE HMONG LETTER NTSA;Lo;0;L;;;;;N;;;;;
+1E10E;NYIAKENG PUACHUE HMONG LETTER KA;Lo;0;L;;;;;N;;;;;
+1E10F;NYIAKENG PUACHUE HMONG LETTER DA;Lo;0;L;;;;;N;;;;;
+1E110;NYIAKENG PUACHUE HMONG LETTER NYA;Lo;0;L;;;;;N;;;;;
+1E111;NYIAKENG PUACHUE HMONG LETTER NRA;Lo;0;L;;;;;N;;;;;
+1E112;NYIAKENG PUACHUE HMONG LETTER VA;Lo;0;L;;;;;N;;;;;
+1E113;NYIAKENG PUACHUE HMONG LETTER NTXA;Lo;0;L;;;;;N;;;;;
+1E114;NYIAKENG PUACHUE HMONG LETTER TXA;Lo;0;L;;;;;N;;;;;
+1E115;NYIAKENG PUACHUE HMONG LETTER FA;Lo;0;L;;;;;N;;;;;
+1E116;NYIAKENG PUACHUE HMONG LETTER RA;Lo;0;L;;;;;N;;;;;
+1E117;NYIAKENG PUACHUE HMONG LETTER QA;Lo;0;L;;;;;N;;;;;
+1E118;NYIAKENG PUACHUE HMONG LETTER YA;Lo;0;L;;;;;N;;;;;
+1E119;NYIAKENG PUACHUE HMONG LETTER NQA;Lo;0;L;;;;;N;;;;;
+1E11A;NYIAKENG PUACHUE HMONG LETTER PA;Lo;0;L;;;;;N;;;;;
+1E11B;NYIAKENG PUACHUE HMONG LETTER XYA;Lo;0;L;;;;;N;;;;;
+1E11C;NYIAKENG PUACHUE HMONG LETTER NPA;Lo;0;L;;;;;N;;;;;
+1E11D;NYIAKENG PUACHUE HMONG LETTER DLA;Lo;0;L;;;;;N;;;;;
+1E11E;NYIAKENG PUACHUE HMONG LETTER NPLA;Lo;0;L;;;;;N;;;;;
+1E11F;NYIAKENG PUACHUE HMONG LETTER HAH;Lo;0;L;;;;;N;;;;;
+1E120;NYIAKENG PUACHUE HMONG LETTER MLA;Lo;0;L;;;;;N;;;;;
+1E121;NYIAKENG PUACHUE HMONG LETTER PLA;Lo;0;L;;;;;N;;;;;
+1E122;NYIAKENG PUACHUE HMONG LETTER GA;Lo;0;L;;;;;N;;;;;
+1E123;NYIAKENG PUACHUE HMONG LETTER RRA;Lo;0;L;;;;;N;;;;;
+1E124;NYIAKENG PUACHUE HMONG LETTER A;Lo;0;L;;;;;N;;;;;
+1E125;NYIAKENG PUACHUE HMONG LETTER AA;Lo;0;L;;;;;N;;;;;
+1E126;NYIAKENG PUACHUE HMONG LETTER I;Lo;0;L;;;;;N;;;;;
+1E127;NYIAKENG PUACHUE HMONG LETTER U;Lo;0;L;;;;;N;;;;;
+1E128;NYIAKENG PUACHUE HMONG LETTER O;Lo;0;L;;;;;N;;;;;
+1E129;NYIAKENG PUACHUE HMONG LETTER OO;Lo;0;L;;;;;N;;;;;
+1E12A;NYIAKENG PUACHUE HMONG LETTER E;Lo;0;L;;;;;N;;;;;
+1E12B;NYIAKENG PUACHUE HMONG LETTER EE;Lo;0;L;;;;;N;;;;;
+1E12C;NYIAKENG PUACHUE HMONG LETTER W;Lo;0;L;;;;;N;;;;;
+1E130;NYIAKENG PUACHUE HMONG TONE-B;Mn;230;NSM;;;;;N;;;;;
+1E131;NYIAKENG PUACHUE HMONG TONE-M;Mn;230;NSM;;;;;N;;;;;
+1E132;NYIAKENG PUACHUE HMONG TONE-J;Mn;230;NSM;;;;;N;;;;;
+1E133;NYIAKENG PUACHUE HMONG TONE-V;Mn;230;NSM;;;;;N;;;;;
+1E134;NYIAKENG PUACHUE HMONG TONE-S;Mn;230;NSM;;;;;N;;;;;
+1E135;NYIAKENG PUACHUE HMONG TONE-G;Mn;230;NSM;;;;;N;;;;;
+1E136;NYIAKENG PUACHUE HMONG TONE-D;Mn;230;NSM;;;;;N;;;;;
+1E137;NYIAKENG PUACHUE HMONG SIGN FOR PERSON;Lm;0;L;;;;;N;;;;;
+1E138;NYIAKENG PUACHUE HMONG SIGN FOR THING;Lm;0;L;;;;;N;;;;;
+1E139;NYIAKENG PUACHUE HMONG SIGN FOR LOCATION;Lm;0;L;;;;;N;;;;;
+1E13A;NYIAKENG PUACHUE HMONG SIGN FOR ANIMAL;Lm;0;L;;;;;N;;;;;
+1E13B;NYIAKENG PUACHUE HMONG SIGN FOR INVERTEBRATE;Lm;0;L;;;;;N;;;;;
+1E13C;NYIAKENG PUACHUE HMONG SIGN XW XW;Lm;0;L;;;;;N;;;;;
+1E13D;NYIAKENG PUACHUE HMONG SYLLABLE LENGTHENER;Lm;0;L;;;;;N;;;;;
+1E140;NYIAKENG PUACHUE HMONG DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+1E141;NYIAKENG PUACHUE HMONG DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+1E142;NYIAKENG PUACHUE HMONG DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+1E143;NYIAKENG PUACHUE HMONG DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+1E144;NYIAKENG PUACHUE HMONG DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+1E145;NYIAKENG PUACHUE HMONG DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+1E146;NYIAKENG PUACHUE HMONG DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+1E147;NYIAKENG PUACHUE HMONG DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+1E148;NYIAKENG PUACHUE HMONG DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+1E149;NYIAKENG PUACHUE HMONG DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+1E14E;NYIAKENG PUACHUE HMONG LOGOGRAM NYAJ;Lo;0;L;;;;;N;;;;;
+1E14F;NYIAKENG PUACHUE HMONG CIRCLED CA;So;0;L;;;;;N;;;;;
+1E2C0;WANCHO LETTER AA;Lo;0;L;;;;;N;;;;;
+1E2C1;WANCHO LETTER A;Lo;0;L;;;;;N;;;;;
+1E2C2;WANCHO LETTER BA;Lo;0;L;;;;;N;;;;;
+1E2C3;WANCHO LETTER CA;Lo;0;L;;;;;N;;;;;
+1E2C4;WANCHO LETTER DA;Lo;0;L;;;;;N;;;;;
+1E2C5;WANCHO LETTER GA;Lo;0;L;;;;;N;;;;;
+1E2C6;WANCHO LETTER YA;Lo;0;L;;;;;N;;;;;
+1E2C7;WANCHO LETTER PHA;Lo;0;L;;;;;N;;;;;
+1E2C8;WANCHO LETTER LA;Lo;0;L;;;;;N;;;;;
+1E2C9;WANCHO LETTER NA;Lo;0;L;;;;;N;;;;;
+1E2CA;WANCHO LETTER PA;Lo;0;L;;;;;N;;;;;
+1E2CB;WANCHO LETTER TA;Lo;0;L;;;;;N;;;;;
+1E2CC;WANCHO LETTER THA;Lo;0;L;;;;;N;;;;;
+1E2CD;WANCHO LETTER FA;Lo;0;L;;;;;N;;;;;
+1E2CE;WANCHO LETTER SA;Lo;0;L;;;;;N;;;;;
+1E2CF;WANCHO LETTER SHA;Lo;0;L;;;;;N;;;;;
+1E2D0;WANCHO LETTER JA;Lo;0;L;;;;;N;;;;;
+1E2D1;WANCHO LETTER ZA;Lo;0;L;;;;;N;;;;;
+1E2D2;WANCHO LETTER WA;Lo;0;L;;;;;N;;;;;
+1E2D3;WANCHO LETTER VA;Lo;0;L;;;;;N;;;;;
+1E2D4;WANCHO LETTER KA;Lo;0;L;;;;;N;;;;;
+1E2D5;WANCHO LETTER O;Lo;0;L;;;;;N;;;;;
+1E2D6;WANCHO LETTER AU;Lo;0;L;;;;;N;;;;;
+1E2D7;WANCHO LETTER RA;Lo;0;L;;;;;N;;;;;
+1E2D8;WANCHO LETTER MA;Lo;0;L;;;;;N;;;;;
+1E2D9;WANCHO LETTER KHA;Lo;0;L;;;;;N;;;;;
+1E2DA;WANCHO LETTER HA;Lo;0;L;;;;;N;;;;;
+1E2DB;WANCHO LETTER E;Lo;0;L;;;;;N;;;;;
+1E2DC;WANCHO LETTER I;Lo;0;L;;;;;N;;;;;
+1E2DD;WANCHO LETTER NGA;Lo;0;L;;;;;N;;;;;
+1E2DE;WANCHO LETTER U;Lo;0;L;;;;;N;;;;;
+1E2DF;WANCHO LETTER LLHA;Lo;0;L;;;;;N;;;;;
+1E2E0;WANCHO LETTER TSA;Lo;0;L;;;;;N;;;;;
+1E2E1;WANCHO LETTER TRA;Lo;0;L;;;;;N;;;;;
+1E2E2;WANCHO LETTER ONG;Lo;0;L;;;;;N;;;;;
+1E2E3;WANCHO LETTER AANG;Lo;0;L;;;;;N;;;;;
+1E2E4;WANCHO LETTER ANG;Lo;0;L;;;;;N;;;;;
+1E2E5;WANCHO LETTER ING;Lo;0;L;;;;;N;;;;;
+1E2E6;WANCHO LETTER ON;Lo;0;L;;;;;N;;;;;
+1E2E7;WANCHO LETTER EN;Lo;0;L;;;;;N;;;;;
+1E2E8;WANCHO LETTER AAN;Lo;0;L;;;;;N;;;;;
+1E2E9;WANCHO LETTER NYA;Lo;0;L;;;;;N;;;;;
+1E2EA;WANCHO LETTER UEN;Lo;0;L;;;;;N;;;;;
+1E2EB;WANCHO LETTER YIH;Lo;0;L;;;;;N;;;;;
+1E2EC;WANCHO TONE TUP;Mn;230;NSM;;;;;N;;;;;
+1E2ED;WANCHO TONE TUPNI;Mn;230;NSM;;;;;N;;;;;
+1E2EE;WANCHO TONE KOI;Mn;230;NSM;;;;;N;;;;;
+1E2EF;WANCHO TONE KOINI;Mn;230;NSM;;;;;N;;;;;
+1E2F0;WANCHO DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+1E2F1;WANCHO DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+1E2F2;WANCHO DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+1E2F3;WANCHO DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+1E2F4;WANCHO DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+1E2F5;WANCHO DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+1E2F6;WANCHO DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+1E2F7;WANCHO DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+1E2F8;WANCHO DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+1E2F9;WANCHO DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
+1E2FF;WANCHO NGUN SIGN;Sc;0;ET;;;;;N;;;;;
1E800;MENDE KIKAKUI SYLLABLE M001 KI;Lo;0;R;;;;;N;;;;;
1E801;MENDE KIKAKUI SYLLABLE M002 KA;Lo;0;R;;;;;N;;;;;
1E802;MENDE KIKAKUI SYLLABLE M003 KU;Lo;0;R;;;;;N;;;;;
@@ -29108,6 +29449,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1E948;ADLAM CONSONANT MODIFIER;Mn;230;NSM;;;;;N;;;;;
1E949;ADLAM GEMINATE CONSONANT MODIFIER;Mn;230;NSM;;;;;N;;;;;
1E94A;ADLAM NUKTA;Mn;7;NSM;;;;;N;;;;;
+1E94B;ADLAM NASALIZATION MARK;Lm;0;R;;;;;N;;;;;
1E950;ADLAM DIGIT ZERO;Nd;0;R;;0;0;0;N;;;;;
1E951;ADLAM DIGIT ONE;Nd;0;R;;1;1;1;N;;;;;
1E952;ADLAM DIGIT TWO;Nd;0;R;;2;2;2;N;;;;;
@@ -29188,6 +29530,67 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1ECB2;INDIC SIYAQ NUMBER ALTERNATE TWO;No;0;AL;;;;2;N;;;;;
1ECB3;INDIC SIYAQ NUMBER ALTERNATE TEN THOUSAND;No;0;AL;;;;10000;N;;;;;
1ECB4;INDIC SIYAQ ALTERNATE LAKH MARK;No;0;AL;;;;100000;N;;;;;
+1ED01;OTTOMAN SIYAQ NUMBER ONE;No;0;AL;;;;1;N;;;;;
+1ED02;OTTOMAN SIYAQ NUMBER TWO;No;0;AL;;;;2;N;;;;;
+1ED03;OTTOMAN SIYAQ NUMBER THREE;No;0;AL;;;;3;N;;;;;
+1ED04;OTTOMAN SIYAQ NUMBER FOUR;No;0;AL;;;;4;N;;;;;
+1ED05;OTTOMAN SIYAQ NUMBER FIVE;No;0;AL;;;;5;N;;;;;
+1ED06;OTTOMAN SIYAQ NUMBER SIX;No;0;AL;;;;6;N;;;;;
+1ED07;OTTOMAN SIYAQ NUMBER SEVEN;No;0;AL;;;;7;N;;;;;
+1ED08;OTTOMAN SIYAQ NUMBER EIGHT;No;0;AL;;;;8;N;;;;;
+1ED09;OTTOMAN SIYAQ NUMBER NINE;No;0;AL;;;;9;N;;;;;
+1ED0A;OTTOMAN SIYAQ NUMBER TEN;No;0;AL;;;;10;N;;;;;
+1ED0B;OTTOMAN SIYAQ NUMBER TWENTY;No;0;AL;;;;20;N;;;;;
+1ED0C;OTTOMAN SIYAQ NUMBER THIRTY;No;0;AL;;;;30;N;;;;;
+1ED0D;OTTOMAN SIYAQ NUMBER FORTY;No;0;AL;;;;40;N;;;;;
+1ED0E;OTTOMAN SIYAQ NUMBER FIFTY;No;0;AL;;;;50;N;;;;;
+1ED0F;OTTOMAN SIYAQ NUMBER SIXTY;No;0;AL;;;;60;N;;;;;
+1ED10;OTTOMAN SIYAQ NUMBER SEVENTY;No;0;AL;;;;70;N;;;;;
+1ED11;OTTOMAN SIYAQ NUMBER EIGHTY;No;0;AL;;;;80;N;;;;;
+1ED12;OTTOMAN SIYAQ NUMBER NINETY;No;0;AL;;;;90;N;;;;;
+1ED13;OTTOMAN SIYAQ NUMBER ONE HUNDRED;No;0;AL;;;;100;N;;;;;
+1ED14;OTTOMAN SIYAQ NUMBER TWO HUNDRED;No;0;AL;;;;200;N;;;;;
+1ED15;OTTOMAN SIYAQ NUMBER THREE HUNDRED;No;0;AL;;;;300;N;;;;;
+1ED16;OTTOMAN SIYAQ NUMBER FOUR HUNDRED;No;0;AL;;;;400;N;;;;;
+1ED17;OTTOMAN SIYAQ NUMBER FIVE HUNDRED;No;0;AL;;;;500;N;;;;;
+1ED18;OTTOMAN SIYAQ NUMBER SIX HUNDRED;No;0;AL;;;;600;N;;;;;
+1ED19;OTTOMAN SIYAQ NUMBER SEVEN HUNDRED;No;0;AL;;;;700;N;;;;;
+1ED1A;OTTOMAN SIYAQ NUMBER EIGHT HUNDRED;No;0;AL;;;;800;N;;;;;
+1ED1B;OTTOMAN SIYAQ NUMBER NINE HUNDRED;No;0;AL;;;;900;N;;;;;
+1ED1C;OTTOMAN SIYAQ NUMBER ONE THOUSAND;No;0;AL;;;;1000;N;;;;;
+1ED1D;OTTOMAN SIYAQ NUMBER TWO THOUSAND;No;0;AL;;;;2000;N;;;;;
+1ED1E;OTTOMAN SIYAQ NUMBER THREE THOUSAND;No;0;AL;;;;3000;N;;;;;
+1ED1F;OTTOMAN SIYAQ NUMBER FOUR THOUSAND;No;0;AL;;;;4000;N;;;;;
+1ED20;OTTOMAN SIYAQ NUMBER FIVE THOUSAND;No;0;AL;;;;5000;N;;;;;
+1ED21;OTTOMAN SIYAQ NUMBER SIX THOUSAND;No;0;AL;;;;6000;N;;;;;
+1ED22;OTTOMAN SIYAQ NUMBER SEVEN THOUSAND;No;0;AL;;;;7000;N;;;;;
+1ED23;OTTOMAN SIYAQ NUMBER EIGHT THOUSAND;No;0;AL;;;;8000;N;;;;;
+1ED24;OTTOMAN SIYAQ NUMBER NINE THOUSAND;No;0;AL;;;;9000;N;;;;;
+1ED25;OTTOMAN SIYAQ NUMBER TEN THOUSAND;No;0;AL;;;;10000;N;;;;;
+1ED26;OTTOMAN SIYAQ NUMBER TWENTY THOUSAND;No;0;AL;;;;20000;N;;;;;
+1ED27;OTTOMAN SIYAQ NUMBER THIRTY THOUSAND;No;0;AL;;;;30000;N;;;;;
+1ED28;OTTOMAN SIYAQ NUMBER FORTY THOUSAND;No;0;AL;;;;40000;N;;;;;
+1ED29;OTTOMAN SIYAQ NUMBER FIFTY THOUSAND;No;0;AL;;;;50000;N;;;;;
+1ED2A;OTTOMAN SIYAQ NUMBER SIXTY THOUSAND;No;0;AL;;;;60000;N;;;;;
+1ED2B;OTTOMAN SIYAQ NUMBER SEVENTY THOUSAND;No;0;AL;;;;70000;N;;;;;
+1ED2C;OTTOMAN SIYAQ NUMBER EIGHTY THOUSAND;No;0;AL;;;;80000;N;;;;;
+1ED2D;OTTOMAN SIYAQ NUMBER NINETY THOUSAND;No;0;AL;;;;90000;N;;;;;
+1ED2E;OTTOMAN SIYAQ MARRATAN;So;0;AL;;;;;N;;;;;
+1ED2F;OTTOMAN SIYAQ ALTERNATE NUMBER TWO;No;0;AL;;;;2;N;;;;;
+1ED30;OTTOMAN SIYAQ ALTERNATE NUMBER THREE;No;0;AL;;;;3;N;;;;;
+1ED31;OTTOMAN SIYAQ ALTERNATE NUMBER FOUR;No;0;AL;;;;4;N;;;;;
+1ED32;OTTOMAN SIYAQ ALTERNATE NUMBER FIVE;No;0;AL;;;;5;N;;;;;
+1ED33;OTTOMAN SIYAQ ALTERNATE NUMBER SIX;No;0;AL;;;;6;N;;;;;
+1ED34;OTTOMAN SIYAQ ALTERNATE NUMBER SEVEN;No;0;AL;;;;7;N;;;;;
+1ED35;OTTOMAN SIYAQ ALTERNATE NUMBER EIGHT;No;0;AL;;;;8;N;;;;;
+1ED36;OTTOMAN SIYAQ ALTERNATE NUMBER NINE;No;0;AL;;;;9;N;;;;;
+1ED37;OTTOMAN SIYAQ ALTERNATE NUMBER TEN;No;0;AL;;;;10;N;;;;;
+1ED38;OTTOMAN SIYAQ ALTERNATE NUMBER FOUR HUNDRED;No;0;AL;;;;400;N;;;;;
+1ED39;OTTOMAN SIYAQ ALTERNATE NUMBER SIX HUNDRED;No;0;AL;;;;600;N;;;;;
+1ED3A;OTTOMAN SIYAQ ALTERNATE NUMBER TWO THOUSAND;No;0;AL;;;;2000;N;;;;;
+1ED3B;OTTOMAN SIYAQ ALTERNATE NUMBER TEN THOUSAND;No;0;AL;;;;10000;N;;;;;
+1ED3C;OTTOMAN SIYAQ FRACTION ONE HALF;No;0;AL;;;;1/2;N;;;;;
+1ED3D;OTTOMAN SIYAQ FRACTION ONE SIXTH;No;0;AL;;;;1/6;N;;;;;
1EE00;ARABIC MATHEMATICAL ALEF;Lo;0;AL;<font> 0627;;;;N;;;;;
1EE01;ARABIC MATHEMATICAL BEH;Lo;0;AL;<font> 0628;;;;N;;;;;
1EE02;ARABIC MATHEMATICAL JEEM;Lo;0;AL;<font> 062C;;;;N;;;;;
@@ -29662,6 +30065,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1F169;NEGATIVE CIRCLED LATIN CAPITAL LETTER Z;So;0;L;;;;;N;;;;;
1F16A;RAISED MC SIGN;So;0;ON;<super> 004D 0043;;;;N;;;;;
1F16B;RAISED MD SIGN;So;0;ON;<super> 004D 0044;;;;N;;;;;
+1F16C;RAISED MR SIGN;So;0;ON;<super> 004D 0052;;;;N;;;;;
1F170;NEGATIVE SQUARED LATIN CAPITAL LETTER A;So;0;L;;;;;N;;;;;
1F171;NEGATIVE SQUARED LATIN CAPITAL LETTER B;So;0;L;;;;;N;;;;;
1F172;NEGATIVE SQUARED LATIN CAPITAL LETTER C;So;0;L;;;;;N;;;;;
@@ -30794,6 +31198,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1F6D2;SHOPPING TROLLEY;So;0;ON;;;;;N;;;;;
1F6D3;STUPA;So;0;ON;;;;;N;;;;;
1F6D4;PAGODA;So;0;ON;;;;;N;;;;;
+1F6D5;HINDU TEMPLE;So;0;ON;;;;;N;;;;;
1F6E0;HAMMER AND WRENCH;So;0;ON;;;;;N;;;;;
1F6E1;SHIELD;So;0;ON;;;;;N;;;;;
1F6E2;OIL DRUM;So;0;ON;;;;;N;;;;;
@@ -30817,6 +31222,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1F6F7;SLED;So;0;ON;;;;;N;;;;;
1F6F8;FLYING SAUCER;So;0;ON;;;;;N;;;;;
1F6F9;SKATEBOARD;So;0;ON;;;;;N;;;;;
+1F6FA;AUTO RICKSHAW;So;0;ON;;;;;N;;;;;
1F700;ALCHEMICAL SYMBOL FOR QUINTESSENCE;So;0;ON;;;;;N;;;;;
1F701;ALCHEMICAL SYMBOL FOR AIR;So;0;ON;;;;;N;;;;;
1F702;ALCHEMICAL SYMBOL FOR FIRE;So;0;ON;;;;;N;;;;;
@@ -31022,6 +31428,18 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1F7D6;NEGATIVE CIRCLED TRIANGLE;So;0;ON;;;;;N;;;;;
1F7D7;CIRCLED SQUARE;So;0;ON;;;;;N;;;;;
1F7D8;NEGATIVE CIRCLED SQUARE;So;0;ON;;;;;N;;;;;
+1F7E0;LARGE ORANGE CIRCLE;So;0;ON;;;;;N;;;;;
+1F7E1;LARGE YELLOW CIRCLE;So;0;ON;;;;;N;;;;;
+1F7E2;LARGE GREEN CIRCLE;So;0;ON;;;;;N;;;;;
+1F7E3;LARGE PURPLE CIRCLE;So;0;ON;;;;;N;;;;;
+1F7E4;LARGE BROWN CIRCLE;So;0;ON;;;;;N;;;;;
+1F7E5;LARGE RED SQUARE;So;0;ON;;;;;N;;;;;
+1F7E6;LARGE BLUE SQUARE;So;0;ON;;;;;N;;;;;
+1F7E7;LARGE ORANGE SQUARE;So;0;ON;;;;;N;;;;;
+1F7E8;LARGE YELLOW SQUARE;So;0;ON;;;;;N;;;;;
+1F7E9;LARGE GREEN SQUARE;So;0;ON;;;;;N;;;;;
+1F7EA;LARGE PURPLE SQUARE;So;0;ON;;;;;N;;;;;
+1F7EB;LARGE BROWN SQUARE;So;0;ON;;;;;N;;;;;
1F800;LEFTWARDS ARROW WITH SMALL TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;;
1F801;UPWARDS ARROW WITH SMALL TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;;
1F802;RIGHTWARDS ARROW WITH SMALL TRIANGLE ARROWHEAD;So;0;ON;;;;;N;;;;;
@@ -31182,6 +31600,9 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1F909;DOWNWARD FACING NOTCHED HOOK;So;0;ON;;;;;N;;;;;
1F90A;DOWNWARD FACING HOOK WITH DOT;So;0;ON;;;;;N;;;;;
1F90B;DOWNWARD FACING NOTCHED HOOK WITH DOT;So;0;ON;;;;;N;;;;;
+1F90D;WHITE HEART;So;0;ON;;;;;N;;;;;
+1F90E;BROWN HEART;So;0;ON;;;;;N;;;;;
+1F90F;PINCHING HAND;So;0;ON;;;;;N;;;;;
1F910;ZIPPER-MOUTH FACE;So;0;ON;;;;;N;;;;;
1F911;MONEY-MOUTH FACE;So;0;ON;;;;;N;;;;;
1F912;FACE WITH THERMOMETER;So;0;ON;;;;;N;;;;;
@@ -31229,6 +31650,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1F93C;WRESTLERS;So;0;ON;;;;;N;;;;;
1F93D;WATER POLO;So;0;ON;;;;;N;;;;;
1F93E;HANDBALL;So;0;ON;;;;;N;;;;;
+1F93F;DIVING MASK;So;0;ON;;;;;N;;;;;
1F940;WILTED FLOWER;So;0;ON;;;;;N;;;;;
1F941;DRUM WITH DRUMSTICKS;So;0;ON;;;;;N;;;;;
1F942;CLINKING GLASSES;So;0;ON;;;;;N;;;;;
@@ -31278,11 +31700,13 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1F96E;MOON CAKE;So;0;ON;;;;;N;;;;;
1F96F;BAGEL;So;0;ON;;;;;N;;;;;
1F970;SMILING FACE WITH SMILING EYES AND THREE HEARTS;So;0;ON;;;;;N;;;;;
+1F971;YAWNING FACE;So;0;ON;;;;;N;;;;;
1F973;FACE WITH PARTY HORN AND PARTY HAT;So;0;ON;;;;;N;;;;;
1F974;FACE WITH UNEVEN EYES AND WAVY MOUTH;So;0;ON;;;;;N;;;;;
1F975;OVERHEATED FACE;So;0;ON;;;;;N;;;;;
1F976;FREEZING FACE;So;0;ON;;;;;N;;;;;
1F97A;FACE WITH PLEADING EYES;So;0;ON;;;;;N;;;;;
+1F97B;SARI;So;0;ON;;;;;N;;;;;
1F97C;LAB COAT;So;0;ON;;;;;N;;;;;
1F97D;GOGGLES;So;0;ON;;;;;N;;;;;
1F97E;HIKING BOOT;So;0;ON;;;;;N;;;;;
@@ -31322,6 +31746,14 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1F9A0;MICROBE;So;0;ON;;;;;N;;;;;
1F9A1;BADGER;So;0;ON;;;;;N;;;;;
1F9A2;SWAN;So;0;ON;;;;;N;;;;;
+1F9A5;SLOTH;So;0;ON;;;;;N;;;;;
+1F9A6;OTTER;So;0;ON;;;;;N;;;;;
+1F9A7;ORANGUTAN;So;0;ON;;;;;N;;;;;
+1F9A8;SKUNK;So;0;ON;;;;;N;;;;;
+1F9A9;FLAMINGO;So;0;ON;;;;;N;;;;;
+1F9AA;OYSTER;So;0;ON;;;;;N;;;;;
+1F9AE;GUIDE DOG;So;0;ON;;;;;N;;;;;
+1F9AF;PROBING CANE;So;0;ON;;;;;N;;;;;
1F9B0;EMOJI COMPONENT RED HAIR;So;0;ON;;;;;N;;;;;
1F9B1;EMOJI COMPONENT CURLY HAIR;So;0;ON;;;;;N;;;;;
1F9B2;EMOJI COMPONENT BALD;So;0;ON;;;;;N;;;;;
@@ -31332,9 +31764,26 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1F9B7;TOOTH;So;0;ON;;;;;N;;;;;
1F9B8;SUPERHERO;So;0;ON;;;;;N;;;;;
1F9B9;SUPERVILLAIN;So;0;ON;;;;;N;;;;;
+1F9BA;SAFETY VEST;So;0;ON;;;;;N;;;;;
+1F9BB;EAR WITH HEARING AID;So;0;ON;;;;;N;;;;;
+1F9BC;MOTORIZED WHEELCHAIR;So;0;ON;;;;;N;;;;;
+1F9BD;MANUAL WHEELCHAIR;So;0;ON;;;;;N;;;;;
+1F9BE;MECHANICAL ARM;So;0;ON;;;;;N;;;;;
+1F9BF;MECHANICAL LEG;So;0;ON;;;;;N;;;;;
1F9C0;CHEESE WEDGE;So;0;ON;;;;;N;;;;;
1F9C1;CUPCAKE;So;0;ON;;;;;N;;;;;
1F9C2;SALT SHAKER;So;0;ON;;;;;N;;;;;
+1F9C3;BEVERAGE BOX;So;0;ON;;;;;N;;;;;
+1F9C4;GARLIC;So;0;ON;;;;;N;;;;;
+1F9C5;ONION;So;0;ON;;;;;N;;;;;
+1F9C6;FALAFEL;So;0;ON;;;;;N;;;;;
+1F9C7;WAFFLE;So;0;ON;;;;;N;;;;;
+1F9C8;BUTTER;So;0;ON;;;;;N;;;;;
+1F9C9;MATE DRINK;So;0;ON;;;;;N;;;;;
+1F9CA;ICE CUBE;So;0;ON;;;;;N;;;;;
+1F9CD;STANDING PERSON;So;0;ON;;;;;N;;;;;
+1F9CE;KNEELING PERSON;So;0;ON;;;;;N;;;;;
+1F9CF;DEAF PERSON;So;0;ON;;;;;N;;;;;
1F9D0;FACE WITH MONOCLE;So;0;ON;;;;;N;;;;;
1F9D1;ADULT;So;0;ON;;;;;N;;;;;
1F9D2;CHILD;So;0;ON;;;;;N;;;;;
@@ -31383,6 +31832,90 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1F9FD;SPONGE;So;0;ON;;;;;N;;;;;
1F9FE;RECEIPT;So;0;ON;;;;;N;;;;;
1F9FF;NAZAR AMULET;So;0;ON;;;;;N;;;;;
+1FA00;NEUTRAL CHESS KING;So;0;ON;;;;;N;;;;;
+1FA01;NEUTRAL CHESS QUEEN;So;0;ON;;;;;N;;;;;
+1FA02;NEUTRAL CHESS ROOK;So;0;ON;;;;;N;;;;;
+1FA03;NEUTRAL CHESS BISHOP;So;0;ON;;;;;N;;;;;
+1FA04;NEUTRAL CHESS KNIGHT;So;0;ON;;;;;N;;;;;
+1FA05;NEUTRAL CHESS PAWN;So;0;ON;;;;;N;;;;;
+1FA06;WHITE CHESS KNIGHT ROTATED FORTY-FIVE DEGREES;So;0;ON;;;;;N;;;;;
+1FA07;BLACK CHESS KNIGHT ROTATED FORTY-FIVE DEGREES;So;0;ON;;;;;N;;;;;
+1FA08;NEUTRAL CHESS KNIGHT ROTATED FORTY-FIVE DEGREES;So;0;ON;;;;;N;;;;;
+1FA09;WHITE CHESS KING ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA0A;WHITE CHESS QUEEN ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA0B;WHITE CHESS ROOK ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA0C;WHITE CHESS BISHOP ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA0D;WHITE CHESS KNIGHT ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA0E;WHITE CHESS PAWN ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA0F;BLACK CHESS KING ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA10;BLACK CHESS QUEEN ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA11;BLACK CHESS ROOK ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA12;BLACK CHESS BISHOP ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA13;BLACK CHESS KNIGHT ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA14;BLACK CHESS PAWN ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA15;NEUTRAL CHESS KING ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA16;NEUTRAL CHESS QUEEN ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA17;NEUTRAL CHESS ROOK ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA18;NEUTRAL CHESS BISHOP ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA19;NEUTRAL CHESS KNIGHT ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA1A;NEUTRAL CHESS PAWN ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA1B;WHITE CHESS KNIGHT ROTATED ONE HUNDRED THIRTY-FIVE DEGREES;So;0;ON;;;;;N;;;;;
+1FA1C;BLACK CHESS KNIGHT ROTATED ONE HUNDRED THIRTY-FIVE DEGREES;So;0;ON;;;;;N;;;;;
+1FA1D;NEUTRAL CHESS KNIGHT ROTATED ONE HUNDRED THIRTY-FIVE DEGREES;So;0;ON;;;;;N;;;;;
+1FA1E;WHITE CHESS TURNED KING;So;0;ON;;;;;N;;;;;
+1FA1F;WHITE CHESS TURNED QUEEN;So;0;ON;;;;;N;;;;;
+1FA20;WHITE CHESS TURNED ROOK;So;0;ON;;;;;N;;;;;
+1FA21;WHITE CHESS TURNED BISHOP;So;0;ON;;;;;N;;;;;
+1FA22;WHITE CHESS TURNED KNIGHT;So;0;ON;;;;;N;;;;;
+1FA23;WHITE CHESS TURNED PAWN;So;0;ON;;;;;N;;;;;
+1FA24;BLACK CHESS TURNED KING;So;0;ON;;;;;N;;;;;
+1FA25;BLACK CHESS TURNED QUEEN;So;0;ON;;;;;N;;;;;
+1FA26;BLACK CHESS TURNED ROOK;So;0;ON;;;;;N;;;;;
+1FA27;BLACK CHESS TURNED BISHOP;So;0;ON;;;;;N;;;;;
+1FA28;BLACK CHESS TURNED KNIGHT;So;0;ON;;;;;N;;;;;
+1FA29;BLACK CHESS TURNED PAWN;So;0;ON;;;;;N;;;;;
+1FA2A;NEUTRAL CHESS TURNED KING;So;0;ON;;;;;N;;;;;
+1FA2B;NEUTRAL CHESS TURNED QUEEN;So;0;ON;;;;;N;;;;;
+1FA2C;NEUTRAL CHESS TURNED ROOK;So;0;ON;;;;;N;;;;;
+1FA2D;NEUTRAL CHESS TURNED BISHOP;So;0;ON;;;;;N;;;;;
+1FA2E;NEUTRAL CHESS TURNED KNIGHT;So;0;ON;;;;;N;;;;;
+1FA2F;NEUTRAL CHESS TURNED PAWN;So;0;ON;;;;;N;;;;;
+1FA30;WHITE CHESS KNIGHT ROTATED TWO HUNDRED TWENTY-FIVE DEGREES;So;0;ON;;;;;N;;;;;
+1FA31;BLACK CHESS KNIGHT ROTATED TWO HUNDRED TWENTY-FIVE DEGREES;So;0;ON;;;;;N;;;;;
+1FA32;NEUTRAL CHESS KNIGHT ROTATED TWO HUNDRED TWENTY-FIVE DEGREES;So;0;ON;;;;;N;;;;;
+1FA33;WHITE CHESS KING ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA34;WHITE CHESS QUEEN ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA35;WHITE CHESS ROOK ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA36;WHITE CHESS BISHOP ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA37;WHITE CHESS KNIGHT ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA38;WHITE CHESS PAWN ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA39;BLACK CHESS KING ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA3A;BLACK CHESS QUEEN ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA3B;BLACK CHESS ROOK ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA3C;BLACK CHESS BISHOP ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA3D;BLACK CHESS KNIGHT ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA3E;BLACK CHESS PAWN ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA3F;NEUTRAL CHESS KING ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA40;NEUTRAL CHESS QUEEN ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA41;NEUTRAL CHESS ROOK ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA42;NEUTRAL CHESS BISHOP ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA43;NEUTRAL CHESS KNIGHT ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA44;NEUTRAL CHESS PAWN ROTATED TWO HUNDRED SEVENTY DEGREES;So;0;ON;;;;;N;;;;;
+1FA45;WHITE CHESS KNIGHT ROTATED THREE HUNDRED FIFTEEN DEGREES;So;0;ON;;;;;N;;;;;
+1FA46;BLACK CHESS KNIGHT ROTATED THREE HUNDRED FIFTEEN DEGREES;So;0;ON;;;;;N;;;;;
+1FA47;NEUTRAL CHESS KNIGHT ROTATED THREE HUNDRED FIFTEEN DEGREES;So;0;ON;;;;;N;;;;;
+1FA48;WHITE CHESS EQUIHOPPER;So;0;ON;;;;;N;;;;;
+1FA49;BLACK CHESS EQUIHOPPER;So;0;ON;;;;;N;;;;;
+1FA4A;NEUTRAL CHESS EQUIHOPPER;So;0;ON;;;;;N;;;;;
+1FA4B;WHITE CHESS EQUIHOPPER ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA4C;BLACK CHESS EQUIHOPPER ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA4D;NEUTRAL CHESS EQUIHOPPER ROTATED NINETY DEGREES;So;0;ON;;;;;N;;;;;
+1FA4E;WHITE CHESS KNIGHT-QUEEN;So;0;ON;;;;;N;;;;;
+1FA4F;WHITE CHESS KNIGHT-ROOK;So;0;ON;;;;;N;;;;;
+1FA50;WHITE CHESS KNIGHT-BISHOP;So;0;ON;;;;;N;;;;;
+1FA51;BLACK CHESS KNIGHT-QUEEN;So;0;ON;;;;;N;;;;;
+1FA52;BLACK CHESS KNIGHT-ROOK;So;0;ON;;;;;N;;;;;
+1FA53;BLACK CHESS KNIGHT-BISHOP;So;0;ON;;;;;N;;;;;
1FA60;XIANGQI RED GENERAL;So;0;ON;;;;;N;;;;;
1FA61;XIANGQI RED MANDARIN;So;0;ON;;;;;N;;;;;
1FA62;XIANGQI RED ELEPHANT;So;0;ON;;;;;N;;;;;
@@ -31397,6 +31930,22 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
1FA6B;XIANGQI BLACK CHARIOT;So;0;ON;;;;;N;;;;;
1FA6C;XIANGQI BLACK CANNON;So;0;ON;;;;;N;;;;;
1FA6D;XIANGQI BLACK SOLDIER;So;0;ON;;;;;N;;;;;
+1FA70;BALLET SHOES;So;0;ON;;;;;N;;;;;
+1FA71;ONE-PIECE SWIMSUIT;So;0;ON;;;;;N;;;;;
+1FA72;BRIEFS;So;0;ON;;;;;N;;;;;
+1FA73;SHORTS;So;0;ON;;;;;N;;;;;
+1FA78;DROP OF BLOOD;So;0;ON;;;;;N;;;;;
+1FA79;ADHESIVE BANDAGE;So;0;ON;;;;;N;;;;;
+1FA7A;STETHOSCOPE;So;0;ON;;;;;N;;;;;
+1FA80;YO-YO;So;0;ON;;;;;N;;;;;
+1FA81;KITE;So;0;ON;;;;;N;;;;;
+1FA82;PARACHUTE;So;0;ON;;;;;N;;;;;
+1FA90;RINGED PLANET;So;0;ON;;;;;N;;;;;
+1FA91;CHAIR;So;0;ON;;;;;N;;;;;
+1FA92;RAZOR;So;0;ON;;;;;N;;;;;
+1FA93;AXE;So;0;ON;;;;;N;;;;;
+1FA94;DIYA LAMP;So;0;ON;;;;;N;;;;;
+1FA95;BANJO;So;0;ON;;;;;N;;;;;
20000;<CJK Ideograph Extension B, First>;Lo;0;L;;;;;N;;;;;
2A6D6;<CJK Ideograph Extension B, Last>;Lo;0;L;;;;;N;;;;;
2A700;<CJK Ideograph Extension C, First>;Lo;0;L;;;;;N;;;;;
diff --git a/lib/stdlib/uc_spec/emoji-data.txt b/lib/stdlib/uc_spec/emoji-data.txt
index 6e66455252..2fb5c3ff68 100644
--- a/lib/stdlib/uc_spec/emoji-data.txt
+++ b/lib/stdlib/uc_spec/emoji-data.txt
@@ -1,11 +1,11 @@
# emoji-data.txt
-# Date: 2018-02-07, 07:55:18 GMT
-# © 2018 Unicode®, Inc.
+# Date: 2019-01-15, 12:10:05 GMT
+# © 2019 Unicode®, Inc.
# Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
# For terms of use, see http://www.unicode.org/terms_of_use.html
#
# Emoji Data for UTS #51
-# Version: 11.0
+# Version: 12.0
#
# For documentation and usage, see http://www.unicode.org/reports/tr51
#
@@ -45,7 +45,7 @@
25FB..25FE ; Emoji # 3.2 [4] (◻️..◾) white medium square..black medium-small square
2600..2604 ; Emoji # 1.1 [5] (☀️..☄️) sun..comet
260E ; Emoji # 1.1 [1] (☎️) telephone
-2611 ; Emoji # 1.1 [1] (☑️) ballot box with check
+2611 ; Emoji # 1.1 [1] (☑️) check box with check
2614..2615 ; Emoji # 4.0 [2] (☔..☕) umbrella with rain drops..hot beverage
2618 ; Emoji # 4.1 [1] (☘️) shamrock
261D ; Emoji # 1.1 [1] (☝️) index pointing up
@@ -82,14 +82,14 @@
26F7..26FA ; Emoji # 5.2 [4] (⛷️..⛺) skier..tent
26FD ; Emoji # 5.2 [1] (⛽) fuel pump
2702 ; Emoji # 1.1 [1] (✂️) scissors
-2705 ; Emoji # 6.0 [1] (✅) white heavy check mark
+2705 ; Emoji # 6.0 [1] (✅) check mark button
2708..2709 ; Emoji # 1.1 [2] (✈️..✉️) airplane..envelope
270A..270B ; Emoji # 6.0 [2] (✊..✋) raised fist..raised hand
270C..270D ; Emoji # 1.1 [2] (✌️..✍️) victory hand..writing hand
270F ; Emoji # 1.1 [1] (✏️) pencil
2712 ; Emoji # 1.1 [1] (✒️) black nib
-2714 ; Emoji # 1.1 [1] (✔️) heavy check mark
-2716 ; Emoji # 1.1 [1] (✖️) heavy multiplication x
+2714 ; Emoji # 1.1 [1] (✔️) check mark
+2716 ; Emoji # 1.1 [1] (✖️) multiplication sign
271D ; Emoji # 1.1 [1] (✝️) latin cross
2721 ; Emoji # 1.1 [1] (✡️) star of David
2728 ; Emoji # 6.0 [1] (✨) sparkles
@@ -100,8 +100,8 @@
274E ; Emoji # 6.0 [1] (❎) cross mark button
2753..2755 ; Emoji # 6.0 [3] (❓..❕) question mark..white exclamation mark
2757 ; Emoji # 5.2 [1] (❗) exclamation mark
-2763..2764 ; Emoji # 1.1 [2] (❣️..❤️) heavy heart exclamation..red heart
-2795..2797 ; Emoji # 6.0 [3] (➕..➗) heavy plus sign..heavy division sign
+2763..2764 ; Emoji # 1.1 [2] (❣️..❤️) heart exclamation..red heart
+2795..2797 ; Emoji # 6.0 [3] (➕..➗) plus sign..division sign
27A1 ; Emoji # 1.1 [1] (➡️) right arrow
27B0 ; Emoji # 6.0 [1] (➰) curly loop
27BF ; Emoji # 6.0 [1] (➿) double curly loop
@@ -109,7 +109,7 @@
2B05..2B07 ; Emoji # 4.0 [3] (⬅️..⬇️) left arrow..down arrow
2B1B..2B1C ; Emoji # 5.1 [2] (⬛..⬜) black large square..white large square
2B50 ; Emoji # 5.1 [1] (⭐) star
-2B55 ; Emoji # 5.2 [1] (⭕) heavy large circle
+2B55 ; Emoji # 5.2 [1] (⭕) hollow red circle
3030 ; Emoji # 1.1 [1] (〰️) wavy dash
303D ; Emoji # 3.2 [1] (〽️) part alternation mark
3297 ; Emoji # 1.1 [1] (㊗️) Japanese “congratulations” button
@@ -206,7 +206,7 @@
1F62E..1F62F ; Emoji # 6.1 [2] (😮..😯) face with open mouth..hushed face
1F630..1F633 ; Emoji # 6.0 [4] (😰..😳) anxious face with sweat..flushed face
1F634 ; Emoji # 6.1 [1] (😴) sleeping face
-1F635..1F640 ; Emoji # 6.0 [12] (😵..🙀) dizzy face..weary cat face
+1F635..1F640 ; Emoji # 6.0 [12] (😵..🙀) dizzy face..weary cat
1F641..1F642 ; Emoji # 7.0 [2] (🙁..🙂) slightly frowning face..slightly smiling face
1F643..1F644 ; Emoji # 8.0 [2] (🙃..🙄) upside-down face..face with rolling eyes
1F645..1F64F ; Emoji # 6.0 [11] (🙅..🙏) person gesturing NO..folded hands
@@ -214,6 +214,7 @@
1F6CB..1F6CF ; Emoji # 7.0 [5] (🛋️..🛏️) couch and lamp..bed
1F6D0 ; Emoji # 8.0 [1] (🛐) place of worship
1F6D1..1F6D2 ; Emoji # 9.0 [2] (🛑..🛒) stop sign..shopping cart
+1F6D5 ; Emoji # 12.0 [1] (🛕) hindu temple
1F6E0..1F6E5 ; Emoji # 7.0 [6] (🛠️..🛥️) hammer and wrench..motor boat
1F6E9 ; Emoji # 7.0 [1] (🛩️) small airplane
1F6EB..1F6EC ; Emoji # 7.0 [2] (🛫..🛬) airplane departure..airplane arrival
@@ -222,6 +223,9 @@
1F6F4..1F6F6 ; Emoji # 9.0 [3] (🛴..🛶) kick scooter..canoe
1F6F7..1F6F8 ; Emoji # 10.0 [2] (🛷..🛸) sled..flying saucer
1F6F9 ; Emoji # 11.0 [1] (🛹) skateboard
+1F6FA ; Emoji # 12.0 [1] (🛺) auto rickshaw
+1F7E0..1F7EB ; Emoji # 12.0 [12] (🟠..🟫) orange circle..brown square
+1F90D..1F90F ; Emoji # 12.0 [3] (🤍..🤏) white heart..pinching hand
1F910..1F918 ; Emoji # 8.0 [9] (🤐..🤘) zipper-mouth face..sign of the horns
1F919..1F91E ; Emoji # 9.0 [6] (🤙..🤞) call me hand..crossed fingers
1F91F ; Emoji # 10.0 [1] (🤟) love-you gesture
@@ -231,27 +235,39 @@
1F931..1F932 ; Emoji # 10.0 [2] (🤱..🤲) breast-feeding..palms up together
1F933..1F93A ; Emoji # 9.0 [8] (🤳..🤺) selfie..person fencing
1F93C..1F93E ; Emoji # 9.0 [3] (🤼..🤾) people wrestling..person playing handball
+1F93F ; Emoji # 12.0 [1] (🤿) diving mask
1F940..1F945 ; Emoji # 9.0 [6] (🥀..🥅) wilted flower..goal net
1F947..1F94B ; Emoji # 9.0 [5] (🥇..🥋) 1st place medal..martial arts uniform
1F94C ; Emoji # 10.0 [1] (🥌) curling stone
1F94D..1F94F ; Emoji # 11.0 [3] (🥍..🥏) lacrosse..flying disc
1F950..1F95E ; Emoji # 9.0 [15] (🥐..🥞) croissant..pancakes
1F95F..1F96B ; Emoji # 10.0 [13] (🥟..🥫) dumpling..canned food
-1F96C..1F970 ; Emoji # 11.0 [5] (🥬..🥰) leafy green..smiling face with 3 hearts
+1F96C..1F970 ; Emoji # 11.0 [5] (🥬..🥰) leafy green..smiling face with hearts
+1F971 ; Emoji # 12.0 [1] (🥱) yawning face
1F973..1F976 ; Emoji # 11.0 [4] (🥳..🥶) partying face..cold face
1F97A ; Emoji # 11.0 [1] (🥺) pleading face
-1F97C..1F97F ; Emoji # 11.0 [4] (🥼..🥿) lab coat..woman’s flat shoe
-1F980..1F984 ; Emoji # 8.0 [5] (🦀..🦄) crab..unicorn face
+1F97B ; Emoji # 12.0 [1] (🥻) sari
+1F97C..1F97F ; Emoji # 11.0 [4] (🥼..🥿) lab coat..flat shoe
+1F980..1F984 ; Emoji # 8.0 [5] (🦀..🦄) crab..unicorn
1F985..1F991 ; Emoji # 9.0 [13] (🦅..🦑) eagle..squid
1F992..1F997 ; Emoji # 10.0 [6] (🦒..🦗) giraffe..cricket
1F998..1F9A2 ; Emoji # 11.0 [11] (🦘..🦢) kangaroo..swan
-1F9B0..1F9B9 ; Emoji # 11.0 [10] (🦰..🦹) red-haired..supervillain
+1F9A5..1F9AA ; Emoji # 12.0 [6] (🦥..🦪) sloth..oyster
+1F9AE..1F9AF ; Emoji # 12.0 [2] (🦮..🦯) guide dog..probing cane
+1F9B0..1F9B9 ; Emoji # 11.0 [10] (🦰..🦹) red hair..supervillain
+1F9BA..1F9BF ; Emoji # 12.0 [6] (🦺..🦿) safety vest..mechanical leg
1F9C0 ; Emoji # 8.0 [1] (🧀) cheese wedge
1F9C1..1F9C2 ; Emoji # 11.0 [2] (🧁..🧂) cupcake..salt
+1F9C3..1F9CA ; Emoji # 12.0 [8] (🧃..🧊) beverage box..ice cube
+1F9CD..1F9CF ; Emoji # 12.0 [3] (🧍..🧏) person standing..deaf person
1F9D0..1F9E6 ; Emoji # 10.0 [23] (🧐..🧦) face with monocle..socks
1F9E7..1F9FF ; Emoji # 11.0 [25] (🧧..🧿) red envelope..nazar amulet
+1FA70..1FA73 ; Emoji # 12.0 [4] (🩰..🩳) ballet shoes..shorts
+1FA78..1FA7A ; Emoji # 12.0 [3] (🩸..🩺) drop of blood..stethoscope
+1FA80..1FA82 ; Emoji # 12.0 [3] (🪀..🪂) yo-yo..parachute
+1FA90..1FA95 ; Emoji # 12.0 [6] (🪐..🪕) ringed planet..banjo
-# Total elements: 1250
+# Total elements: 1311
# ================================================
@@ -278,19 +294,19 @@
26F5 ; Emoji_Presentation # 5.2 [1] (⛵) sailboat
26FA ; Emoji_Presentation # 5.2 [1] (⛺) tent
26FD ; Emoji_Presentation # 5.2 [1] (⛽) fuel pump
-2705 ; Emoji_Presentation # 6.0 [1] (✅) white heavy check mark
+2705 ; Emoji_Presentation # 6.0 [1] (✅) check mark button
270A..270B ; Emoji_Presentation # 6.0 [2] (✊..✋) raised fist..raised hand
2728 ; Emoji_Presentation # 6.0 [1] (✨) sparkles
274C ; Emoji_Presentation # 6.0 [1] (❌) cross mark
274E ; Emoji_Presentation # 6.0 [1] (❎) cross mark button
2753..2755 ; Emoji_Presentation # 6.0 [3] (❓..❕) question mark..white exclamation mark
2757 ; Emoji_Presentation # 5.2 [1] (❗) exclamation mark
-2795..2797 ; Emoji_Presentation # 6.0 [3] (➕..➗) heavy plus sign..heavy division sign
+2795..2797 ; Emoji_Presentation # 6.0 [3] (➕..➗) plus sign..division sign
27B0 ; Emoji_Presentation # 6.0 [1] (➰) curly loop
27BF ; Emoji_Presentation # 6.0 [1] (➿) double curly loop
2B1B..2B1C ; Emoji_Presentation # 5.1 [2] (⬛..⬜) black large square..white large square
2B50 ; Emoji_Presentation # 5.1 [1] (⭐) star
-2B55 ; Emoji_Presentation # 5.2 [1] (⭕) heavy large circle
+2B55 ; Emoji_Presentation # 5.2 [1] (⭕) hollow red circle
1F004 ; Emoji_Presentation # 5.1 [1] (🀄) mahjong red dragon
1F0CF ; Emoji_Presentation # 6.0 [1] (🃏) joker
1F18E ; Emoji_Presentation # 6.0 [1] (🆎) AB button (blood type)
@@ -349,7 +365,7 @@
1F62E..1F62F ; Emoji_Presentation # 6.1 [2] (😮..😯) face with open mouth..hushed face
1F630..1F633 ; Emoji_Presentation # 6.0 [4] (😰..😳) anxious face with sweat..flushed face
1F634 ; Emoji_Presentation # 6.1 [1] (😴) sleeping face
-1F635..1F640 ; Emoji_Presentation # 6.0 [12] (😵..🙀) dizzy face..weary cat face
+1F635..1F640 ; Emoji_Presentation # 6.0 [12] (😵..🙀) dizzy face..weary cat
1F641..1F642 ; Emoji_Presentation # 7.0 [2] (🙁..🙂) slightly frowning face..slightly smiling face
1F643..1F644 ; Emoji_Presentation # 8.0 [2] (🙃..🙄) upside-down face..face with rolling eyes
1F645..1F64F ; Emoji_Presentation # 6.0 [11] (🙅..🙏) person gesturing NO..folded hands
@@ -357,10 +373,14 @@
1F6CC ; Emoji_Presentation # 7.0 [1] (🛌) person in bed
1F6D0 ; Emoji_Presentation # 8.0 [1] (🛐) place of worship
1F6D1..1F6D2 ; Emoji_Presentation # 9.0 [2] (🛑..🛒) stop sign..shopping cart
+1F6D5 ; Emoji_Presentation # 12.0 [1] (🛕) hindu temple
1F6EB..1F6EC ; Emoji_Presentation # 7.0 [2] (🛫..🛬) airplane departure..airplane arrival
1F6F4..1F6F6 ; Emoji_Presentation # 9.0 [3] (🛴..🛶) kick scooter..canoe
1F6F7..1F6F8 ; Emoji_Presentation # 10.0 [2] (🛷..🛸) sled..flying saucer
1F6F9 ; Emoji_Presentation # 11.0 [1] (🛹) skateboard
+1F6FA ; Emoji_Presentation # 12.0 [1] (🛺) auto rickshaw
+1F7E0..1F7EB ; Emoji_Presentation # 12.0 [12] (🟠..🟫) orange circle..brown square
+1F90D..1F90F ; Emoji_Presentation # 12.0 [3] (🤍..🤏) white heart..pinching hand
1F910..1F918 ; Emoji_Presentation # 8.0 [9] (🤐..🤘) zipper-mouth face..sign of the horns
1F919..1F91E ; Emoji_Presentation # 9.0 [6] (🤙..🤞) call me hand..crossed fingers
1F91F ; Emoji_Presentation # 10.0 [1] (🤟) love-you gesture
@@ -370,27 +390,39 @@
1F931..1F932 ; Emoji_Presentation # 10.0 [2] (🤱..🤲) breast-feeding..palms up together
1F933..1F93A ; Emoji_Presentation # 9.0 [8] (🤳..🤺) selfie..person fencing
1F93C..1F93E ; Emoji_Presentation # 9.0 [3] (🤼..🤾) people wrestling..person playing handball
+1F93F ; Emoji_Presentation # 12.0 [1] (🤿) diving mask
1F940..1F945 ; Emoji_Presentation # 9.0 [6] (🥀..🥅) wilted flower..goal net
1F947..1F94B ; Emoji_Presentation # 9.0 [5] (🥇..🥋) 1st place medal..martial arts uniform
1F94C ; Emoji_Presentation # 10.0 [1] (🥌) curling stone
1F94D..1F94F ; Emoji_Presentation # 11.0 [3] (🥍..🥏) lacrosse..flying disc
1F950..1F95E ; Emoji_Presentation # 9.0 [15] (🥐..🥞) croissant..pancakes
1F95F..1F96B ; Emoji_Presentation # 10.0 [13] (🥟..🥫) dumpling..canned food
-1F96C..1F970 ; Emoji_Presentation # 11.0 [5] (🥬..🥰) leafy green..smiling face with 3 hearts
+1F96C..1F970 ; Emoji_Presentation # 11.0 [5] (🥬..🥰) leafy green..smiling face with hearts
+1F971 ; Emoji_Presentation # 12.0 [1] (🥱) yawning face
1F973..1F976 ; Emoji_Presentation # 11.0 [4] (🥳..🥶) partying face..cold face
1F97A ; Emoji_Presentation # 11.0 [1] (🥺) pleading face
-1F97C..1F97F ; Emoji_Presentation # 11.0 [4] (🥼..🥿) lab coat..woman’s flat shoe
-1F980..1F984 ; Emoji_Presentation # 8.0 [5] (🦀..🦄) crab..unicorn face
+1F97B ; Emoji_Presentation # 12.0 [1] (🥻) sari
+1F97C..1F97F ; Emoji_Presentation # 11.0 [4] (🥼..🥿) lab coat..flat shoe
+1F980..1F984 ; Emoji_Presentation # 8.0 [5] (🦀..🦄) crab..unicorn
1F985..1F991 ; Emoji_Presentation # 9.0 [13] (🦅..🦑) eagle..squid
1F992..1F997 ; Emoji_Presentation # 10.0 [6] (🦒..🦗) giraffe..cricket
1F998..1F9A2 ; Emoji_Presentation # 11.0 [11] (🦘..🦢) kangaroo..swan
-1F9B0..1F9B9 ; Emoji_Presentation # 11.0 [10] (🦰..🦹) red-haired..supervillain
+1F9A5..1F9AA ; Emoji_Presentation # 12.0 [6] (🦥..🦪) sloth..oyster
+1F9AE..1F9AF ; Emoji_Presentation # 12.0 [2] (🦮..🦯) guide dog..probing cane
+1F9B0..1F9B9 ; Emoji_Presentation # 11.0 [10] (🦰..🦹) red hair..supervillain
+1F9BA..1F9BF ; Emoji_Presentation # 12.0 [6] (🦺..🦿) safety vest..mechanical leg
1F9C0 ; Emoji_Presentation # 8.0 [1] (🧀) cheese wedge
1F9C1..1F9C2 ; Emoji_Presentation # 11.0 [2] (🧁..🧂) cupcake..salt
+1F9C3..1F9CA ; Emoji_Presentation # 12.0 [8] (🧃..🧊) beverage box..ice cube
+1F9CD..1F9CF ; Emoji_Presentation # 12.0 [3] (🧍..🧏) person standing..deaf person
1F9D0..1F9E6 ; Emoji_Presentation # 10.0 [23] (🧐..🧦) face with monocle..socks
1F9E7..1F9FF ; Emoji_Presentation # 11.0 [25] (🧧..🧿) red envelope..nazar amulet
+1FA70..1FA73 ; Emoji_Presentation # 12.0 [4] (🩰..🩳) ballet shoes..shorts
+1FA78..1FA7A ; Emoji_Presentation # 12.0 [3] (🩸..🩺) drop of blood..stethoscope
+1FA80..1FA82 ; Emoji_Presentation # 12.0 [3] (🪀..🪂) yo-yo..parachute
+1FA90..1FA95 ; Emoji_Presentation # 12.0 [6] (🪐..🪕) ringed planet..banjo
-# Total elements: 1032
+# Total elements: 1093
# ================================================
@@ -417,12 +449,12 @@
1F3CB..1F3CC ; Emoji_Modifier_Base # 7.0 [2] (🏋️..🏌️) person lifting weights..person golfing
1F442..1F443 ; Emoji_Modifier_Base # 6.0 [2] (👂..👃) ear..nose
1F446..1F450 ; Emoji_Modifier_Base # 6.0 [11] (👆..👐) backhand index pointing up..open hands
-1F466..1F469 ; Emoji_Modifier_Base # 6.0 [4] (👦..👩) boy..woman
-1F46E ; Emoji_Modifier_Base # 6.0 [1] (👮) police officer
-1F470..1F478 ; Emoji_Modifier_Base # 6.0 [9] (👰..👸) bride with veil..princess
+1F466..1F478 ; Emoji_Modifier_Base # 6.0 [19] (👦..👸) boy..princess
1F47C ; Emoji_Modifier_Base # 6.0 [1] (👼) baby angel
1F481..1F483 ; Emoji_Modifier_Base # 6.0 [3] (💁..💃) person tipping hand..woman dancing
1F485..1F487 ; Emoji_Modifier_Base # 6.0 [3] (💅..💇) nail polish..person getting haircut
+1F48F ; Emoji_Modifier_Base # 6.0 [1] (💏) kiss
+1F491 ; Emoji_Modifier_Base # 6.0 [1] (💑) couple with heart
1F4AA ; Emoji_Modifier_Base # 6.0 [1] (💪) flexed biceps
1F574..1F575 ; Emoji_Modifier_Base # 7.0 [2] (🕴️..🕵️) man in suit levitating..detective
1F57A ; Emoji_Modifier_Base # 9.0 [1] (🕺) man dancing
@@ -434,20 +466,22 @@
1F6B4..1F6B6 ; Emoji_Modifier_Base # 6.0 [3] (🚴..🚶) person biking..person walking
1F6C0 ; Emoji_Modifier_Base # 6.0 [1] (🛀) person taking bath
1F6CC ; Emoji_Modifier_Base # 7.0 [1] (🛌) person in bed
+1F90F ; Emoji_Modifier_Base # 12.0 [1] (🤏) pinching hand
1F918 ; Emoji_Modifier_Base # 8.0 [1] (🤘) sign of the horns
-1F919..1F91C ; Emoji_Modifier_Base # 9.0 [4] (🤙..🤜) call me hand..right-facing fist
-1F91E ; Emoji_Modifier_Base # 9.0 [1] (🤞) crossed fingers
+1F919..1F91E ; Emoji_Modifier_Base # 9.0 [6] (🤙..🤞) call me hand..crossed fingers
1F91F ; Emoji_Modifier_Base # 10.0 [1] (🤟) love-you gesture
1F926 ; Emoji_Modifier_Base # 9.0 [1] (🤦) person facepalming
1F930 ; Emoji_Modifier_Base # 9.0 [1] (🤰) pregnant woman
1F931..1F932 ; Emoji_Modifier_Base # 10.0 [2] (🤱..🤲) breast-feeding..palms up together
1F933..1F939 ; Emoji_Modifier_Base # 9.0 [7] (🤳..🤹) selfie..person juggling
-1F93D..1F93E ; Emoji_Modifier_Base # 9.0 [2] (🤽..🤾) person playing water polo..person playing handball
+1F93C..1F93E ; Emoji_Modifier_Base # 9.0 [3] (🤼..🤾) people wrestling..person playing handball
1F9B5..1F9B6 ; Emoji_Modifier_Base # 11.0 [2] (🦵..🦶) leg..foot
1F9B8..1F9B9 ; Emoji_Modifier_Base # 11.0 [2] (🦸..🦹) superhero..supervillain
-1F9D1..1F9DD ; Emoji_Modifier_Base # 10.0 [13] (🧑..🧝) adult..elf
+1F9BB ; Emoji_Modifier_Base # 12.0 [1] (🦻) ear with hearing aid
+1F9CD..1F9CF ; Emoji_Modifier_Base # 12.0 [3] (🧍..🧏) person standing..deaf person
+1F9D1..1F9DD ; Emoji_Modifier_Base # 10.0 [13] (🧑..🧝) person..elf
-# Total elements: 106
+# Total elements: 120
# ================================================
@@ -462,7 +496,7 @@
FE0F ; Emoji_Component # 3.2 [1] () VARIATION SELECTOR-16
1F1E6..1F1FF ; Emoji_Component # 6.0 [26] (🇦..🇿) regional indicator symbol letter a..regional indicator symbol letter z
1F3FB..1F3FF ; Emoji_Component # 8.0 [5] (🏻..🏿) light skin tone..dark skin tone
-1F9B0..1F9B3 ; Emoji_Component # 11.0 [4] (🦰..🦳) red-haired..white-haired
+1F9B0..1F9B3 ; Emoji_Component # 11.0 [4] (🦰..🦳) red hair..white hair
E0020..E007F ; Emoji_Component # 3.1 [96] (󠀠..󠁿) tag space..cancel tag
# Total elements: 146
@@ -482,7 +516,7 @@ E0020..E007F ; Emoji_Component # 3.1 [96] (󠀠..󠁿) tag space..ca
21A9..21AA ; Extended_Pictographic# 1.1 [2] (↩️..↪️) right arrow curving left..left arrow curving right
231A..231B ; Extended_Pictographic# 1.1 [2] (⌚..⌛) watch..hourglass done
2328 ; Extended_Pictographic# 1.1 [1] (⌨️) keyboard
-2388 ; Extended_Pictographic# 3.0 [1] (⎈️) HELM SYMBOL
+2388 ; Extended_Pictographic# 3.0 [1] (⎈) HELM SYMBOL
23CF ; Extended_Pictographic# 4.0 [1] (⏏️) eject button
23E9..23F3 ; Extended_Pictographic# 6.0 [11] (⏩..⏳) fast-forward button..hourglass not done
23F8..23FA ; Extended_Pictographic# 7.0 [3] (⏸️..⏺️) pause button..record button
@@ -491,42 +525,42 @@ E0020..E007F ; Emoji_Component # 3.1 [96] (󠀠..󠁿) tag space..ca
25B6 ; Extended_Pictographic# 1.1 [1] (▶️) play button
25C0 ; Extended_Pictographic# 1.1 [1] (◀️) reverse button
25FB..25FE ; Extended_Pictographic# 3.2 [4] (◻️..◾) white medium square..black medium-small square
-2600..2605 ; Extended_Pictographic# 1.1 [6] (☀️..★️) sun..BLACK STAR
-2607..2612 ; Extended_Pictographic# 1.1 [12] (☇️..☒️) LIGHTNING..BALLOT BOX WITH X
+2600..2605 ; Extended_Pictographic# 1.1 [6] (☀️..★) sun..BLACK STAR
+2607..2612 ; Extended_Pictographic# 1.1 [12] (☇..☒) LIGHTNING..BALLOT BOX WITH X
2614..2615 ; Extended_Pictographic# 4.0 [2] (☔..☕) umbrella with rain drops..hot beverage
-2616..2617 ; Extended_Pictographic# 3.2 [2] (☖️..☗️) WHITE SHOGI PIECE..BLACK SHOGI PIECE
+2616..2617 ; Extended_Pictographic# 3.2 [2] (☖..☗) WHITE SHOGI PIECE..BLACK SHOGI PIECE
2618 ; Extended_Pictographic# 4.1 [1] (☘️) shamrock
-2619 ; Extended_Pictographic# 3.0 [1] (☙️) REVERSED ROTATED FLORAL HEART BULLET
-261A..266F ; Extended_Pictographic# 1.1 [86] (☚️..♯️) BLACK LEFT POINTING INDEX..MUSIC SHARP SIGN
-2670..2671 ; Extended_Pictographic# 3.0 [2] (♰️..♱️) WEST SYRIAC CROSS..EAST SYRIAC CROSS
-2672..267D ; Extended_Pictographic# 3.2 [12] (♲️..♽️) UNIVERSAL RECYCLING SYMBOL..PARTIALLY-RECYCLED PAPER SYMBOL
+2619 ; Extended_Pictographic# 3.0 [1] (☙) REVERSED ROTATED FLORAL HEART BULLET
+261A..266F ; Extended_Pictographic# 1.1 [86] (☚..♯) BLACK LEFT POINTING INDEX..MUSIC SHARP SIGN
+2670..2671 ; Extended_Pictographic# 3.0 [2] (♰..♱) WEST SYRIAC CROSS..EAST SYRIAC CROSS
+2672..267D ; Extended_Pictographic# 3.2 [12] (♲..♽) UNIVERSAL RECYCLING SYMBOL..PARTIALLY-RECYCLED PAPER SYMBOL
267E..267F ; Extended_Pictographic# 4.1 [2] (♾️..♿) infinity..wheelchair symbol
-2680..2685 ; Extended_Pictographic# 3.2 [6] (⚀️..⚅️) DIE FACE-1..DIE FACE-6
-2690..2691 ; Extended_Pictographic# 4.0 [2] (⚐️..⚑️) WHITE FLAG..BLACK FLAG
+2680..2685 ; Extended_Pictographic# 3.2 [6] (⚀..⚅) DIE FACE-1..DIE FACE-6
+2690..2691 ; Extended_Pictographic# 4.0 [2] (⚐..⚑) WHITE FLAG..BLACK FLAG
2692..269C ; Extended_Pictographic# 4.1 [11] (⚒️..⚜️) hammer and pick..fleur-de-lis
-269D ; Extended_Pictographic# 5.1 [1] (⚝️) OUTLINED WHITE STAR
-269E..269F ; Extended_Pictographic# 5.2 [2] (⚞️..⚟️) THREE LINES CONVERGING RIGHT..THREE LINES CONVERGING LEFT
+269D ; Extended_Pictographic# 5.1 [1] (⚝) OUTLINED WHITE STAR
+269E..269F ; Extended_Pictographic# 5.2 [2] (⚞..⚟) THREE LINES CONVERGING RIGHT..THREE LINES CONVERGING LEFT
26A0..26A1 ; Extended_Pictographic# 4.0 [2] (⚠️..⚡) warning..high voltage
-26A2..26B1 ; Extended_Pictographic# 4.1 [16] (⚢️..⚱️) DOUBLED FEMALE SIGN..funeral urn
-26B2 ; Extended_Pictographic# 5.0 [1] (⚲️) NEUTER
-26B3..26BC ; Extended_Pictographic# 5.1 [10] (⚳️..⚼️) CERES..SESQUIQUADRATE
-26BD..26BF ; Extended_Pictographic# 5.2 [3] (⚽..⚿️) soccer ball..SQUARED KEY
-26C0..26C3 ; Extended_Pictographic# 5.1 [4] (⛀️..⛃️) WHITE DRAUGHTS MAN..BLACK DRAUGHTS KING
-26C4..26CD ; Extended_Pictographic# 5.2 [10] (⛄..⛍️) snowman without snow..DISABLED CAR
+26A2..26B1 ; Extended_Pictographic# 4.1 [16] (⚢..⚱️) DOUBLED FEMALE SIGN..funeral urn
+26B2 ; Extended_Pictographic# 5.0 [1] (⚲) NEUTER
+26B3..26BC ; Extended_Pictographic# 5.1 [10] (⚳..⚼) CERES..SESQUIQUADRATE
+26BD..26BF ; Extended_Pictographic# 5.2 [3] (⚽..⚿) soccer ball..SQUARED KEY
+26C0..26C3 ; Extended_Pictographic# 5.1 [4] (⛀..⛃) WHITE DRAUGHTS MAN..BLACK DRAUGHTS KING
+26C4..26CD ; Extended_Pictographic# 5.2 [10] (⛄..⛍) snowman without snow..DISABLED CAR
26CE ; Extended_Pictographic# 6.0 [1] (⛎) Ophiuchus
-26CF..26E1 ; Extended_Pictographic# 5.2 [19] (⛏️..⛡️) pick..RESTRICTED LEFT ENTRY-2
-26E2 ; Extended_Pictographic# 6.0 [1] (⛢️) ASTRONOMICAL SYMBOL FOR URANUS
-26E3 ; Extended_Pictographic# 5.2 [1] (⛣️) HEAVY CIRCLE WITH STROKE AND TWO DOTS ABOVE
-26E4..26E7 ; Extended_Pictographic# 6.0 [4] (⛤️..⛧️) PENTAGRAM..INVERTED PENTAGRAM
-26E8..26FF ; Extended_Pictographic# 5.2 [24] (⛨️..⛿️) BLACK CROSS ON SHIELD..WHITE FLAG WITH HORIZONTAL MIDDLE BLACK STRIPE
-2700 ; Extended_Pictographic# 7.0 [1] (✀️) BLACK SAFETY SCISSORS
-2701..2704 ; Extended_Pictographic# 1.1 [4] (✁️..✄️) UPPER BLADE SCISSORS..WHITE SCISSORS
-2705 ; Extended_Pictographic# 6.0 [1] (✅) white heavy check mark
+26CF..26E1 ; Extended_Pictographic# 5.2 [19] (⛏️..⛡) pick..RESTRICTED LEFT ENTRY-2
+26E2 ; Extended_Pictographic# 6.0 [1] (⛢) ASTRONOMICAL SYMBOL FOR URANUS
+26E3 ; Extended_Pictographic# 5.2 [1] (⛣) HEAVY CIRCLE WITH STROKE AND TWO DOTS ABOVE
+26E4..26E7 ; Extended_Pictographic# 6.0 [4] (⛤..⛧) PENTAGRAM..INVERTED PENTAGRAM
+26E8..26FF ; Extended_Pictographic# 5.2 [24] (⛨..⛿) BLACK CROSS ON SHIELD..WHITE FLAG WITH HORIZONTAL MIDDLE BLACK STRIPE
+2700 ; Extended_Pictographic# 7.0 [1] (✀) BLACK SAFETY SCISSORS
+2701..2704 ; Extended_Pictographic# 1.1 [4] (✁..✄) UPPER BLADE SCISSORS..WHITE SCISSORS
+2705 ; Extended_Pictographic# 6.0 [1] (✅) check mark button
2708..2709 ; Extended_Pictographic# 1.1 [2] (✈️..✉️) airplane..envelope
270A..270B ; Extended_Pictographic# 6.0 [2] (✊..✋) raised fist..raised hand
270C..2712 ; Extended_Pictographic# 1.1 [7] (✌️..✒️) victory hand..black nib
-2714 ; Extended_Pictographic# 1.1 [1] (✔️) heavy check mark
-2716 ; Extended_Pictographic# 1.1 [1] (✖️) heavy multiplication x
+2714 ; Extended_Pictographic# 1.1 [1] (✔️) check mark
+2716 ; Extended_Pictographic# 1.1 [1] (✖️) multiplication sign
271D ; Extended_Pictographic# 1.1 [1] (✝️) latin cross
2721 ; Extended_Pictographic# 1.1 [1] (✡️) star of David
2728 ; Extended_Pictographic# 6.0 [1] (✨) sparkles
@@ -537,8 +571,8 @@ E0020..E007F ; Emoji_Component # 3.1 [96] (󠀠..󠁿) tag space..ca
274E ; Extended_Pictographic# 6.0 [1] (❎) cross mark button
2753..2755 ; Extended_Pictographic# 6.0 [3] (❓..❕) question mark..white exclamation mark
2757 ; Extended_Pictographic# 5.2 [1] (❗) exclamation mark
-2763..2767 ; Extended_Pictographic# 1.1 [5] (❣️..❧️) heavy heart exclamation..ROTATED FLORAL HEART BULLET
-2795..2797 ; Extended_Pictographic# 6.0 [3] (➕..➗) heavy plus sign..heavy division sign
+2763..2767 ; Extended_Pictographic# 1.1 [5] (❣️..❧) heart exclamation..ROTATED FLORAL HEART BULLET
+2795..2797 ; Extended_Pictographic# 6.0 [3] (➕..➗) plus sign..division sign
27A1 ; Extended_Pictographic# 1.1 [1] (➡️) right arrow
27B0 ; Extended_Pictographic# 6.0 [1] (➰) curly loop
27BF ; Extended_Pictographic# 6.0 [1] (➿) double curly loop
@@ -546,45 +580,46 @@ E0020..E007F ; Emoji_Component # 3.1 [96] (󠀠..󠁿) tag space..ca
2B05..2B07 ; Extended_Pictographic# 4.0 [3] (⬅️..⬇️) left arrow..down arrow
2B1B..2B1C ; Extended_Pictographic# 5.1 [2] (⬛..⬜) black large square..white large square
2B50 ; Extended_Pictographic# 5.1 [1] (⭐) star
-2B55 ; Extended_Pictographic# 5.2 [1] (⭕) heavy large circle
+2B55 ; Extended_Pictographic# 5.2 [1] (⭕) hollow red circle
3030 ; Extended_Pictographic# 1.1 [1] (〰️) wavy dash
303D ; Extended_Pictographic# 3.2 [1] (〽️) part alternation mark
3297 ; Extended_Pictographic# 1.1 [1] (㊗️) Japanese “congratulations” button
3299 ; Extended_Pictographic# 1.1 [1] (㊙️) Japanese “secret” button
-1F000..1F02B ; Extended_Pictographic# 5.1 [44] (🀀️..🀫️) MAHJONG TILE EAST WIND..MAHJONG TILE BACK
-1F02C..1F02F ; Extended_Pictographic# NA [4] (🀬️..🀯️) <reserved-1F02C>..<reserved-1F02F>
-1F030..1F093 ; Extended_Pictographic# 5.1[100] (🀰️..🂓️) DOMINO TILE HORIZONTAL BACK..DOMINO TILE VERTICAL-06-06
-1F094..1F09F ; Extended_Pictographic# NA [12] (🂔️..🂟️) <reserved-1F094>..<reserved-1F09F>
-1F0A0..1F0AE ; Extended_Pictographic# 6.0 [15] (🂠️..🂮️) PLAYING CARD BACK..PLAYING CARD KING OF SPADES
-1F0AF..1F0B0 ; Extended_Pictographic# NA [2] (🂯️..🂰️) <reserved-1F0AF>..<reserved-1F0B0>
-1F0B1..1F0BE ; Extended_Pictographic# 6.0 [14] (🂱️..🂾️) PLAYING CARD ACE OF HEARTS..PLAYING CARD KING OF HEARTS
-1F0BF ; Extended_Pictographic# 7.0 [1] (🂿️) PLAYING CARD RED JOKER
-1F0C0 ; Extended_Pictographic# NA [1] (🃀️) <reserved-1F0C0>
-1F0C1..1F0CF ; Extended_Pictographic# 6.0 [15] (🃁️..🃏) PLAYING CARD ACE OF DIAMONDS..joker
-1F0D0 ; Extended_Pictographic# NA [1] (🃐️) <reserved-1F0D0>
-1F0D1..1F0DF ; Extended_Pictographic# 6.0 [15] (🃑️..🃟️) PLAYING CARD ACE OF CLUBS..PLAYING CARD WHITE JOKER
-1F0E0..1F0F5 ; Extended_Pictographic# 7.0 [22] (🃠️..🃵️) PLAYING CARD FOOL..PLAYING CARD TRUMP-21
-1F0F6..1F0FF ; Extended_Pictographic# NA [10] (🃶️..🃿️) <reserved-1F0F6>..<reserved-1F0FF>
-1F10D..1F10F ; Extended_Pictographic# NA [3] (🄍️..🄏️) <reserved-1F10D>..<reserved-1F10F>
-1F12F ; Extended_Pictographic# 11.0 [1] (🄯️) COPYLEFT SYMBOL
-1F16C..1F16F ; Extended_Pictographic# NA [4] (🅬️..🅯️) <reserved-1F16C>..<reserved-1F16F>
+1F000..1F02B ; Extended_Pictographic# 5.1 [44] (🀀..🀫) MAHJONG TILE EAST WIND..MAHJONG TILE BACK
+1F02C..1F02F ; Extended_Pictographic# NA [4] (🀬..🀯) <reserved-1F02C>..<reserved-1F02F>
+1F030..1F093 ; Extended_Pictographic# 5.1[100] (🀰..🂓) DOMINO TILE HORIZONTAL BACK..DOMINO TILE VERTICAL-06-06
+1F094..1F09F ; Extended_Pictographic# NA [12] (🂔..🂟) <reserved-1F094>..<reserved-1F09F>
+1F0A0..1F0AE ; Extended_Pictographic# 6.0 [15] (🂠..🂮) PLAYING CARD BACK..PLAYING CARD KING OF SPADES
+1F0AF..1F0B0 ; Extended_Pictographic# NA [2] (🂯..🂰) <reserved-1F0AF>..<reserved-1F0B0>
+1F0B1..1F0BE ; Extended_Pictographic# 6.0 [14] (🂱..🂾) PLAYING CARD ACE OF HEARTS..PLAYING CARD KING OF HEARTS
+1F0BF ; Extended_Pictographic# 7.0 [1] (🂿) PLAYING CARD RED JOKER
+1F0C0 ; Extended_Pictographic# NA [1] (🃀) <reserved-1F0C0>
+1F0C1..1F0CF ; Extended_Pictographic# 6.0 [15] (🃁..🃏) PLAYING CARD ACE OF DIAMONDS..joker
+1F0D0 ; Extended_Pictographic# NA [1] (🃐) <reserved-1F0D0>
+1F0D1..1F0DF ; Extended_Pictographic# 6.0 [15] (🃑..🃟) PLAYING CARD ACE OF CLUBS..PLAYING CARD WHITE JOKER
+1F0E0..1F0F5 ; Extended_Pictographic# 7.0 [22] (🃠..🃵) PLAYING CARD FOOL..PLAYING CARD TRUMP-21
+1F0F6..1F0FF ; Extended_Pictographic# NA [10] (🃶..🃿) <reserved-1F0F6>..<reserved-1F0FF>
+1F10D..1F10F ; Extended_Pictographic# NA [3] (🄍..🄏) <reserved-1F10D>..<reserved-1F10F>
+1F12F ; Extended_Pictographic# 11.0 [1] (🄯) COPYLEFT SYMBOL
+1F16C ; Extended_Pictographic# 12.0 [1] (🅬) RAISED MR SIGN
+1F16D..1F16F ; Extended_Pictographic# NA [3] (🅭..🅯) <reserved-1F16D>..<reserved-1F16F>
1F170..1F171 ; Extended_Pictographic# 6.0 [2] (🅰️..🅱️) A button (blood type)..B button (blood type)
1F17E ; Extended_Pictographic# 6.0 [1] (🅾️) O button (blood type)
1F17F ; Extended_Pictographic# 5.2 [1] (🅿️) P button
1F18E ; Extended_Pictographic# 6.0 [1] (🆎) AB button (blood type)
1F191..1F19A ; Extended_Pictographic# 6.0 [10] (🆑..🆚) CL button..VS button
-1F1AD..1F1E5 ; Extended_Pictographic# NA [57] (🆭️..🇥️) <reserved-1F1AD>..<reserved-1F1E5>
+1F1AD..1F1E5 ; Extended_Pictographic# NA [57] (🆭..🇥) <reserved-1F1AD>..<reserved-1F1E5>
1F201..1F202 ; Extended_Pictographic# 6.0 [2] (🈁..🈂️) Japanese “here” button..Japanese “service charge” button
-1F203..1F20F ; Extended_Pictographic# NA [13] (🈃️..🈏️) <reserved-1F203>..<reserved-1F20F>
+1F203..1F20F ; Extended_Pictographic# NA [13] (🈃..🈏) <reserved-1F203>..<reserved-1F20F>
1F21A ; Extended_Pictographic# 5.2 [1] (🈚) Japanese “free of charge” button
1F22F ; Extended_Pictographic# 5.2 [1] (🈯) Japanese “reserved” button
1F232..1F23A ; Extended_Pictographic# 6.0 [9] (🈲..🈺) Japanese “prohibited” button..Japanese “open for business” button
-1F23C..1F23F ; Extended_Pictographic# NA [4] (🈼️..🈿️) <reserved-1F23C>..<reserved-1F23F>
-1F249..1F24F ; Extended_Pictographic# NA [7] (🉉️..🉏️) <reserved-1F249>..<reserved-1F24F>
+1F23C..1F23F ; Extended_Pictographic# NA [4] (🈼..🈿) <reserved-1F23C>..<reserved-1F23F>
+1F249..1F24F ; Extended_Pictographic# NA [7] (🉉..🉏) <reserved-1F249>..<reserved-1F24F>
1F250..1F251 ; Extended_Pictographic# 6.0 [2] (🉐..🉑) Japanese “bargain” button..Japanese “acceptable” button
-1F252..1F25F ; Extended_Pictographic# NA [14] (🉒️..🉟️) <reserved-1F252>..<reserved-1F25F>
-1F260..1F265 ; Extended_Pictographic# 10.0 [6] (🉠️..🉥️) ROUNDED SYMBOL FOR FU..ROUNDED SYMBOL FOR CAI
-1F266..1F2FF ; Extended_Pictographic# NA[154] (🉦️..🋿️) <reserved-1F266>..<reserved-1F2FF>
+1F252..1F25F ; Extended_Pictographic# NA [14] (🉒..🉟) <reserved-1F252>..<reserved-1F25F>
+1F260..1F265 ; Extended_Pictographic# 10.0 [6] (🉠..🉥) ROUNDED SYMBOL FOR FU..ROUNDED SYMBOL FOR CAI
+1F266..1F2FF ; Extended_Pictographic# NA[154] (🉦..🋿) <reserved-1F266>..<reserved-1F2FF>
1F300..1F320 ; Extended_Pictographic# 6.0 [33] (🌀..🌠) cyclone..shooting star
1F321..1F32C ; Extended_Pictographic# 7.0 [12] (🌡️..🌬️) thermometer..wind face
1F32D..1F32F ; Extended_Pictographic# 8.0 [3] (🌭..🌯) hot dog..burrito
@@ -594,7 +629,7 @@ E0020..E007F ; Emoji_Component # 3.1 [96] (󠀠..󠁿) tag space..ca
1F37D ; Extended_Pictographic# 7.0 [1] (🍽️) fork and knife with plate
1F37E..1F37F ; Extended_Pictographic# 8.0 [2] (🍾..🍿) bottle with popping cork..popcorn
1F380..1F393 ; Extended_Pictographic# 6.0 [20] (🎀..🎓) ribbon..graduation cap
-1F394..1F39F ; Extended_Pictographic# 7.0 [12] (🎔️..🎟️) HEART WITH TIP ON THE LEFT..admission tickets
+1F394..1F39F ; Extended_Pictographic# 7.0 [12] (🎔..🎟️) HEART WITH TIP ON THE LEFT..admission tickets
1F3A0..1F3C4 ; Extended_Pictographic# 6.0 [37] (🎠..🏄) carousel horse..person surfing
1F3C5 ; Extended_Pictographic# 7.0 [1] (🏅) sports medal
1F3C6..1F3CA ; Extended_Pictographic# 6.0 [5] (🏆..🏊) trophy..person swimming
@@ -602,7 +637,7 @@ E0020..E007F ; Emoji_Component # 3.1 [96] (󠀠..󠁿) tag space..ca
1F3CF..1F3D3 ; Extended_Pictographic# 8.0 [5] (🏏..🏓) cricket game..ping pong
1F3D4..1F3DF ; Extended_Pictographic# 7.0 [12] (🏔️..🏟️) snow-capped mountain..stadium
1F3E0..1F3F0 ; Extended_Pictographic# 6.0 [17] (🏠..🏰) house..castle
-1F3F1..1F3F7 ; Extended_Pictographic# 7.0 [7] (🏱️..🏷️) WHITE PENNANT..label
+1F3F1..1F3F7 ; Extended_Pictographic# 7.0 [7] (🏱..🏷️) WHITE PENNANT..label
1F3F8..1F3FA ; Extended_Pictographic# 8.0 [3] (🏸..🏺) badminton..amphora
1F400..1F43E ; Extended_Pictographic# 6.0 [63] (🐀..🐾) rat..paw prints
1F43F ; Extended_Pictographic# 7.0 [1] (🐿️) chipmunk
@@ -611,15 +646,15 @@ E0020..E007F ; Emoji_Component # 3.1 [96] (󠀠..󠁿) tag space..ca
1F442..1F4F7 ; Extended_Pictographic# 6.0[182] (👂..📷) ear..camera
1F4F8 ; Extended_Pictographic# 7.0 [1] (📸) camera with flash
1F4F9..1F4FC ; Extended_Pictographic# 6.0 [4] (📹..📼) video camera..videocassette
-1F4FD..1F4FE ; Extended_Pictographic# 7.0 [2] (📽️..📾️) film projector..PORTABLE STEREO
+1F4FD..1F4FE ; Extended_Pictographic# 7.0 [2] (📽️..📾) film projector..PORTABLE STEREO
1F4FF ; Extended_Pictographic# 8.0 [1] (📿) prayer beads
1F500..1F53D ; Extended_Pictographic# 6.0 [62] (🔀..🔽) shuffle tracks button..downwards button
-1F546..1F54A ; Extended_Pictographic# 7.0 [5] (🕆️..🕊️) WHITE LATIN CROSS..dove
-1F54B..1F54F ; Extended_Pictographic# 8.0 [5] (🕋..🕏️) kaaba..BOWL OF HYGIEIA
+1F546..1F54A ; Extended_Pictographic# 7.0 [5] (🕆..🕊️) WHITE LATIN CROSS..dove
+1F54B..1F54F ; Extended_Pictographic# 8.0 [5] (🕋..🕏) kaaba..BOWL OF HYGIEIA
1F550..1F567 ; Extended_Pictographic# 6.0 [24] (🕐..🕧) one o’clock..twelve-thirty
-1F568..1F579 ; Extended_Pictographic# 7.0 [18] (🕨️..🕹️) RIGHT SPEAKER..joystick
+1F568..1F579 ; Extended_Pictographic# 7.0 [18] (🕨..🕹️) RIGHT SPEAKER..joystick
1F57A ; Extended_Pictographic# 9.0 [1] (🕺) man dancing
-1F57B..1F5A3 ; Extended_Pictographic# 7.0 [41] (🕻️..🖣️) LEFT HAND TELEPHONE RECEIVER..BLACK DOWN POINTING BACKHAND INDEX
+1F57B..1F5A3 ; Extended_Pictographic# 7.0 [41] (🕻..🖣) LEFT HAND TELEPHONE RECEIVER..BLACK DOWN POINTING BACKHAND INDEX
1F5A4 ; Extended_Pictographic# 9.0 [1] (🖤) black heart
1F5A5..1F5FA ; Extended_Pictographic# 7.0 [86] (🖥️..🗺️) desktop computer..world map
1F5FB..1F5FF ; Extended_Pictographic# 6.0 [5] (🗻..🗿) mount fuji..moai
@@ -644,32 +679,37 @@ E0020..E007F ; Emoji_Component # 3.1 [96] (󠀠..󠁿) tag space..ca
1F62E..1F62F ; Extended_Pictographic# 6.1 [2] (😮..😯) face with open mouth..hushed face
1F630..1F633 ; Extended_Pictographic# 6.0 [4] (😰..😳) anxious face with sweat..flushed face
1F634 ; Extended_Pictographic# 6.1 [1] (😴) sleeping face
-1F635..1F640 ; Extended_Pictographic# 6.0 [12] (😵..🙀) dizzy face..weary cat face
+1F635..1F640 ; Extended_Pictographic# 6.0 [12] (😵..🙀) dizzy face..weary cat
1F641..1F642 ; Extended_Pictographic# 7.0 [2] (🙁..🙂) slightly frowning face..slightly smiling face
1F643..1F644 ; Extended_Pictographic# 8.0 [2] (🙃..🙄) upside-down face..face with rolling eyes
1F645..1F64F ; Extended_Pictographic# 6.0 [11] (🙅..🙏) person gesturing NO..folded hands
1F680..1F6C5 ; Extended_Pictographic# 6.0 [70] (🚀..🛅) rocket..left luggage
-1F6C6..1F6CF ; Extended_Pictographic# 7.0 [10] (🛆️..🛏️) TRIANGLE WITH ROUNDED CORNERS..bed
+1F6C6..1F6CF ; Extended_Pictographic# 7.0 [10] (🛆..🛏️) TRIANGLE WITH ROUNDED CORNERS..bed
1F6D0 ; Extended_Pictographic# 8.0 [1] (🛐) place of worship
1F6D1..1F6D2 ; Extended_Pictographic# 9.0 [2] (🛑..🛒) stop sign..shopping cart
-1F6D3..1F6D4 ; Extended_Pictographic# 10.0 [2] (🛓️..🛔️) STUPA..PAGODA
-1F6D5..1F6DF ; Extended_Pictographic# NA [11] (🛕️..🛟️) <reserved-1F6D5>..<reserved-1F6DF>
+1F6D3..1F6D4 ; Extended_Pictographic# 10.0 [2] (🛓..🛔) STUPA..PAGODA
+1F6D5 ; Extended_Pictographic# 12.0 [1] (🛕) hindu temple
+1F6D6..1F6DF ; Extended_Pictographic# NA [10] (🛖..🛟) <reserved-1F6D6>..<reserved-1F6DF>
1F6E0..1F6EC ; Extended_Pictographic# 7.0 [13] (🛠️..🛬) hammer and wrench..airplane arrival
-1F6ED..1F6EF ; Extended_Pictographic# NA [3] (🛭️..🛯️) <reserved-1F6ED>..<reserved-1F6EF>
+1F6ED..1F6EF ; Extended_Pictographic# NA [3] (🛭..🛯) <reserved-1F6ED>..<reserved-1F6EF>
1F6F0..1F6F3 ; Extended_Pictographic# 7.0 [4] (🛰️..🛳️) satellite..passenger ship
1F6F4..1F6F6 ; Extended_Pictographic# 9.0 [3] (🛴..🛶) kick scooter..canoe
1F6F7..1F6F8 ; Extended_Pictographic# 10.0 [2] (🛷..🛸) sled..flying saucer
1F6F9 ; Extended_Pictographic# 11.0 [1] (🛹) skateboard
-1F6FA..1F6FF ; Extended_Pictographic# NA [6] (🛺️..🛿️) <reserved-1F6FA>..<reserved-1F6FF>
-1F774..1F77F ; Extended_Pictographic# NA [12] (🝴️..🝿️) <reserved-1F774>..<reserved-1F77F>
-1F7D5..1F7D8 ; Extended_Pictographic# 11.0 [4] (🟕️..🟘️) CIRCLED TRIANGLE..NEGATIVE CIRCLED SQUARE
-1F7D9..1F7FF ; Extended_Pictographic# NA [39] (🟙️..🟿️) <reserved-1F7D9>..<reserved-1F7FF>
-1F80C..1F80F ; Extended_Pictographic# NA [4] (🠌️..🠏️) <reserved-1F80C>..<reserved-1F80F>
-1F848..1F84F ; Extended_Pictographic# NA [8] (🡈️..🡏️) <reserved-1F848>..<reserved-1F84F>
-1F85A..1F85F ; Extended_Pictographic# NA [6] (🡚️..🡟️) <reserved-1F85A>..<reserved-1F85F>
-1F888..1F88F ; Extended_Pictographic# NA [8] (🢈️..🢏️) <reserved-1F888>..<reserved-1F88F>
-1F8AE..1F8FF ; Extended_Pictographic# NA [82] (🢮️..🣿️) <reserved-1F8AE>..<reserved-1F8FF>
-1F90C..1F90F ; Extended_Pictographic# NA [4] (🤌️..🤏️) <reserved-1F90C>..<reserved-1F90F>
+1F6FA ; Extended_Pictographic# 12.0 [1] (🛺) auto rickshaw
+1F6FB..1F6FF ; Extended_Pictographic# NA [5] (🛻..🛿) <reserved-1F6FB>..<reserved-1F6FF>
+1F774..1F77F ; Extended_Pictographic# NA [12] (🝴..🝿) <reserved-1F774>..<reserved-1F77F>
+1F7D5..1F7D8 ; Extended_Pictographic# 11.0 [4] (🟕..🟘) CIRCLED TRIANGLE..NEGATIVE CIRCLED SQUARE
+1F7D9..1F7DF ; Extended_Pictographic# NA [7] (🟙..🟟) <reserved-1F7D9>..<reserved-1F7DF>
+1F7E0..1F7EB ; Extended_Pictographic# 12.0 [12] (🟠..🟫) orange circle..brown square
+1F7EC..1F7FF ; Extended_Pictographic# NA [20] (🟬..🟿) <reserved-1F7EC>..<reserved-1F7FF>
+1F80C..1F80F ; Extended_Pictographic# NA [4] (🠌..🠏) <reserved-1F80C>..<reserved-1F80F>
+1F848..1F84F ; Extended_Pictographic# NA [8] (🡈..🡏) <reserved-1F848>..<reserved-1F84F>
+1F85A..1F85F ; Extended_Pictographic# NA [6] (🡚..🡟) <reserved-1F85A>..<reserved-1F85F>
+1F888..1F88F ; Extended_Pictographic# NA [8] (🢈..🢏) <reserved-1F888>..<reserved-1F88F>
+1F8AE..1F8FF ; Extended_Pictographic# NA [82] (🢮..🣿) <reserved-1F8AE>..<reserved-1F8FF>
+1F90C ; Extended_Pictographic# NA [1] (🤌) <reserved-1F90C>
+1F90D..1F90F ; Extended_Pictographic# 12.0 [3] (🤍..🤏) white heart..pinching hand
1F910..1F918 ; Extended_Pictographic# 8.0 [9] (🤐..🤘) zipper-mouth face..sign of the horns
1F919..1F91E ; Extended_Pictographic# 9.0 [6] (🤙..🤞) call me hand..crossed fingers
1F91F ; Extended_Pictographic# 10.0 [1] (🤟) love-you gesture
@@ -679,35 +719,50 @@ E0020..E007F ; Emoji_Component # 3.1 [96] (󠀠..󠁿) tag space..ca
1F931..1F932 ; Extended_Pictographic# 10.0 [2] (🤱..🤲) breast-feeding..palms up together
1F933..1F93A ; Extended_Pictographic# 9.0 [8] (🤳..🤺) selfie..person fencing
1F93C..1F93E ; Extended_Pictographic# 9.0 [3] (🤼..🤾) people wrestling..person playing handball
-1F93F ; Extended_Pictographic# NA [1] (🤿️) <reserved-1F93F>
+1F93F ; Extended_Pictographic# 12.0 [1] (🤿) diving mask
1F940..1F945 ; Extended_Pictographic# 9.0 [6] (🥀..🥅) wilted flower..goal net
1F947..1F94B ; Extended_Pictographic# 9.0 [5] (🥇..🥋) 1st place medal..martial arts uniform
1F94C ; Extended_Pictographic# 10.0 [1] (🥌) curling stone
1F94D..1F94F ; Extended_Pictographic# 11.0 [3] (🥍..🥏) lacrosse..flying disc
1F950..1F95E ; Extended_Pictographic# 9.0 [15] (🥐..🥞) croissant..pancakes
1F95F..1F96B ; Extended_Pictographic# 10.0 [13] (🥟..🥫) dumpling..canned food
-1F96C..1F970 ; Extended_Pictographic# 11.0 [5] (🥬..🥰) leafy green..smiling face with 3 hearts
-1F971..1F972 ; Extended_Pictographic# NA [2] (🥱️..🥲️) <reserved-1F971>..<reserved-1F972>
+1F96C..1F970 ; Extended_Pictographic# 11.0 [5] (🥬..🥰) leafy green..smiling face with hearts
+1F971 ; Extended_Pictographic# 12.0 [1] (🥱) yawning face
+1F972 ; Extended_Pictographic# NA [1] (🥲) <reserved-1F972>
1F973..1F976 ; Extended_Pictographic# 11.0 [4] (🥳..🥶) partying face..cold face
-1F977..1F979 ; Extended_Pictographic# NA [3] (🥷️..🥹️) <reserved-1F977>..<reserved-1F979>
+1F977..1F979 ; Extended_Pictographic# NA [3] (🥷..🥹) <reserved-1F977>..<reserved-1F979>
1F97A ; Extended_Pictographic# 11.0 [1] (🥺) pleading face
-1F97B ; Extended_Pictographic# NA [1] (🥻️) <reserved-1F97B>
-1F97C..1F97F ; Extended_Pictographic# 11.0 [4] (🥼..🥿) lab coat..woman’s flat shoe
-1F980..1F984 ; Extended_Pictographic# 8.0 [5] (🦀..🦄) crab..unicorn face
+1F97B ; Extended_Pictographic# 12.0 [1] (🥻) sari
+1F97C..1F97F ; Extended_Pictographic# 11.0 [4] (🥼..🥿) lab coat..flat shoe
+1F980..1F984 ; Extended_Pictographic# 8.0 [5] (🦀..🦄) crab..unicorn
1F985..1F991 ; Extended_Pictographic# 9.0 [13] (🦅..🦑) eagle..squid
1F992..1F997 ; Extended_Pictographic# 10.0 [6] (🦒..🦗) giraffe..cricket
1F998..1F9A2 ; Extended_Pictographic# 11.0 [11] (🦘..🦢) kangaroo..swan
-1F9A3..1F9AF ; Extended_Pictographic# NA [13] (🦣️..🦯️) <reserved-1F9A3>..<reserved-1F9AF>
-1F9B0..1F9B9 ; Extended_Pictographic# 11.0 [10] (🦰..🦹) red-haired..supervillain
-1F9BA..1F9BF ; Extended_Pictographic# NA [6] (🦺️..🦿️) <reserved-1F9BA>..<reserved-1F9BF>
+1F9A3..1F9A4 ; Extended_Pictographic# NA [2] (🦣..🦤) <reserved-1F9A3>..<reserved-1F9A4>
+1F9A5..1F9AA ; Extended_Pictographic# 12.0 [6] (🦥..🦪) sloth..oyster
+1F9AB..1F9AD ; Extended_Pictographic# NA [3] (🦫..🦭) <reserved-1F9AB>..<reserved-1F9AD>
+1F9AE..1F9AF ; Extended_Pictographic# 12.0 [2] (🦮..🦯) guide dog..probing cane
+1F9B0..1F9B9 ; Extended_Pictographic# 11.0 [10] (🦰..🦹) red hair..supervillain
+1F9BA..1F9BF ; Extended_Pictographic# 12.0 [6] (🦺..🦿) safety vest..mechanical leg
1F9C0 ; Extended_Pictographic# 8.0 [1] (🧀) cheese wedge
1F9C1..1F9C2 ; Extended_Pictographic# 11.0 [2] (🧁..🧂) cupcake..salt
-1F9C3..1F9CF ; Extended_Pictographic# NA [13] (🧃️..🧏️) <reserved-1F9C3>..<reserved-1F9CF>
+1F9C3..1F9CA ; Extended_Pictographic# 12.0 [8] (🧃..🧊) beverage box..ice cube
+1F9CB..1F9CC ; Extended_Pictographic# NA [2] (🧋..🧌) <reserved-1F9CB>..<reserved-1F9CC>
+1F9CD..1F9CF ; Extended_Pictographic# 12.0 [3] (🧍..🧏) person standing..deaf person
1F9D0..1F9E6 ; Extended_Pictographic# 10.0 [23] (🧐..🧦) face with monocle..socks
1F9E7..1F9FF ; Extended_Pictographic# 11.0 [25] (🧧..🧿) red envelope..nazar amulet
-1FA00..1FA5F ; Extended_Pictographic# NA [96] (🨀️..🩟️) <reserved-1FA00>..<reserved-1FA5F>
-1FA60..1FA6D ; Extended_Pictographic# 11.0 [14] (🩠️..🩭️) XIANGQI RED GENERAL..XIANGQI BLACK SOLDIER
-1FA6E..1FFFD ; Extended_Pictographic# NA[1424] (🩮️..🿽️) <reserved-1FA6E>..<reserved-1FFFD>
+1FA00..1FA53 ; Extended_Pictographic# 12.0 [84] (🨀..🩓) NEUTRAL CHESS KING..BLACK CHESS KNIGHT-BISHOP
+1FA54..1FA5F ; Extended_Pictographic# NA [12] (🩔..🩟) <reserved-1FA54>..<reserved-1FA5F>
+1FA60..1FA6D ; Extended_Pictographic# 11.0 [14] (🩠..🩭) XIANGQI RED GENERAL..XIANGQI BLACK SOLDIER
+1FA6E..1FA6F ; Extended_Pictographic# NA [2] (🩮..🩯) <reserved-1FA6E>..<reserved-1FA6F>
+1FA70..1FA73 ; Extended_Pictographic# 12.0 [4] (🩰..🩳) ballet shoes..shorts
+1FA74..1FA77 ; Extended_Pictographic# NA [4] (🩴..🩷) <reserved-1FA74>..<reserved-1FA77>
+1FA78..1FA7A ; Extended_Pictographic# 12.0 [3] (🩸..🩺) drop of blood..stethoscope
+1FA7B..1FA7F ; Extended_Pictographic# NA [5] (🩻..🩿) <reserved-1FA7B>..<reserved-1FA7F>
+1FA80..1FA82 ; Extended_Pictographic# 12.0 [3] (🪀..🪂) yo-yo..parachute
+1FA83..1FA8F ; Extended_Pictographic# NA [13] (🪃..🪏) <reserved-1FA83>..<reserved-1FA8F>
+1FA90..1FA95 ; Extended_Pictographic# 12.0 [6] (🪐..🪕) ringed planet..banjo
+1FA96..1FFFD ; Extended_Pictographic# NA[1384] (🪖..🿽) <reserved-1FA96>..<reserved-1FFFD>
# Total elements: 3793
diff --git a/lib/stdlib/uc_spec/gen_unicode_mod.escript b/lib/stdlib/uc_spec/gen_unicode_mod.escript
index de67b18afc..d820b9ed8e 100644
--- a/lib/stdlib/uc_spec/gen_unicode_mod.escript
+++ b/lib/stdlib/uc_spec/gen_unicode_mod.escript
@@ -191,7 +191,7 @@ gen_static(Fd) ->
" {U,L} -> #{upper=>U,lower=>L,title=>U,fold=>L};\n"
" {U,L,T,F} -> #{upper=>U,lower=>L,title=>T,fold=>F}\n"
" end.\n\n"),
- io:put_chars(Fd, "spec_version() -> {11,0}.\n\n\n"),
+ io:put_chars(Fd, "spec_version() -> {12,1}.\n\n\n"),
io:put_chars(Fd, "class(Codepoint) -> {CCC,_,_} = unicode_table(Codepoint),\n CCC.\n\n"),
io:put_chars(Fd, "-spec uppercase(unicode:chardata()) -> "
"maybe_improper_list(gc(),unicode:chardata()).\n"),
diff --git a/lib/stdlib/vsn.mk b/lib/stdlib/vsn.mk
index c2f586fef5..e2ed11a3d2 100644
--- a/lib/stdlib/vsn.mk
+++ b/lib/stdlib/vsn.mk
@@ -1 +1 @@
-STDLIB_VSN = 3.9.2
+STDLIB_VSN = 3.10
diff --git a/lib/syntax_tools/doc/src/notes.xml b/lib/syntax_tools/doc/src/notes.xml
index a2dd78f280..31b3e45016 100644
--- a/lib/syntax_tools/doc/src/notes.xml
+++ b/lib/syntax_tools/doc/src/notes.xml
@@ -32,6 +32,22 @@
<p>This document describes the changes made to the Syntax_Tools
application.</p>
+<section><title>Syntax_Tools 2.2.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Add missing calls to <c>erl_syntax:unwrap/1</c>. The
+ nodes concerned represent names and values of maps and
+ map types. </p>
+ <p>
+ Own Id: OTP-16012 Aux Id: PR-2348 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Syntax_Tools 2.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -60,6 +76,22 @@
</section>
+<section><title>Syntax_Tools 2.1.7.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Add missing calls to <c>erl_syntax:unwrap/1</c>. The
+ nodes concerned represent names and values of maps and
+ map types. </p>
+ <p>
+ Own Id: OTP-16012 Aux Id: PR-2348 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Syntax_Tools 2.1.7</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -786,4 +818,3 @@
<p>Miscellaneous changes.</p>
</section>
</chapter>
-
diff --git a/lib/syntax_tools/src/erl_syntax.erl b/lib/syntax_tools/src/erl_syntax.erl
index 1c0c532323..ed94bd383c 100644
--- a/lib/syntax_tools/src/erl_syntax.erl
+++ b/lib/syntax_tools/src/erl_syntax.erl
@@ -2191,11 +2191,11 @@ revert_map_field_assoc(Node) ->
-spec map_field_assoc_name(syntaxTree()) -> syntaxTree().
map_field_assoc_name(Node) ->
- case Node of
+ case unwrap(Node) of
{map_field_assoc, _, Name, _} ->
Name;
- _ ->
- (data(Node))#map_field_assoc.name
+ Node1 ->
+ (data(Node1))#map_field_assoc.name
end.
@@ -2207,11 +2207,11 @@ map_field_assoc_name(Node) ->
-spec map_field_assoc_value(syntaxTree()) -> syntaxTree().
map_field_assoc_value(Node) ->
- case Node of
+ case unwrap(Node) of
{map_field_assoc, _, _, Value} ->
Value;
- _ ->
- (data(Node))#map_field_assoc.value
+ Node1 ->
+ (data(Node1))#map_field_assoc.value
end.
@@ -2249,11 +2249,11 @@ revert_map_field_exact(Node) ->
-spec map_field_exact_name(syntaxTree()) -> syntaxTree().
map_field_exact_name(Node) ->
- case Node of
+ case unwrap(Node) of
{map_field_exact, _, Name, _} ->
Name;
- _ ->
- (data(Node))#map_field_exact.name
+ Node1 ->
+ (data(Node1))#map_field_exact.name
end.
@@ -2265,11 +2265,11 @@ map_field_exact_name(Node) ->
-spec map_field_exact_value(syntaxTree()) -> syntaxTree().
map_field_exact_value(Node) ->
- case Node of
+ case unwrap(Node) of
{map_field_exact, _, _, Value} ->
Value;
- _ ->
- (data(Node))#map_field_exact.value
+ Node1 ->
+ (data(Node1))#map_field_exact.value
end.
@@ -5338,11 +5338,11 @@ revert_map_type_assoc(Node) ->
-spec map_type_assoc_name(syntaxTree()) -> syntaxTree().
map_type_assoc_name(Node) ->
- case Node of
+ case unwrap(Node) of
{type, _, map_field_assoc, [Name, _]} ->
Name;
- _ ->
- (data(Node))#map_type_assoc.name
+ Node1 ->
+ (data(Node1))#map_type_assoc.name
end.
@@ -5354,11 +5354,11 @@ map_type_assoc_name(Node) ->
-spec map_type_assoc_value(syntaxTree()) -> syntaxTree().
map_type_assoc_value(Node) ->
- case Node of
+ case unwrap(Node) of
{type, _, map_field_assoc, [_, Value]} ->
Value;
- _ ->
- (data(Node))#map_type_assoc.value
+ Node1 ->
+ (data(Node1))#map_type_assoc.value
end.
@@ -5396,11 +5396,11 @@ revert_map_type_exact(Node) ->
-spec map_type_exact_name(syntaxTree()) -> syntaxTree().
map_type_exact_name(Node) ->
- case Node of
+ case unwrap(Node) of
{type, _, map_field_exact, [Name, _]} ->
Name;
- _ ->
- (data(Node))#map_type_exact.name
+ Node1 ->
+ (data(Node1))#map_type_exact.name
end.
@@ -5412,11 +5412,11 @@ map_type_exact_name(Node) ->
-spec map_type_exact_value(syntaxTree()) -> syntaxTree().
map_type_exact_value(Node) ->
- case Node of
+ case unwrap(Node) of
{type, _, map_field_exact, [_, Value]} ->
Value;
- _ ->
- (data(Node))#map_type_exact.value
+ Node1 ->
+ (data(Node1))#map_type_exact.value
end.
diff --git a/lib/syntax_tools/test/syntax_tools_SUITE.erl b/lib/syntax_tools/test/syntax_tools_SUITE.erl
index e1dd1bd73b..9baf36ce11 100644
--- a/lib/syntax_tools/test/syntax_tools_SUITE.erl
+++ b/lib/syntax_tools/test/syntax_tools_SUITE.erl
@@ -24,7 +24,7 @@
%% Test cases
-export([app_test/1,appup_test/1,smoke_test/1,revert/1,revert_map/1,
- revert_map_type/1,
+ revert_map_type/1,wrapped_subtrees/1,
t_abstract_type/1,t_erl_parse_type/1,t_type/1, t_epp_dodger/1,
t_comment_scan/1,t_igor/1,t_erl_tidy/1,t_prettypr/1]).
@@ -32,6 +32,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[app_test,appup_test,smoke_test,revert,revert_map,revert_map_type,
+ wrapped_subtrees,
t_abstract_type,t_erl_parse_type,t_type,t_epp_dodger,
t_comment_scan,t_igor,t_erl_tidy,t_prettypr].
@@ -143,6 +144,41 @@ revert_map_type(Config) when is_list(Config) ->
Form2 = erl_syntax:revert(Mapped2),
?t:timetrap_cancel(Dog).
+%% Read with erl_parse, wrap each tree node with erl_syntax and check that
+%% erl_syntax:subtrees can access the wrapped node.
+wrapped_subtrees(Config) when is_list(Config) ->
+ Dog = ?t:timetrap(?t:minutes(2)),
+ Wc = filename:join([code:lib_dir(stdlib),"src","*.erl"]),
+ Fs = filelib:wildcard(Wc) ++ test_files(Config),
+ Path = [filename:join(code:lib_dir(stdlib), "include"),
+ filename:join(code:lib_dir(kernel), "include")],
+ io:format("~p files\n", [length(Fs)]),
+ Map = fun (File) -> wrapped_subtrees_file(File, Path) end,
+ case p_run(Map, Fs) of
+ 0 -> ok;
+ N -> ?t:fail({N,errors})
+ end,
+ ?t:timetrap_cancel(Dog).
+
+wrapped_subtrees_file(File, Path) ->
+ case epp:parse_file(File, Path, []) of
+ {ok,Fs0} ->
+ lists:foreach(fun wrap_each/1, Fs0)
+ end.
+
+wrap_each(Tree) ->
+ % only `wrap` top-level erl_parse node
+ Tree1 = erl_syntax:set_pos(Tree, erl_syntax:get_pos(Tree)),
+ % assert ability to access subtrees of wrapped node with erl_syntax:subtrees/1
+ case erl_syntax:subtrees(Tree1) of
+ [] -> ok;
+ List ->
+ GrpsF = fun(Group) ->
+ lists:foreach(fun wrap_each/1, Group)
+ end,
+ lists:foreach(GrpsF, List)
+ end.
+
%% api tests
t_type(Config) when is_list(Config) ->
diff --git a/lib/syntax_tools/test/syntax_tools_SUITE_data/type_specs.erl b/lib/syntax_tools/test/syntax_tools_SUITE_data/type_specs.erl
index e4f8a1c3de..b23acdb39e 100644
--- a/lib/syntax_tools/test/syntax_tools_SUITE_data/type_specs.erl
+++ b/lib/syntax_tools/test/syntax_tools_SUITE_data/type_specs.erl
@@ -37,6 +37,9 @@
-record(par, {a :: undefined | ?MODULE}).
+-record(mt, {e :: #{any() := any()},
+ a :: #{any() => any()}}).
+
-record(r0, {}).
-record(r,
diff --git a/lib/syntax_tools/vsn.mk b/lib/syntax_tools/vsn.mk
index 0ace11772d..9e6967d45d 100644
--- a/lib/syntax_tools/vsn.mk
+++ b/lib/syntax_tools/vsn.mk
@@ -1 +1 @@
-SYNTAX_TOOLS_VSN = 2.2
+SYNTAX_TOOLS_VSN = 2.2.1
diff --git a/lib/tftp/Makefile b/lib/tftp/Makefile
index 348a4a86b6..d0397beaa0 100644
--- a/lib/tftp/Makefile
+++ b/lib/tftp/Makefile
@@ -43,38 +43,6 @@ include $(ERL_TOP)/make/otp_subdir.mk
.PHONY: info gclean dialyzer dialyzer_plt dclean
-info:
- @echo "OS: $(OS)"
- @echo "DOCB: $(DOCB)"
- @echo ""
- @echo "TFTP_VSN: $(TFTP_VSN)"
- @echo "APP_VSN: $(APP_VSN)"
- @echo ""
- @echo "DIA_PLT: $(DIA_PLT)"
- @echo "DIA_ANALYSIS: $(DIA_ANALYSIS)"
- @echo ""
-
-gclean:
- git clean -fXd
-
-dclean:
- rm -f $(DIA_PLT)
- rm -f $(DIA_ANALYSIS)
-
-dialyzer_plt: $(DIA_PLT)
-
-$(DIA_PLT):
- @echo "Building $(APPLICATION) plt file"
- @dialyzer --build_plt \
- --output_plt $@ \
- -r ../$(APPLICATION)/ebin \
- --output $(DIA_ANALYSIS) \
- --verbose
-
-dialyzer: $(DIA_PLT)
- @echo "Running dialyzer on $(APPLICATION)"
- @dialyzer --plt $< \
- ../$(APPLICATION)/ebin \
- --verbose
+DIA_PLT_APPS=
include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/tools/Makefile b/lib/tools/Makefile
index 811926e20d..d849989a2d 100644
--- a/lib/tools/Makefile
+++ b/lib/tools/Makefile
@@ -36,4 +36,6 @@ SPECIAL_TARGETS =
# ----------------------------------------------------
include $(ERL_TOP)/make/otp_subdir.mk
+DIA_PLT_APPS=compiler runtime_tools
+
include $(ERL_TOP)/make/app_targets.mk
diff --git a/lib/tools/doc/src/notes.xml b/lib/tools/doc/src/notes.xml
index 09ae5ef04a..c1e664b10f 100644
--- a/lib/tools/doc/src/notes.xml
+++ b/lib/tools/doc/src/notes.xml
@@ -31,6 +31,21 @@
</header>
<p>This document describes the changes made to the Tools application.</p>
+<section><title>Tools 3.2.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p><c>cover</c> would fail to start if two processes
+ tried to start it at the exact same time.</p>
+ <p>
+ Own Id: OTP-15813 Aux Id: ERL-943 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Tools 3.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/tools/vsn.mk b/lib/tools/vsn.mk
index 191a458c62..022332e840 100644
--- a/lib/tools/vsn.mk
+++ b/lib/tools/vsn.mk
@@ -1 +1 @@
-TOOLS_VSN = 3.2
+TOOLS_VSN = 3.2.1
diff --git a/lib/wx/doc/src/notes.xml b/lib/wx/doc/src/notes.xml
index 0c3374091d..4de771d209 100644
--- a/lib/wx/doc/src/notes.xml
+++ b/lib/wx/doc/src/notes.xml
@@ -32,6 +32,22 @@
<p>This document describes the changes made to the wxErlang
application.</p>
+<section><title>Wx 1.8.9</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix a driver bug that could crashes when allocating
+ memory.</p>
+ <p>
+ Own Id: OTP-15883 Aux Id: PR-2261 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Wx 1.8.8</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/wx/vsn.mk b/lib/wx/vsn.mk
index 91d15de3a9..ec07f7b691 100644
--- a/lib/wx/vsn.mk
+++ b/lib/wx/vsn.mk
@@ -1 +1 @@
-WX_VSN = 1.8.8
+WX_VSN = 1.8.9
diff --git a/lib/xmerl/Makefile b/lib/xmerl/Makefile
index 84b243fe68..65a0af9e4a 100644
--- a/lib/xmerl/Makefile
+++ b/lib/xmerl/Makefile
@@ -50,12 +50,7 @@ SPECIAL_TARGETS =
include $(ERL_TOP)/make/otp_subdir.mk
-.PHONY: info version
-
-info:
- @echo "APP_RELEASE_DIR: $(APP_RELEASE_DIR)"
- @echo "APP_DIR: $(APP_DIR)"
- @echo "APP_TAR_FILE: $(APP_TAR_FILE)"
+.PHONY: version
version:
@echo "$(VSN)"
diff --git a/lib/xmerl/doc/src/notes.xml b/lib/xmerl/doc/src/notes.xml
index 9fb4a430e5..4355b7114a 100644
--- a/lib/xmerl/doc/src/notes.xml
+++ b/lib/xmerl/doc/src/notes.xml
@@ -32,6 +32,23 @@
<p>This document describes the changes made to the Xmerl application.</p>
+<section><title>Xmerl 1.3.22</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ <c>xmerl_sax_parser</c> crashed during charset detection
+ when the xml declarations attribute values was missing
+ the closing quotation (&apos; or &quot;).</p>
+ <p>
+ Own Id: OTP-15826</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Xmerl 1.3.21</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/xmerl/vsn.mk b/lib/xmerl/vsn.mk
index 08696606e6..fc73964773 100644
--- a/lib/xmerl/vsn.mk
+++ b/lib/xmerl/vsn.mk
@@ -1 +1 @@
-XMERL_VSN = 1.3.21
+XMERL_VSN = 1.3.22
diff --git a/make/app_targets.mk b/make/app_targets.mk
index 3f28a529d4..e9aaa4193c 100644
--- a/make/app_targets.mk
+++ b/make/app_targets.mk
@@ -18,8 +18,49 @@
# %CopyrightEnd%
#
+APPLICATION ?= $(basename $(notdir $(PWD)))
-.PHONY: test
+.PHONY: test info gclean dialyzer dialyzer_plt dclean
test:
$(ERL_TOP)/make/test_target_script.sh $(ERL_TOP)
+
+info:
+ @echo "$(APPLICATION)_VSN: $(VSN)"
+ @echo "APP_VSN: $(APP_VSN)"
+ @echo ""
+ @echo "DIA_PLT: $(DIA_PLT)"
+ @echo "DIA_ANALYSIS: $(DIA_ANALYSIS)"
+ @echo ""
+
+gclean:
+ git clean -fXd
+
+
+DIA_DEFAULT_PLT_APPS = erts kernel stdlib $(APPLICATION)
+DIA_PLT_DIR = ./priv/plt
+DIA_PLT = $(DIA_PLT_DIR)/$(APPLICATION).plt
+DIA_ANALYSIS = $(basename $(DIA_PLT)).dialyzer_analysis
+
+dialyzer_plt: $(DIA_PLT)
+
+$(DIA_PLT_DIR):
+ @mkdir -p $@
+
+$(DIA_PLT): $(DIA_PLT_DIR)
+ @echo "Building $(APPLICATION) plt file"
+ @$(ERL_TOP)/bin/dialyzer --build_plt \
+ --output_plt $@ \
+ --apps $(sort $(DIA_PLT_APPS) $(DIA_DEFAULT_PLT_APPS)) \
+ --output $(DIA_ANALYSIS) \
+ --verbose
+
+dialyzer: $(DIA_PLT)
+ @echo "Running dialyzer on $(APPLICATION)"
+ @dialyzer --plt $< \
+ ../$(APPLICATION)/ebin \
+ --verbose
+
+dclean:
+ rm -f $(DIA_PLT)
+ rm -f $(DIA_ANALYSIS)
diff --git a/make/otp.mk.in b/make/otp.mk.in
index cc76f00e7e..63748c2f2b 100644
--- a/make/otp.mk.in
+++ b/make/otp.mk.in
@@ -321,5 +321,5 @@ $(MAN6DIR)/%.6 $(MAN6DIR)/%.7: $(XMLDIR)/%_app.xml
$(XMLDIR)/%.xml: $(XMLDIR)/%.xmlsrc
$(gen_verbose)escript $(DOCGEN)/priv/bin/codeline_preprocessing.escript $(shell pwd) $< $@
-.fo.pdf:
+$(PDFDIR)/%.pdf: %.fo
$(FOP) -c $(FOP_CONFIG) -cache $(ERL_TOP)/make/$(TARGET)/fop-fonts.cache -fo $< -pdf $@
diff --git a/make/otp_patch_solve_forward_merge_version b/make/otp_patch_solve_forward_merge_version
index b4de394767..48082f72f0 100644
--- a/make/otp_patch_solve_forward_merge_version
+++ b/make/otp_patch_solve_forward_merge_version
@@ -1 +1 @@
-11
+12
diff --git a/make/otp_version_tickets_in_merge b/make/otp_version_tickets_in_merge
index e69de29bb2..a8bd952e0e 100644
--- a/make/otp_version_tickets_in_merge
+++ b/make/otp_version_tickets_in_merge
@@ -0,0 +1,125 @@
+OTP-10400
+OTP-13872
+OTP-15328
+OTP-15330
+OTP-15331
+OTP-15332
+OTP-15344
+OTP-15370
+OTP-15395
+OTP-15422
+OTP-15431
+OTP-15510
+OTP-15566
+OTP-15731
+OTP-15738
+OTP-15747
+OTP-15764
+OTP-15765
+OTP-15778
+OTP-15789
+OTP-15807
+OTP-15813
+OTP-15814
+OTP-15817
+OTP-15818
+OTP-15820
+OTP-15822
+OTP-15823
+OTP-15826
+OTP-15827
+OTP-15830
+OTP-15831
+OTP-15833
+OTP-15836
+OTP-15843
+OTP-15849
+OTP-15850
+OTP-15851
+OTP-15852
+OTP-15853
+OTP-15854
+OTP-15858
+OTP-15859
+OTP-15863
+OTP-15869
+OTP-15870
+OTP-15874
+OTP-15876
+OTP-15880
+OTP-15881
+OTP-15882
+OTP-15883
+OTP-15884
+OTP-15889
+OTP-15893
+OTP-15897
+OTP-15901
+OTP-15904
+OTP-15905
+OTP-15906
+OTP-15911
+OTP-15916
+OTP-15917
+OTP-15918
+OTP-15923
+OTP-15924
+OTP-15926
+OTP-15927
+OTP-15928
+OTP-15929
+OTP-15931
+OTP-15932
+OTP-15933
+OTP-15934
+OTP-15935
+OTP-15942
+OTP-15954
+OTP-15955
+OTP-15958
+OTP-15959
+OTP-15962
+OTP-15963
+OTP-15966
+OTP-15968
+OTP-15969
+OTP-15970
+OTP-15971
+OTP-15974
+OTP-15975
+OTP-15977
+OTP-15978
+OTP-15979
+OTP-15980
+OTP-15982
+OTP-15983
+OTP-15984
+OTP-15985
+OTP-15987
+OTP-15992
+OTP-15996
+OTP-15997
+OTP-16000
+OTP-16001
+OTP-16002
+OTP-16006
+OTP-16012
+OTP-16022
+OTP-16023
+OTP-16027
+OTP-16028
+OTP-16032
+OTP-16035
+OTP-16036
+OTP-16037
+OTP-16038
+OTP-16040
+OTP-16041
+OTP-16042
+OTP-16044
+OTP-16049
+OTP-16050
+OTP-16051
+OTP-16056
+OTP-16058
+OTP-16060
diff --git a/otp_versions.table b/otp_versions.table
index 415e9dcc04..d55b21470d 100644
--- a/otp_versions.table
+++ b/otp_versions.table
@@ -1,3 +1,5 @@
+OTP-22.1 : common_test-1.18 compiler-7.4.5 crypto-4.6 dialyzer-4.1 erl_docgen-0.10 erl_interface-3.13 erts-10.5 eunit-2.3.8 ftp-1.0.3 inets-7.1 jinterface-1.10.1 kernel-6.5 megaco-3.18.6 mnesia-4.16.1 observer-2.9.2 os_mon-2.5.1 public_key-1.7 runtime_tools-1.14 sasl-3.4.1 snmp-5.4 ssh-4.8 ssl-9.4 stdlib-3.10 syntax_tools-2.2.1 tools-3.2.1 wx-1.8.9 xmerl-1.3.22 # asn1-5.0.9 debugger-4.2.7 diameter-2.2.1 edoc-0.11 eldap-1.2.8 et-1.6.4 hipe-3.19.1 odbc-2.12.4 parsetools-2.1.8 reltool-0.8 tftp-1.0.1 :
+OTP-22.0.7 : compiler-7.4.4 # asn1-5.0.9 common_test-1.17.3 crypto-4.5.1 debugger-4.2.7 dialyzer-4.0.3 diameter-2.2.1 edoc-0.11 eldap-1.2.8 erl_docgen-0.9.1 erl_interface-3.12 erts-10.4.4 et-1.6.4 eunit-2.3.7 ftp-1.0.2 hipe-3.19.1 inets-7.0.9 jinterface-1.10 kernel-6.4.1 megaco-3.18.5 mnesia-4.16 observer-2.9.1 odbc-2.12.4 os_mon-2.5 parsetools-2.1.8 public_key-1.6.7 reltool-0.8 runtime_tools-1.13.3 sasl-3.4 snmp-5.3 ssh-4.7.7 ssl-9.3.5 stdlib-3.9.2 syntax_tools-2.2 tftp-1.0.1 tools-3.2 wx-1.8.8 xmerl-1.3.21 :
OTP-22.0.6 : compiler-7.4.3 dialyzer-4.0.3 hipe-3.19.1 ssl-9.3.5 # asn1-5.0.9 common_test-1.17.3 crypto-4.5.1 debugger-4.2.7 diameter-2.2.1 edoc-0.11 eldap-1.2.8 erl_docgen-0.9.1 erl_interface-3.12 erts-10.4.4 et-1.6.4 eunit-2.3.7 ftp-1.0.2 inets-7.0.9 jinterface-1.10 kernel-6.4.1 megaco-3.18.5 mnesia-4.16 observer-2.9.1 odbc-2.12.4 os_mon-2.5 parsetools-2.1.8 public_key-1.6.7 reltool-0.8 runtime_tools-1.13.3 sasl-3.4 snmp-5.3 ssh-4.7.7 stdlib-3.9.2 syntax_tools-2.2 tftp-1.0.1 tools-3.2 wx-1.8.8 xmerl-1.3.21 :
OTP-22.0.5 : dialyzer-4.0.2 erts-10.4.4 inets-7.0.9 ssl-9.3.4 # asn1-5.0.9 common_test-1.17.3 compiler-7.4.2 crypto-4.5.1 debugger-4.2.7 diameter-2.2.1 edoc-0.11 eldap-1.2.8 erl_docgen-0.9.1 erl_interface-3.12 et-1.6.4 eunit-2.3.7 ftp-1.0.2 hipe-3.19 jinterface-1.10 kernel-6.4.1 megaco-3.18.5 mnesia-4.16 observer-2.9.1 odbc-2.12.4 os_mon-2.5 parsetools-2.1.8 public_key-1.6.7 reltool-0.8 runtime_tools-1.13.3 sasl-3.4 snmp-5.3 ssh-4.7.7 stdlib-3.9.2 syntax_tools-2.2 tftp-1.0.1 tools-3.2 wx-1.8.8 xmerl-1.3.21 :
OTP-22.0.4 : erts-10.4.3 kernel-6.4.1 ssl-9.3.3 # asn1-5.0.9 common_test-1.17.3 compiler-7.4.2 crypto-4.5.1 debugger-4.2.7 dialyzer-4.0.1 diameter-2.2.1 edoc-0.11 eldap-1.2.8 erl_docgen-0.9.1 erl_interface-3.12 et-1.6.4 eunit-2.3.7 ftp-1.0.2 hipe-3.19 inets-7.0.8 jinterface-1.10 megaco-3.18.5 mnesia-4.16 observer-2.9.1 odbc-2.12.4 os_mon-2.5 parsetools-2.1.8 public_key-1.6.7 reltool-0.8 runtime_tools-1.13.3 sasl-3.4 snmp-5.3 ssh-4.7.7 stdlib-3.9.2 syntax_tools-2.2 tftp-1.0.1 tools-3.2 wx-1.8.8 xmerl-1.3.21 :
@@ -5,6 +7,8 @@ OTP-22.0.3 : compiler-7.4.2 dialyzer-4.0.1 erts-10.4.2 ssl-9.3.2 stdlib-3.9.2 #
OTP-22.0.2 : compiler-7.4.1 crypto-4.5.1 erts-10.4.1 stdlib-3.9.1 # asn1-5.0.9 common_test-1.17.3 debugger-4.2.7 dialyzer-4.0 diameter-2.2.1 edoc-0.11 eldap-1.2.8 erl_docgen-0.9.1 erl_interface-3.12 et-1.6.4 eunit-2.3.7 ftp-1.0.2 hipe-3.19 inets-7.0.8 jinterface-1.10 kernel-6.4 megaco-3.18.5 mnesia-4.16 observer-2.9.1 odbc-2.12.4 os_mon-2.5 parsetools-2.1.8 public_key-1.6.7 reltool-0.8 runtime_tools-1.13.3 sasl-3.4 snmp-5.3 ssh-4.7.7 ssl-9.3.1 syntax_tools-2.2 tftp-1.0.1 tools-3.2 wx-1.8.8 xmerl-1.3.21 :
OTP-22.0.1 : ssl-9.3.1 # asn1-5.0.9 common_test-1.17.3 compiler-7.4 crypto-4.5 debugger-4.2.7 dialyzer-4.0 diameter-2.2.1 edoc-0.11 eldap-1.2.8 erl_docgen-0.9.1 erl_interface-3.12 erts-10.4 et-1.6.4 eunit-2.3.7 ftp-1.0.2 hipe-3.19 inets-7.0.8 jinterface-1.10 kernel-6.4 megaco-3.18.5 mnesia-4.16 observer-2.9.1 odbc-2.12.4 os_mon-2.5 parsetools-2.1.8 public_key-1.6.7 reltool-0.8 runtime_tools-1.13.3 sasl-3.4 snmp-5.3 ssh-4.7.7 stdlib-3.9 syntax_tools-2.2 tftp-1.0.1 tools-3.2 wx-1.8.8 xmerl-1.3.21 :
OTP-22.0 : asn1-5.0.9 common_test-1.17.3 compiler-7.4 crypto-4.5 debugger-4.2.7 dialyzer-4.0 edoc-0.11 eldap-1.2.8 erl_docgen-0.9.1 erl_interface-3.12 erts-10.4 hipe-3.19 inets-7.0.8 jinterface-1.10 kernel-6.4 megaco-3.18.5 mnesia-4.16 observer-2.9.1 odbc-2.12.4 os_mon-2.5 public_key-1.6.7 reltool-0.8 runtime_tools-1.13.3 sasl-3.4 snmp-5.3 ssh-4.7.7 ssl-9.3 stdlib-3.9 syntax_tools-2.2 tools-3.2 wx-1.8.8 xmerl-1.3.21 # diameter-2.2.1 et-1.6.4 eunit-2.3.7 ftp-1.0.2 parsetools-2.1.8 tftp-1.0.1 :
+OTP-21.3.8.7 : erts-10.3.5.5 inets-7.0.7.1 kernel-6.3.1.3 ssh-4.7.6.1 syntax_tools-2.1.7.1 # asn1-5.0.8 common_test-1.17.2.1 compiler-7.3.2 crypto-4.4.2 debugger-4.2.6 dialyzer-3.3.2 diameter-2.2.1 edoc-0.10 eldap-1.2.7 erl_docgen-0.9 erl_interface-3.11.3 et-1.6.4 eunit-2.3.7 ftp-1.0.2 hipe-3.18.3 jinterface-1.9.1 megaco-3.18.4 mnesia-4.15.6 observer-2.9 odbc-2.12.3 os_mon-2.4.7 otp_mibs-1.2.1 parsetools-2.1.8 public_key-1.6.6.1 reltool-0.7.8 runtime_tools-1.13.2 sasl-3.3 snmp-5.2.12 ssl-9.2.3.5 stdlib-3.8.2.2 tftp-1.0.1 tools-3.1.0.1 wx-1.8.7 xmerl-1.3.20.1 :
+OTP-21.3.8.6 : ssl-9.2.3.5 # asn1-5.0.8 common_test-1.17.2.1 compiler-7.3.2 crypto-4.4.2 debugger-4.2.6 dialyzer-3.3.2 diameter-2.2.1 edoc-0.10 eldap-1.2.7 erl_docgen-0.9 erl_interface-3.11.3 erts-10.3.5.4 et-1.6.4 eunit-2.3.7 ftp-1.0.2 hipe-3.18.3 inets-7.0.7 jinterface-1.9.1 kernel-6.3.1.2 megaco-3.18.4 mnesia-4.15.6 observer-2.9 odbc-2.12.3 os_mon-2.4.7 otp_mibs-1.2.1 parsetools-2.1.8 public_key-1.6.6.1 reltool-0.7.8 runtime_tools-1.13.2 sasl-3.3 snmp-5.2.12 ssh-4.7.6 stdlib-3.8.2.2 syntax_tools-2.1.7 tftp-1.0.1 tools-3.1.0.1 wx-1.8.7 xmerl-1.3.20.1 :
OTP-21.3.8.5 : erts-10.3.5.4 ssl-9.2.3.4 # asn1-5.0.8 common_test-1.17.2.1 compiler-7.3.2 crypto-4.4.2 debugger-4.2.6 dialyzer-3.3.2 diameter-2.2.1 edoc-0.10 eldap-1.2.7 erl_docgen-0.9 erl_interface-3.11.3 et-1.6.4 eunit-2.3.7 ftp-1.0.2 hipe-3.18.3 inets-7.0.7 jinterface-1.9.1 kernel-6.3.1.2 megaco-3.18.4 mnesia-4.15.6 observer-2.9 odbc-2.12.3 os_mon-2.4.7 otp_mibs-1.2.1 parsetools-2.1.8 public_key-1.6.6.1 reltool-0.7.8 runtime_tools-1.13.2 sasl-3.3 snmp-5.2.12 ssh-4.7.6 stdlib-3.8.2.2 syntax_tools-2.1.7 tftp-1.0.1 tools-3.1.0.1 wx-1.8.7 xmerl-1.3.20.1 :
OTP-21.3.8.4 : common_test-1.17.2.1 erts-10.3.5.3 kernel-6.3.1.2 public_key-1.6.6.1 ssl-9.2.3.3 stdlib-3.8.2.2 # asn1-5.0.8 compiler-7.3.2 crypto-4.4.2 debugger-4.2.6 dialyzer-3.3.2 diameter-2.2.1 edoc-0.10 eldap-1.2.7 erl_docgen-0.9 erl_interface-3.11.3 et-1.6.4 eunit-2.3.7 ftp-1.0.2 hipe-3.18.3 inets-7.0.7 jinterface-1.9.1 megaco-3.18.4 mnesia-4.15.6 observer-2.9 odbc-2.12.3 os_mon-2.4.7 otp_mibs-1.2.1 parsetools-2.1.8 reltool-0.7.8 runtime_tools-1.13.2 sasl-3.3 snmp-5.2.12 ssh-4.7.6 syntax_tools-2.1.7 tftp-1.0.1 tools-3.1.0.1 wx-1.8.7 xmerl-1.3.20.1 :
OTP-21.3.8.3 : erts-10.3.5.2 kernel-6.3.1.1 ssl-9.2.3.2 stdlib-3.8.2.1 # asn1-5.0.8 common_test-1.17.2 compiler-7.3.2 crypto-4.4.2 debugger-4.2.6 dialyzer-3.3.2 diameter-2.2.1 edoc-0.10 eldap-1.2.7 erl_docgen-0.9 erl_interface-3.11.3 et-1.6.4 eunit-2.3.7 ftp-1.0.2 hipe-3.18.3 inets-7.0.7 jinterface-1.9.1 megaco-3.18.4 mnesia-4.15.6 observer-2.9 odbc-2.12.3 os_mon-2.4.7 otp_mibs-1.2.1 parsetools-2.1.8 public_key-1.6.6 reltool-0.7.8 runtime_tools-1.13.2 sasl-3.3 snmp-5.2.12 ssh-4.7.6 syntax_tools-2.1.7 tftp-1.0.1 tools-3.1.0.1 wx-1.8.7 xmerl-1.3.20.1 :
diff --git a/scripts/build-otp b/scripts/build-otp
index abf8d5d67f..afab1f9136 100755
--- a/scripts/build-otp
+++ b/scripts/build-otp
@@ -42,8 +42,11 @@ if [ ! -d "logs" ]; then
mkdir logs
fi
+export ERLC_USE_SERVER=yes
do_and_log "Autoconfing" ./otp_build autoconf
do_and_log "Configuring" ./otp_build configure
+echo Configure result:
+tail -n 20 $log
do_and_log "Building OTP" ./otp_build boot -a
if [ "$1" = "release" ]; then
diff --git a/system/doc/design_principles/statem.xml b/system/doc/design_principles/statem.xml
index 23e9054547..360bf958ad 100644
--- a/system/doc/design_principles/statem.xml
+++ b/system/doc/design_principles/statem.xml
@@ -108,7 +108,9 @@ State(S) x Event(E) -> Actions(A), State(S')</pre>
<item>
Co-located callback code for each state,
for all
- <seealso marker="#Event Types"><em>Event Types</em></seealso>
+ <seealso marker="#Event Types and Event Content">
+ <em>Event Types</em>
+ </seealso>
(such as <em>call</em>, <em>cast</em> and <em>info</em>)
</item>
<item>
@@ -136,7 +138,7 @@ State(S) x Event(E) -> Actions(A), State(S')</pre>
(<seealso marker="#State Time-Outs">State Time-Outs</seealso>,
<seealso marker="#Event Time-Outs">Event Time-Outs</seealso>
and
- <seealso marker="#Generic Time-Outs">Generic Time-outs</seealso>
+ <seealso marker="#Generic Time-Outs">Generic Time-Outs</seealso>
(named time-outs))
</item>
</list>
@@ -353,9 +355,10 @@ State(S) x Event(E) -> Actions(A), State(S')</pre>
</taglist>
<p>
The state is either the name of the function itself or an argument to it.
- The other arguments are the <c>EventType</c> described in section
- <seealso marker="#Event Types">Event Types</seealso>,
- the event dependent <c>EventContent</c>,
+ The other arguments are the <c>EventType</c>
+ and the event dependent <c>EventContent</c>,
+ both described in section
+ <seealso marker="#Event Types and Event Content">Event Types and Event Content</seealso>,
and the current server <c>Data</c>.
</p>
<p>
@@ -561,34 +564,48 @@ State(S) x Event(E) -> Actions(A), State(S')</pre>
</item>
<tag>
<seealso marker="stdlib:gen_statem#type-state_timeout">
- <c>{state_timeout, EventContent, Time}</c>
+ <c>{state_timeout, Time, EventContent}</c>
+ </seealso>
+ <br />
+ <c>{state_timeout, Time, EventContent, Opts}</c><br />
+ <seealso marker="stdlib:gen_statem#type-timeout_update_action">
+ <c>{state_timeout, update, EventContent}</c>
</seealso>
<br />
- <c>{state_timeout, EventContent, Time, Opts}</c>
+ <seealso marker="stdlib:gen_statem#type-timeout_cancel_action">
+ <c>{state_timeout, cancel}</c>
+ </seealso>
</tag>
<item>
- Start a state time-out, read more in sections
+ Start, update or cancel a state time-out, read more in sections
<seealso marker="#Time-Outs">Time-Outs</seealso> and
<seealso marker="#State Time-Outs">State Time-Outs</seealso>.
</item>
<tag>
<seealso marker="stdlib:gen_statem#type-generic_timeout">
- <c>{{timeout, Name}, EventContent, Time}</c>
+ <c>{{timeout, Name}, Time, EventContent}</c>
+ </seealso>
+ <br />
+ <c>{{timeout, Name}, Time, EventContent, Opts}</c><br />
+ <seealso marker="stdlib:gen_statem#type-timeout_update_action">
+ <c>{{timeout, Name}, update, EventContent}</c>
</seealso>
<br />
- <c>{{timeout, Name}, EventContent, Time, Opts}</c>
+ <seealso marker="stdlib:gen_statem#type-timeout_cancel_action">
+ <c>{{timeout, Name}, cancel}</c>
+ </seealso>
</tag>
<item>
- Start a generic time-out, read more in sections
+ Start, update or cancel a generic time-out, read more in sections
<seealso marker="#Time-Outs">Time-Outs</seealso> and
<seealso marker="#Generic Time-Outs">Generic Time-Outs</seealso>.
</item>
<tag>
<seealso marker="stdlib:gen_statem#type-event_timeout">
- <c>{timeout, EventContent, Time}</c>
+ <c>{timeout, Time, EventContent}</c>
</seealso>
<br />
- <c>{timeout, EventContent, Time, Opts}</c><br />
+ <c>{timeout, Time, EventContent, Opts}</c><br />
<c>Time</c>
</tag>
<item>
@@ -624,19 +641,42 @@ State(S) x Event(E) -> Actions(A), State(S')</pre>
and set a time-out to use absolute instead of relative time
(using the <c>Opts</c> field).
</p>
+ <p>
+ Among these <em>transition actions</em> only to reply to a caller
+ is an immediate action. The others are collected and handled
+ later during the <em>state transition</em>.
+ <seealso marker="#Inserted Events">Inserted Events</seealso>
+ are stored and inserted all together,
+ and the rest set transition options
+ where the last of a specific type override the previous.
+ See the description of a <em>state transition</em>
+ in the <c>gen_statem(3)</c> manual page for type
+ <seealso marker="stdlib:gen_statem#type-transition_option"><c>transition_option()</c></seealso>.
+ </p>
+ <p>
+ The different
+ <seealso marker="#Time-Outs">Time-Outs</seealso> and
+ <seealso marker="#Inserted Events"><c>next_event</c></seealso>
+ actions generate new events with corresponding
+ <seealso marker="#Event Types and Event Content">
+ Event Types and Event Content
+ </seealso>.
+ </p>
</section>
<!-- =================================================================== -->
<section>
- <marker id="Event Types" />
- <title>Event Types</title>
+ <marker id="Event Types and Event Content" />
+ <title>Event Types and Event Content</title>
<p>
Events are categorized in different
<seealso marker="stdlib:gen_statem#type-event_type"><em>event types</em></seealso>.
Events of all types are for a given state
handled in the same callback function, and that function gets
<c>EventType</c> and <c>EventContent</c> as arguments.
+ The meaning of the <c>EventContent</c>
+ depends on the <c>EventType</c>.
</p>
<p>
The following is a complete list of <em>event types</em> and where
@@ -650,7 +690,10 @@ State(S) x Event(E) -> Actions(A), State(S')</pre>
</tag>
<item>
Generated by
- <seealso marker="stdlib:gen_statem#cast/2"><c>gen_statem:cast</c></seealso>.
+ <seealso marker="stdlib:gen_statem#cast/2">
+ <c>gen_statem:cast(ServerRef, Msg)</c>
+ </seealso>
+ where <c>Msg</c> becomes the <c>EventContent</c>.
</item>
<tag>
<seealso marker="stdlib:gen_statem#type-external_event_type">
@@ -659,11 +702,17 @@ State(S) x Event(E) -> Actions(A), State(S')</pre>
</tag>
<item>
Generated by
- <seealso marker="stdlib:gen_statem#call/2"><c>gen_statem:call</c></seealso>,
- where <c>From</c> is the reply address to use
+ <seealso marker="stdlib:gen_statem#call/2">
+ <c>gen_statem:call(ServerRef, Request)</c>
+ </seealso>
+ where <c>Request</c> becomes the <c>EventContent</c>.
+ <c>From</c> is the reply address to use
when replying either through the <em>transition action</em>
- <c>{reply,From,Msg}</c> or by calling
- <seealso marker="stdlib:gen_statem#reply/1"><c>gen_statem:reply</c></seealso>.
+ <c>{reply,From,Reply}</c> or by calling
+ <seealso marker="stdlib:gen_statem#reply/1">
+ <c>gen_statem:reply(From, Reply)</c>
+ </seealso>
+ from the <em>callback module</em>.
</item>
<tag>
<seealso marker="stdlib:gen_statem#type-external_event_type">
@@ -673,6 +722,7 @@ State(S) x Event(E) -> Actions(A), State(S')</pre>
<item>
Generated by any regular process message sent to
the <c>gen_statem</c> process.
+ The process message becomes the <c>EventContent</c>.
</item>
<tag>
<seealso marker="stdlib:gen_statem#type-timeout_event_type">
@@ -724,7 +774,7 @@ State(S) x Event(E) -> Actions(A), State(S')</pre>
</tag>
<item>
Generated by <em>transition action</em>
- <seealso marker="stdlib:gen_statem#type-enter_action"><c>{next_event,internal,EventContent}</c></seealso>.
+ <seealso marker="stdlib:gen_statem#type-action"><c>{next_event,internal,EventContent}</c></seealso>.
All <em>event types</em> above can also be generated using
the <c>next_event</c> action:
<c>{next_event,EventType,EventContent}</c>.
@@ -793,7 +843,7 @@ StateName(EventType, EventContent, Data) ->
<section>
<marker id="Time-Outs" />
- <title>Time-outs</title>
+ <title>Time-Outs</title>
<p>
Time-outs in <c>gen_statem</c> are started from a
<seealso marker="#Transition Actions">
@@ -844,9 +894,9 @@ StateName(EventType, EventContent, Data) ->
</item>
</taglist>
<p>
- When a time-out is started any running time-out with the same tag,
+ When a time-out is started any running time-out of the same type;
<c>state_timeout</c>, <c>{timeout, Name}</c> or <c>timeout</c>,
- is cancelled, that is the time-out is restarted with the new time.
+ is cancelled, that is, the time-out is restarted with the new time.
</p>
<p>
All time-outs has got an <c>EventContent</c> that is part of the
@@ -869,6 +919,39 @@ StateName(EventType, EventContent, Data) ->
The <c>EventContent</c> will in this case be ignored,
so why not set it to <c>undefined</c>.
</p>
+ <p>
+ A more explicit way to cancel a timer is to use a
+ <seealso marker="#Transition Actions">
+ <em>transition action</em>
+ </seealso>
+ on the form
+ <seealso marker="stdlib:gen_statem#type-timeout_cancel_action">
+ <c>{TimeoutType, cancel}</c>
+ </seealso>
+ which is a feature introduced in OTP 22.1.
+ </p>
+ </section>
+ <section>
+ <marker id="Updating a Time-Out" />
+ <title>Updating a Time-Out</title>
+ <p>
+ While a time-out is running, its <c>EventContent</c>
+ can be updated using a
+ <seealso marker="#Transition Actions">
+ <em>transition action</em>
+ </seealso>
+ on the form
+ <seealso marker="stdlib:gen_statem#type-timeout_update_action">
+ <c>{TimeoutType, update, NewEventContent}</c>
+ </seealso>
+ which is a feature introduced in OTP 22.1.
+ </p>
+ <p>
+ If this feature is used while no such <c>TimeoutType</c>
+ is running then a time-out event is immediately delivered
+ as when starting a
+ <seealso marker="#Time-Out Zero">Time-Out Zero</seealso>.
+ </p>
</section>
<section>
<marker id="Time-Out Zero" />
@@ -1200,10 +1283,12 @@ open(state_timeout, lock, Data) ->
<p>
The timer for a state time-out is automatically cancelled
when the state machine does a <em>state change</em>.
- You can restart a state time-out by setting it to a new time,
- which cancels the running timer and starts a new.
- This implies that you can cancel a state time-out
- by restarting it with time <c>infinity</c>.
+ </p>
+ <p>
+ You can restart, cancel or update a state time-out.
+ See section
+ <seealso marker="#Time-Outs">Time-Outs</seealso>
+ for details.
</p>
</section>
@@ -1472,12 +1557,13 @@ locked(
<p>
An event time-out is cancelled by any other event so you either
get some other event or the time-out event. It is therefore
- not possible nor needed to cancel or restart an event time-out.
- Whatever event you act on has already cancelled
- the event time-out...
+ not possible nor needed to cancel, restart or update an event time-out.
+ Whatever event you act on has already cancelled the event time-out,
+ so there is never a running event time-out
+ while the <em>state callback</em> executes.
</p>
<p>
- Note that an event time-out does not work well with
+ Note that an event time-out does not work well
when you have for example a status call as in section
<seealso marker="#All State Events">All State Events</seealso>,
or handle unknown events, since all kinds of events
@@ -1548,6 +1634,12 @@ open(cast, {button,_}, Data) ->
a late time-out event can be handled by ignoring it
if it arrives in a state where it is known to be late.
</p>
+ <p>
+ You can restart, cancel or update a generic time-out.
+ See section
+ <seealso marker="#Time-Outs">Time-Outs</seealso>
+ for details.
+ </p>
</section>
<!-- =================================================================== -->
@@ -1858,7 +1950,7 @@ open(state_timeout, lock, Data) ->
<seealso marker="#Transition Actions">
<em>transition action</em>
</seealso>
- <c>{next_event,EventType,EventContent}</c>.
+ <seealso marker="stdlib:gen_statem#type-action"><c>{next_event,EventType,EventContent}</c></seealso>.
</p>
<p>
You can generate events of any existing