diff options
28 files changed, 470 insertions, 265 deletions
diff --git a/OTP_VERSION b/OTP_VERSION index 5f39e91446..a8f5438c0a 100644 --- a/OTP_VERSION +++ b/OTP_VERSION @@ -1 +1 @@ -21.0 +21.0.1 diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 41612f3ac1..29b1d106cd 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -6170,7 +6170,7 @@ true</pre> <p>Monitors the new process (like <seealso marker="#monitor/2"><c>monitor/2</c></seealso> does).</p> </item> - <tag><c>{priority, <anno>Level</anno></c></tag> + <tag><c>{priority, <anno>Level</anno>}</c></tag> <item> <p>Sets the priority of the new process. Equivalent to executing <seealso marker="#process_flag_priority"> diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index ed0946d6ba..67e27afc81 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -31,6 +31,21 @@ </header> <p>This document describes the changes made to the ERTS application.</p> +<section><title>Erts 10.0.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p>The keys used in <c>os:getenv</c> and <c>os:putenv</c> + are case-insensitive again on Windows.</p> + <p> + Own Id: OTP-15147 Aux Id: ERL-644 </p> + </item> + </list> + </section> + +</section> + <section><title>Erts 10.0</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 8253ec4f40..11b52526d5 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -3761,6 +3761,8 @@ dequeue_process(ErtsRunQueue *runq, int prio_q, erts_aint32_t *statep) ERTS_THR_DATA_DEPENDENCY_READ_MEMORY_BARRIER; state = erts_atomic32_read_nob(&p->state); + ASSERT(state & ERTS_PSFLG_IN_RUNQ); + if (statep) *statep = state; @@ -3768,8 +3770,7 @@ dequeue_process(ErtsRunQueue *runq, int prio_q, erts_aint32_t *statep) rqi = &runq->procs.prio_info[prio]; - if (p) - unqueue_process(runq, rpq, rqi, prio, NULL, p); + unqueue_process(runq, rpq, rqi, prio, NULL, p); return p; } @@ -4088,7 +4089,7 @@ evacuate_run_queue(ErtsRunQueue *rq, erts_runq_unlock(to_rq); smp_notify_inc_runq(to_rq); - erts_runq_lock(to_rq); + erts_runq_lock(rq); } if (rq->ports.start) { @@ -4157,22 +4158,17 @@ evacuate_run_queue(ErtsRunQueue *rq, free_proxy_proc(proc); else { erts_aint32_t clr_bits; -#ifdef DEBUG - erts_aint32_t old; -#endif clr_bits = ERTS_PSFLG_IN_RUNQ; clr_bits |= qbit << ERTS_PSFLGS_IN_PRQ_MASK_OFFSET; -#ifdef DEBUG - old = -#else - (void) -#endif - erts_atomic32_read_band_mb(&proc->state, - ~clr_bits); - ASSERT((old & clr_bits) == clr_bits); + state = erts_atomic32_read_band_mb(&proc->state, ~clr_bits); + ASSERT((state & clr_bits) == clr_bits); + if (state & ERTS_PSFLG_FREE) { + /* free and not queued by proxy */ + erts_proc_dec_refc(proc); + } } goto handle_next_proc; @@ -6208,13 +6204,14 @@ fin_dirty_enq_s_change(Process *p, /* Already enqueue by someone else... */ if (pstruct_reserved) { /* We reserved process struct for enqueue; clear it... */ -#ifdef DEBUG - erts_aint32_t old = -#else - (void) -#endif - erts_atomic32_read_band_nob(&p->state, ~ERTS_PSFLG_IN_RUNQ); - ASSERT(old & ERTS_PSFLG_IN_RUNQ); + erts_aint32_t state; + + state = erts_atomic32_read_band_nob(&p->state, ~ERTS_PSFLG_IN_RUNQ); + ASSERT(state & ERTS_PSFLG_IN_RUNQ); + + if (state & ERTS_PSFLG_FREE) { + erts_proc_dec_refc(p); + } } return 0; } @@ -6407,8 +6404,9 @@ schedule_out_process(ErtsRunQueue *c_rq, erts_aint32_t state, Process *p, == ERTS_PSFLG_ACTIVE)); n &= ~running_flgs; - if ((a & (ERTS_PSFLG_ACTIVE_SYS|ERTS_PSFLG_DIRTY_ACTIVE_SYS)) - || (a & (ERTS_PSFLG_ACTIVE|ERTS_PSFLG_SUSPENDED)) == ERTS_PSFLG_ACTIVE) { + if ((!!(a & (ERTS_PSFLG_ACTIVE_SYS|ERTS_PSFLG_DIRTY_ACTIVE_SYS)) + | ((a & (ERTS_PSFLG_ACTIVE|ERTS_PSFLG_SUSPENDED)) == ERTS_PSFLG_ACTIVE)) + & !(a & ERTS_PSFLG_FREE)) { enqueue = check_enqueue_in_prio_queue(p, &enq_prio, &n, a); } a = erts_atomic32_cmpxchg_mb(&p->state, n, e); @@ -6655,62 +6653,72 @@ erts_schedule_process(Process *p, erts_aint32_t state, ErtsProcLocks locks) schedule_process(p, state, locks); } +/* Enqueues the given sys task on the process and schedules it. The task may be + * NULL if only scheduling is desired. */ static ERTS_INLINE erts_aint32_t -active_sys_enqueue(Process *p, erts_aint32_t state, - erts_aint32_t enable_flags, int status_locked) -{ - /* - * This function may or may not be called with status locke held. - * It always returns without the status lock held! - */ - unsigned int prof_runnable_procs = erts_system_profile_flags.runnable_procs; - erts_aint32_t n, a = state, enq_prio = -1; - int slocked = status_locked; +active_sys_enqueue(Process *p, ErtsProcSysTask *sys_task, + erts_aint32_t task_prio, erts_aint32_t enable_flags, + erts_aint32_t state, erts_aint32_t *fail_state_p) +{ + int runnable_procs = erts_system_profile_flags.runnable_procs; + erts_aint32_t n, a, enq_prio, fail_state; + int already_scheduled; + int status_locked; int enqueue; /* < 0 -> use proxy */ - /* Status lock prevents out of order "runnable proc" trace msgs */ - ERTS_LC_ASSERT(slocked || !(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p))); - ERTS_LC_ASSERT(!slocked || (ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p))); - - if (!prof_runnable_procs) { - if (slocked) { - erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS); - slocked = 0; - } - } - else { - if (!slocked) { - erts_proc_lock(p, ERTS_PROC_LOCK_STATUS); - slocked = !0; - } - } + enable_flags |= ERTS_PSFLG_ACTIVE_SYS; + fail_state = *fail_state_p; + already_scheduled = 0; + status_locked = 0; + enq_prio = -1; + a = state; + ERTS_LC_ASSERT(!(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p))); + ASSERT(fail_state & (ERTS_PSFLG_EXITING | ERTS_PSFLG_FREE)); + ASSERT(!(fail_state & enable_flags)); ASSERT(!(state & ERTS_PSFLG_PROXY)); + /* When runnable_procs is enabled, we need to take the status lock to + * prevent trace messages from being sent in the wrong order. The lock must + * be held over the call to add2runq. + * + * Otherwise, we only need to take it when we're enqueuing a task and can + * safely release it before add2runq. */ + if (sys_task || runnable_procs) { + erts_proc_lock(p, ERTS_PROC_LOCK_STATUS); + status_locked = 1; + } + while (1) { erts_aint32_t e; n = e = a; - if (a & ERTS_PSFLG_FREE) - goto cleanup; /* We don't want to schedule free processes... */ + if (a & fail_state) { + *fail_state_p = a & fail_state; + goto cleanup; + } enqueue = ERTS_ENQUEUE_NOT; - n |= enable_flags; - n |= ERTS_PSFLG_ACTIVE_SYS; + n |= enable_flags; + if (!(a & (ERTS_PSFLG_RUNNING | ERTS_PSFLG_RUNNING_SYS | ERTS_PSFLG_DIRTY_RUNNING - | ERTS_PSFLG_DIRTY_RUNNING_SYS))) + | ERTS_PSFLG_DIRTY_RUNNING_SYS))) { enqueue = check_enqueue_in_prio_queue(p, &enq_prio, &n, a); + } + a = erts_atomic32_cmpxchg_mb(&p->state, n, e); - if (a == e) + if (a == e) { break; - if (a == n && enqueue == ERTS_ENQUEUE_NOT) - goto cleanup; + } + else if (a == n && enqueue == ERTS_ENQUEUE_NOT) { + already_scheduled = 1; + break; + } } - if (prof_runnable_procs) { - + if (!already_scheduled && runnable_procs) { if (!(a & (ERTS_PSFLG_ACTIVE_SYS | ERTS_PSFLG_RUNNING | ERTS_PSFLG_RUNNING_SYS @@ -6720,19 +6728,56 @@ active_sys_enqueue(Process *p, erts_aint32_t state, /* We activated a prevously inactive process */ profile_runnable_proc(p, am_active); } - - erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS); - slocked = 0; } - add2runq(enqueue, enq_prio, p, n, NULL); + if (sys_task) { + ErtsProcSysTaskQs *stqs = p->sys_task_qs; -cleanup: + if (!stqs) { + sys_task->next = sys_task->prev = sys_task; - if (slocked) - erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS); + stqs = proc_sys_task_queues_alloc(); - ERTS_LC_ASSERT(!(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p))); + stqs->qmask = 1 << task_prio; + stqs->ncount = 0; + stqs->q[PRIORITY_MAX] = NULL; + stqs->q[PRIORITY_HIGH] = NULL; + stqs->q[PRIORITY_NORMAL] = NULL; + stqs->q[PRIORITY_LOW] = NULL; + stqs->q[task_prio] = sys_task; + + p->sys_task_qs = stqs; + } + else { + if (!stqs->q[task_prio]) { + sys_task->next = sys_task->prev = sys_task; + + stqs->q[task_prio] = sys_task; + stqs->qmask |= 1 << task_prio; + } + else { + sys_task->next = stqs->q[task_prio]; + sys_task->prev = stqs->q[task_prio]->prev; + sys_task->next->prev = sys_task; + sys_task->prev->next = sys_task; + ASSERT(stqs->qmask & (1 << task_prio)); + } + } + } + + if (status_locked && !runnable_procs) { + erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS); + status_locked = 0; + } + + if (!already_scheduled) { + add2runq(enqueue, enq_prio, p, n, NULL); + } + +cleanup: + if (status_locked) { + erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS); + } return n; } @@ -6740,103 +6785,41 @@ cleanup: erts_aint32_t erts_proc_sys_schedule(Process *p, erts_aint32_t state, erts_aint32_t enable_flag) { - /* We are not allowed to call this function with status lock held... */ - return active_sys_enqueue(p, state, enable_flag, 0); + erts_aint32_t fail_state = ERTS_PSFLG_FREE; + + return active_sys_enqueue(p, NULL, 0, enable_flag, state, &fail_state); } static int schedule_process_sys_task(Process *p, erts_aint32_t prio, ErtsProcSysTask *st, erts_aint32_t *fail_state_p) { - int res; - int locked; - ErtsProcSysTaskQs *stqs, *free_stqs; erts_aint32_t fail_state, state; - fail_state = *fail_state_p; - - res = 1; /* prepare for success */ - st->next = st->prev = st; /* Prep for empty prio queue */ + /* Elevate priority if needed. */ state = erts_atomic32_read_nob(&p->state); - locked = 0; - free_stqs = NULL; - if (state & ERTS_PSFLG_SYS_TASKS) - stqs = NULL; - else { - alloc_qs: - stqs = proc_sys_task_queues_alloc(); - stqs->qmask = 1 << prio; - stqs->ncount = 0; - stqs->q[PRIORITY_MAX] = NULL; - stqs->q[PRIORITY_HIGH] = NULL; - stqs->q[PRIORITY_NORMAL] = NULL; - stqs->q[PRIORITY_LOW] = NULL; - stqs->q[prio] = st; - } - - if (!locked) { - locked = 1; - erts_proc_lock(p, ERTS_PROC_LOCK_STATUS); - - state = erts_atomic32_read_nob(&p->state); - if (state & fail_state) { - erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS); - *fail_state_p = (state & fail_state); - free_stqs = stqs; - res = 0; - goto cleanup; - } - } - - if (!p->sys_task_qs) { - if (stqs) - p->sys_task_qs = stqs; - else - goto alloc_qs; - } - else { - free_stqs = stqs; - stqs = p->sys_task_qs; - if (!stqs->q[prio]) { - stqs->q[prio] = st; - stqs->qmask |= 1 << prio; - } - else { - st->next = stqs->q[prio]; - st->prev = stqs->q[prio]->prev; - st->next->prev = st; - st->prev->next = st; - ASSERT(stqs->qmask & (1 << prio)); - } - } - if (ERTS_PSFLGS_GET_ACT_PRIO(state) > prio) { - erts_aint32_t n, a, e; - /* Need to elevate actual prio */ + erts_aint32_t n, a, e; - a = state; - do { - if (ERTS_PSFLGS_GET_ACT_PRIO(a) <= prio) { - n = a; - break; - } - n = e = a; - n &= ~ERTS_PSFLGS_ACT_PRIO_MASK; - n |= (prio << ERTS_PSFLGS_ACT_PRIO_OFFSET); - a = erts_atomic32_cmpxchg_nob(&p->state, n, e); - } while (a != e); - state = n; - } - - /* active_sys_enqueue() always return with status lock unlocked */ - (void) active_sys_enqueue(p, state, ERTS_PSFLG_SYS_TASKS, locked); + a = state; + do { + if (ERTS_PSFLGS_GET_ACT_PRIO(a) <= prio) { + n = a; + break; + } + n = e = a; + n &= ~ERTS_PSFLGS_ACT_PRIO_MASK; + n |= (prio << ERTS_PSFLGS_ACT_PRIO_OFFSET); + a = erts_atomic32_cmpxchg_nob(&p->state, n, e); + } while (a != e); -cleanup: + state = n; + } - if (free_stqs) - proc_sys_task_queues_free(free_stqs); + fail_state = *fail_state_p; - return res; + return !(active_sys_enqueue(p, st, prio, ERTS_PSFLG_SYS_TASKS, + state, fail_state_p) & fail_state); } static ERTS_INLINE int @@ -9563,6 +9546,7 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls) } else if (state & ERTS_PSFLG_FREE) { /* free and not queued by proxy */ + ASSERT(state & ERTS_PSFLG_IN_RUNQ); erts_proc_dec_refc(p); } if (!is_normal_sched) diff --git a/erts/emulator/sys/common/erl_osenv.c b/erts/emulator/sys/common/erl_osenv.c index 9f54d1dff0..487ff87116 100644 --- a/erts/emulator/sys/common/erl_osenv.c +++ b/erts/emulator/sys/common/erl_osenv.c @@ -75,7 +75,15 @@ static int compare_env_keys(const erts_osenv_data_t a, const erts_osenv_data_t b #include "erl_rbtree.h" static int compare_env_keys(const erts_osenv_data_t a, const erts_osenv_data_t b) { - int relation = sys_memcmp(a.data, b.data, MIN(a.length, b.length)); + int relation; + +#ifdef __WIN32__ + /* Environment variables are case-insensitive on Windows. */ + relation = _wcsnicmp((const WCHAR*)a.data, (const WCHAR*)b.data, + MIN(a.length, b.length) / sizeof(WCHAR)); +#else + relation = sys_memcmp(a.data, b.data, MIN(a.length, b.length)); +#endif if(relation != 0) { return relation; diff --git a/erts/emulator/test/bif_SUITE.erl b/erts/emulator/test/bif_SUITE.erl index 32bfcd5520..9e7bcd5255 100644 --- a/erts/emulator/test/bif_SUITE.erl +++ b/erts/emulator/test/bif_SUITE.erl @@ -36,7 +36,8 @@ error_stacktrace_during_call_trace/1, group_leader_prio/1, group_leader_prio_dirty/1, is_process_alive/1, - process_info_blast/1]). + process_info_blast/1, + os_env_case_sensitivity/1]). suite() -> [{ct_hooks,[ts_install_cth]}, @@ -51,7 +52,7 @@ all() -> erl_crash_dump_bytes, min_max, erlang_halt, is_builtin, error_stacktrace, error_stacktrace_during_call_trace, group_leader_prio, group_leader_prio_dirty, - is_process_alive, process_info_blast]. + is_process_alive, process_info_blast, os_env_case_sensitivity]. %% Uses erlang:display to test that erts_printf does not do deep recursion display(Config) when is_list(Config) -> @@ -443,6 +444,17 @@ os_env_long(Min, Max, Value) -> true = os:unsetenv(EnvVar), os_env_long(Min+1, Max, Value). +os_env_case_sensitivity(Config) when is_list(Config) -> + %% The keys in os:getenv/putenv must be case-insensitive on Windows, and + %% case-sensitive elsewhere. + true = os:putenv("os_env_gurka", "gaffel"), + Expected = case os:type() of + {win32, _} -> "gaffel"; + _ -> false + end, + Expected = os:getenv("OS_ENV_GURKA"), + ok. + %% Test that string:to_integer does not Halloc in wrong order. otp_7526(Config) when is_list(Config) -> ok = test_7526(256). diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl index 585c5a1871..57eb082d64 100644 --- a/erts/emulator/test/process_SUITE.erl +++ b/erts/emulator/test/process_SUITE.erl @@ -59,6 +59,7 @@ no_priority_inversion2/1, system_task_blast/1, system_task_on_suspended/1, + system_task_failed_enqueue/1, gc_request_when_gc_disabled/1, gc_request_blast_when_gc_disabled/1]). -export([prio_server/2, prio_client/2, init/1, handle_event/2]). @@ -106,7 +107,7 @@ groups() -> otp_7738_resume]}, {system_task, [], [no_priority_inversion, no_priority_inversion2, - system_task_blast, system_task_on_suspended, + system_task_blast, system_task_on_suspended, system_task_failed_enqueue, gc_request_when_gc_disabled, gc_request_blast_when_gc_disabled]}]. init_per_suite(Config) -> @@ -2625,6 +2626,57 @@ system_task_on_suspended(Config) when is_list(Config) -> ok end. +%% When a system task couldn't be enqueued due to the process being in an +%% incompatible state, it would linger in the system task list and get executed +%% anyway the next time the process was scheduled. This would result in a +%% double-free at best. +%% +%% This test continuously purges modules while other processes run dirty code, +%% which will provoke this error as ERTS_PSTT_CPC can't be enqueued while a +%% process is running dirty code. +system_task_failed_enqueue(Config) when is_list(Config) -> + case erlang:system_info(dirty_cpu_schedulers) of + N when N > 0 -> + system_task_failed_enqueue_1(Config); + _ -> + {skipped, "No dirty scheduler support"} + end. + +system_task_failed_enqueue_1(Config) -> + Priv = proplists:get_value(priv_dir, Config), + + Purgers = [spawn_link(fun() -> purge_loop(Priv, Id) end) + || Id <- lists:seq(1, erlang:system_info(schedulers))], + Hogs = [spawn_link(fun() -> dirty_loop() end) + || _ <- lists:seq(1, erlang:system_info(dirty_cpu_schedulers))], + + ct:sleep(5000), + + [begin + unlink(Pid), + exit(Pid, kill) + end || Pid <- (Purgers ++ Hogs)], + + ok. + +purge_loop(PrivDir, Id) -> + Mod = "failed_enq_" ++ integer_to_list(Id), + Path = PrivDir ++ "/" ++ Mod, + file:write_file(Path ++ ".erl", + "-module('" ++ Mod ++ "').\n" ++ + "-export([t/0]).\n" ++ + "t() -> ok."), + purge_loop_1(Path). +purge_loop_1(Path) -> + {ok, Mod} = compile:file(Path, []), + erlang:delete_module(Mod), + erts_code_purger:purge(Mod), + purge_loop_1(Path). + +dirty_loop() -> + ok = erts_debug:dirty_cpu(reschedule, 10000), + dirty_loop(). + gc_request_when_gc_disabled(Config) when is_list(Config) -> AIS = erts_debug:set_internal_state(available_internal_state, true), gc_request_when_gc_disabled_do(ref), diff --git a/erts/etc/unix/cerl.src b/erts/etc/unix/cerl.src index 3572fdd954..896e4c8e45 100644 --- a/erts/etc/unix/cerl.src +++ b/erts/etc/unix/cerl.src @@ -69,15 +69,6 @@ cxargs_add() { done } -eeargs= -eeargs_add() { - while [ $# -gt 0 ]; do - cargs="$cargs $1" - eeargs="$eeargs $1" - shift - done -} - core= GDB= @@ -97,8 +88,6 @@ TARGET=%TARGET% PROGNAME=$ROOTDIR/bin/cerl EMU=beam -PRELOADED=$ROOTDIR/erts/preloaded/ebin - while [ $# -gt 0 ]; do case "$1" in @@ -255,7 +244,7 @@ EXEC=$BINDIR/erlexec PROGNAME="$PROGNAME$cargs" EMU="$EMU$TYPE" -EMU_NAME=`$EXEC -emu_name_exit $eeargs` +EMU_NAME=`$EXEC -emu_name_exit` if [ $skip_erlexec = yes ]; then emu_xargs=`echo $xargs | sed "s|+|-|g"` @@ -269,8 +258,6 @@ if [ $skip_erlexec = yes ]; then ' set -- $beam_args IFS="$SAVE_IFS" -else - xargs="$xargs -pz $PRELOADED --" fi if [ "x$GDB" = "x" ]; then if [ $run_valgrind = yes ]; then @@ -316,12 +303,12 @@ if [ "x$GDB" = "x" ]; then sched_arg= fi - exec $taskset1 valgrind $valgrind_xml $valgrind_log $valgrind_misc_flags $BINDIR/$EMU_NAME $sched_arg $emu_xargs "$@" -pz $PRELOADED + exec $taskset1 valgrind $valgrind_xml $valgrind_log $valgrind_misc_flags $BINDIR/$EMU_NAME $sched_arg $emu_xargs "$@" elif [ $run_rr = yes ]; then - exec rr record --ignore-nested $BINDIR/$EMU_NAME $emu_xargs "$@" -pz $PRELOADED + exec rr record --ignore-nested $BINDIR/$EMU_NAME $emu_xargs "$@" else - exec $EXEC $eeargs $xargs ${1+"$@"} + exec $EXEC $xargs ${1+"$@"} fi elif [ "x$GDB" = "xgdb" ]; then case "x$core" in diff --git a/erts/vsn.mk b/erts/vsn.mk index 519556119d..e60405215a 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -18,7 +18,7 @@ # %CopyrightEnd% # -VSN = 10.0 +VSN = 10.0.1 # Port number 4365 in 4.2 # Port number 4366 in 4.3 diff --git a/lib/compiler/doc/src/notes.xml b/lib/compiler/doc/src/notes.xml index 997a506181..fcfa7d38a7 100644 --- a/lib/compiler/doc/src/notes.xml +++ b/lib/compiler/doc/src/notes.xml @@ -32,6 +32,21 @@ <p>This document describes the changes made to the Compiler application.</p> +<section><title>Compiler 7.2.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p>The could could crash when compiling a complicated + function that used the binary syntax.</p> + <p> + Own Id: OTP-15150 Aux Id: ERL-650 </p> + </item> + </list> + </section> + +</section> + <section><title>Compiler 7.2</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/compiler/src/beam_utils.erl b/lib/compiler/src/beam_utils.erl index 5510624b2d..ff587c4982 100644 --- a/lib/compiler/src/beam_utils.erl +++ b/lib/compiler/src/beam_utils.erl @@ -1105,8 +1105,12 @@ defs([{bif,_,{f,Fail},_Src,Dst}=I|Is], Regs0, D) -> defs([{block,Block0}|Is], Regs0, D0) -> {Block,Regs,D} = defs_list(Block0, Regs0, D0), [{block,[make_anno({def,Regs0})|Block]}|defs(Is, Regs, D)]; -defs([{bs_init,{f,L},_,_,_,Dst}=I|Is], Regs0, D) -> - Regs = def_regs([Dst], Regs0), +defs([{bs_init,{f,L},_,Live,_,Dst}=I|Is], Regs0, D) -> + Regs1 = case Live of + none -> Regs0; + _ -> init_def_regs(Live) + end, + Regs = def_regs([Dst], Regs1), [I|defs(Is, Regs, update_regs(L, Regs, D))]; defs([{bs_put,{f,L},_,_}=I|Is], Regs, D) -> [I|defs(Is, Regs, update_regs(L, Regs, D))]; diff --git a/lib/compiler/test/beam_utils_SUITE.erl b/lib/compiler/test/beam_utils_SUITE.erl index 3d35b546fc..360dcc1e84 100644 --- a/lib/compiler/test/beam_utils_SUITE.erl +++ b/lib/compiler/test/beam_utils_SUITE.erl @@ -132,6 +132,15 @@ bs_init(_Config) -> <<"foo/foo">> = do_bs_init_4(<<"foo">>, true), error = do_bs_init_4([], not_boolean), + Id = 17575, + Domain = -8798798, + [<<10,1:16,Id:16/signed>>,<<8,2:16,Domain:32/signed>>] = + do_bs_init_5(#{tag=>value,id=>Id,domain=>Domain}), + {'EXIT',{{required,id},[_|_]}} = + (catch do_bs_init_5(#{tag=>value,id=>nil,domain=>Domain})), + {'EXIT',{{required,domain},[_|_]}} = + (catch do_bs_init_5(#{tag=>value,id=>Id,domain=>nil})), + ok. do_bs_init_1([?MODULE], Sz) -> @@ -189,6 +198,20 @@ do_bs_init_4(Arg1, Arg2) -> error end. +do_bs_init_5(#{tag := value, id := Id, domain := Domain}) -> + [case Id of + nil -> + error(id({required, id})); + _ -> + <<10, 1:16/signed, Id:16/signed>> + end, + case Domain of + nil -> + error(id({required, domain})); + _ -> + <<8, 2:16/signed, Domain:32/signed>> + end]. + bs_save(_Config) -> {a,30,<<>>} = do_bs_save(<<1:1,30:5>>), {b,127,<<>>} = do_bs_save(<<1:1,31:5,0:1,127:7>>), diff --git a/lib/compiler/vsn.mk b/lib/compiler/vsn.mk index a26cb09dff..261c908513 100644 --- a/lib/compiler/vsn.mk +++ b/lib/compiler/vsn.mk @@ -1 +1 @@ -COMPILER_VSN = 7.2 +COMPILER_VSN = 7.2.1 diff --git a/lib/ftp/doc/src/ftp.xml b/lib/ftp/doc/src/ftp.xml index 3dbd3ed403..23a9e3fec7 100644 --- a/lib/ftp/doc/src/ftp.xml +++ b/lib/ftp/doc/src/ftp.xml @@ -546,11 +546,12 @@ <v>start_option() = {verbose, verbose()} | {debug, debug()}</v> <v>verbose() = boolean() (default is false)</v> <v>debug() = disable | debug | trace (default is disable)</v> - <v>open_option() = {ipfamily, ipfamily()} | {port, port()} | {mode, mode()} | {tls, tls_options()} | {timeout, timeout()} | {dtimeout, dtimeout()} | {progress, progress()}</v> + <v>open_option() = {ipfamily, ipfamily()} | {port, port()} | {mode, mode()} | {tls, tls_options()} | {timeout, timeout()} | {dtimeout, dtimeout()} | {progress, progress() | {sock_ctrl, sock_opts()} | {sock_data_act, sock_opts()} | {sock_data_pass, sock_opts()} }</v> <v>ipfamily() = inet | inet6 | inet6fb4 (default is inet)</v> <v>port() = integer() > 0 (default is 21)</v> <v>mode() = active | passive (default is passive)</v> <v>tls_options() = [<seealso marker="ssl:ssl#type-ssloption">ssl:ssloption()</seealso>]</v> + <v>sock_opts() = [<seealso marker="kernel:gen_tcp#type-option">gen_tcp:option()</seealso> except for ipv6_v6only, active, packet, mode, packet_size and header</v> <v>timeout() = integer() > 0 (default is 60000 milliseconds)</v> <v>dtimeout() = integer() > 0 | infinity (default is infinity)</v> <v>pogress() = ignore | {module(), function(), initial_data()} (default is ignore)</v> @@ -573,6 +574,11 @@ is used for securing both the control connection and the data sessions. </p> + <p>The options <c>sock_ctrl</c>, <c>sock_data_act</c> and <c>sock_data_pass</c> passes options down to + the underlying transport layer (tcp). The default value for <c>sock_ctrl</c> is <c>[]</c>. Both + <c>sock_data_act</c> and <c>sock_data_pass</c> uses the value of <c>sock_ctrl</c> as default value. + </p> + <p>A session opened in this way is closed using function <seealso marker="#close">close</seealso>.</p> diff --git a/lib/ftp/src/ftp.erl b/lib/ftp/src/ftp.erl index 8790bfec13..40f6b53fa3 100644 --- a/lib/ftp/src/ftp.erl +++ b/lib/ftp/src/ftp.erl @@ -101,6 +101,9 @@ %% data needed further on. caller = undefined, % term() ipfamily, % inet | inet6 | inet6fb4 + sockopts_ctrl = [], + sockopts_data_passive = [], + sockopts_data_active = [], progress = ignore, % ignore | pid() dtimeout = ?DATA_ACCEPT_TIMEOUT, % non_neg_integer() | infinity tls_upgrading_data_connection = false, @@ -135,9 +138,10 @@ start_standalone(Options) -> try {ok, StartOptions} = start_options(Options), {ok, OpenOptions} = open_options(Options), + {ok, SocketOptions} = socket_options(Options), case start_link(StartOptions, []) of {ok, Pid} -> - call(Pid, {open, ip_comm, OpenOptions}, plain); + call(Pid, {open, ip_comm, OpenOptions, SocketOptions}, plain); Error1 -> Error1 end @@ -149,10 +153,11 @@ start_standalone(Options) -> start_service(Options) -> try {ok, StartOptions} = start_options(Options), - {ok, OpenOptions} = open_options(Options), + {ok, OpenOptions} = open_options(Options), + {ok, SocketOptions} = socket_options(Options), case ftp_sup:start_child([[[{client, self()} | StartOptions], []]]) of {ok, Pid} -> - call(Pid, {open, ip_comm, OpenOptions}, plain); + call(Pid, {open, ip_comm, OpenOptions, SocketOptions}, plain); Error1 -> Error1 end @@ -200,9 +205,10 @@ open({option_list, Options}) when is_list(Options) -> try {ok, StartOptions} = start_options(Options), {ok, OpenOptions} = open_options(Options), + {ok, SockOpts} = socket_options(Options), case ftp_sup:start_child([[[{client, self()} | StartOptions], []]]) of {ok, Pid} -> - call(Pid, {open, ip_comm, OpenOptions}, plain); + call(Pid, {open, ip_comm, OpenOptions, SockOpts}, plain); Error1 -> Error1 end @@ -227,9 +233,10 @@ open(Host, Opts) when is_list(Opts) -> try {ok, StartOptions} = start_options(Opts), {ok, OpenOptions} = open_options([{host, Host}|Opts]), + {ok, SocketOptions} = socket_options(Opts), case start_link(StartOptions, []) of {ok, Pid} -> - do_open(Pid, OpenOptions, tls_options(Opts)); + do_open(Pid, OpenOptions, SocketOptions, tls_options(Opts)); Error1 -> Error1 end @@ -238,8 +245,8 @@ open(Host, Opts) when is_list(Opts) -> Error2 end. -do_open(Pid, OpenOptions, TLSOpts) -> - case call(Pid, {open, ip_comm, OpenOptions}, plain) of +do_open(Pid, OpenOptions, SocketOptions, TLSOpts) -> + case call(Pid, {open, ip_comm, OpenOptions, SocketOptions}, plain) of {ok, Pid} -> maybe_tls_upgrade(Pid, TLSOpts); Error -> @@ -1026,7 +1033,7 @@ handle_call({_,latest_ctrl_response}, _, #state{latest_ctrl_response=Resp} = Sta handle_call({Pid, _}, _, #state{owner = Owner} = State) when Owner =/= Pid -> {reply, {error, not_connection_owner}, State}; -handle_call({_, {open, ip_comm, Opts}}, From, State) -> +handle_call({_, {open, ip_comm, Opts, {CtrlOpts, DataPassOpts, DataActOpts}}}, From, State) -> case key_search(host, Opts, undefined) of undefined -> {stop, normal, {error, ehost}, State}; @@ -1043,6 +1050,9 @@ handle_call({_, {open, ip_comm, Opts}}, From, State) -> mode = Mode, progress = progress(Progress), ipfamily = IpFamily, + sockopts_ctrl = CtrlOpts, + sockopts_data_passive = DataPassOpts, + sockopts_data_active = DataActOpts, dtimeout = DTimeout, ftp_extension = FtpExt}, @@ -1055,28 +1065,6 @@ handle_call({_, {open, ip_comm, Opts}}, From, State) -> end end; -handle_call({_, {open, ip_comm, Host, Opts}}, From, State) -> - Mode = key_search(mode, Opts, ?DEFAULT_MODE), - Port = key_search(port, Opts, ?FTP_PORT), - Timeout = key_search(timeout, Opts, ?CONNECTION_TIMEOUT), - DTimeout = key_search(dtimeout, Opts, ?DATA_ACCEPT_TIMEOUT), - Progress = key_search(progress, Opts, ignore), - FtpExt = key_search(ftp_extension, Opts, ?FTP_EXT_DEFAULT), - - State2 = State#state{client = From, - mode = Mode, - progress = progress(Progress), - dtimeout = DTimeout, - ftp_extension = FtpExt}, - - case setup_ctrl_connection(Host, Port, Timeout, State2) of - {ok, State3, WaitTimeout} -> - {noreply, State3, WaitTimeout}; - {error, _Reason} -> - gen_server:reply(From, {error, ehost}), - {stop, normal, State2#state{client = undefined}} - end; - handle_call({_, {open, tls_upgrade, TLSOptions}}, From, State) -> _ = send_ctrl_message(State, mk_cmd("AUTH TLS", [])), activate_ctrl_connection(State), @@ -1659,11 +1647,12 @@ handle_ctrl_result({pos_compl, Lines}, client = From, caller = {setup_data_connection, Caller}, csock = CSock, + sockopts_data_passive = SockOpts, timeout = Timeout} = State) -> [_, PortStr | _] = lists:reverse(string:tokens(Lines, "|")), {ok, {IP, _}} = peername(CSock), - case connect(IP, list_to_integer(PortStr), Timeout, State) of + case connect(IP, list_to_integer(PortStr), SockOpts, Timeout, State) of {ok, _, Socket} -> handle_caller(State#state{caller = Caller, dsock = {tcp, Socket}}); {error, _Reason} = Error -> @@ -1676,7 +1665,8 @@ handle_ctrl_result({pos_compl, Lines}, ipfamily = inet, client = From, caller = {setup_data_connection, Caller}, - timeout = Timeout, + timeout = Timeout, + sockopts_data_passive = SockOpts, ftp_extension = false} = State) -> {_, [?LEFT_PAREN | Rest]} = @@ -1690,7 +1680,7 @@ handle_ctrl_result({pos_compl, Lines}, Port = (P1 * 256) + P2, ?DBG('<--data tcp connect to ~p:~p, Caller=~p~n',[IP,Port,Caller]), - case connect(IP, Port, Timeout, State) of + case connect(IP, Port, SockOpts, Timeout, State) of {ok, _, Socket} -> handle_caller(State#state{caller = Caller, dsock = {tcp,Socket}}); {error, _Reason} = Error -> @@ -1705,13 +1695,14 @@ handle_ctrl_result({pos_compl, Lines}, caller = {setup_data_connection, Caller}, csock = CSock, timeout = Timeout, + sockopts_data_passive = SockOpts, ftp_extension = true} = State) -> [_, PortStr | _] = lists:reverse(string:tokens(Lines, "|")), {ok, {IP, _}} = peername(CSock), ?DBG('<--data tcp connect to ~p:~p, Caller=~p~n',[IP,PortStr,Caller]), - case connect(IP, list_to_integer(PortStr), Timeout, State) of + case connect(IP, list_to_integer(PortStr), SockOpts, Timeout, State) of {ok, _, Socket} -> handle_caller(State#state{caller = Caller, dsock = {tcp, Socket}}); {error, _Reason} = Error -> @@ -2075,9 +2066,9 @@ handle_caller(#state{caller = {transfer_data, {Cmd, Bin, RemoteFile}}} = %% Connect to FTP server at Host (default is TCP port 21) %% in order to establish a control connection. -setup_ctrl_connection(Host, Port, Timeout, State) -> +setup_ctrl_connection(Host, Port, Timeout, #state{sockopts_ctrl = SockOpts} = State) -> MsTime = erlang:monotonic_time(), - case connect(Host, Port, Timeout, State) of + case connect(Host, Port, SockOpts, Timeout, State) of {ok, IpFam, CSock} -> NewState = State#state{csock = {tcp, CSock}, ipfamily = IpFam}, activate_ctrl_connection(NewState), @@ -2095,12 +2086,15 @@ setup_ctrl_connection(Host, Port, Timeout, State) -> setup_data_connection(#state{mode = active, caller = Caller, csock = CSock, + sockopts_data_active = SockOpts, ftp_extension = FtpExt} = State) -> case (catch sockname(CSock)) of - {ok, {{_, _, _, _, _, _, _, _} = IP, _}} -> + {ok, {{_, _, _, _, _, _, _, _} = IP0, _}} -> + IP = proplists:get_value(ip, SockOpts, IP0), {ok, LSock} = gen_tcp:listen(0, [{ip, IP}, {active, false}, - inet6, binary, {packet, 0}]), + inet6, binary, {packet, 0} | + lists:keydelete(ip,1,SockOpts)]), {ok, {_, Port}} = sockname({tcp,LSock}), IpAddress = inet_parse:ntoa(IP), Cmd = mk_cmd("EPRT |2|~s|~p|", [IpAddress, Port]), @@ -2108,9 +2102,11 @@ setup_data_connection(#state{mode = active, activate_ctrl_connection(State), {noreply, State#state{caller = {setup_data_connection, {LSock, Caller}}}}; - {ok, {{_,_,_,_} = IP, _}} -> + {ok, {{_,_,_,_} = IP0, _}} -> + IP = proplists:get_value(ip, SockOpts, IP0), {ok, LSock} = gen_tcp:listen(0, [{ip, IP}, {active, false}, - binary, {packet, 0}]), + binary, {packet, 0} | + lists:keydelete(ip,1,SockOpts)]), {ok, Port} = inet:port(LSock), _ = case FtpExt of false -> @@ -2149,41 +2145,41 @@ setup_data_connection(#state{mode = passive, ipfamily = inet, activate_ctrl_connection(State), {noreply, State#state{caller = {setup_data_connection, Caller}}}. -connect(Host, Port, Timeout, #state{ipfamily = inet = IpFam}) -> - connect2(Host, Port, IpFam, Timeout); +connect(Host, Port, SockOpts, Timeout, #state{ipfamily = inet = IpFam}) -> + connect2(Host, Port, IpFam, SockOpts, Timeout); -connect(Host, Port, Timeout, #state{ipfamily = inet6 = IpFam}) -> - connect2(Host, Port, IpFam, Timeout); +connect(Host, Port, SockOpts, Timeout, #state{ipfamily = inet6 = IpFam}) -> + connect2(Host, Port, IpFam, SockOpts, Timeout); -connect(Host, Port, Timeout, #state{ipfamily = inet6fb4}) -> +connect(Host, Port, SockOpts, Timeout, #state{ipfamily = inet6fb4}) -> case inet:getaddr(Host, inet6) of {ok, {0, 0, 0, 0, 0, 16#ffff, _, _} = IPv6} -> case inet:getaddr(Host, inet) of {ok, IPv4} -> IpFam = inet, - connect2(IPv4, Port, IpFam, Timeout); + connect2(IPv4, Port, IpFam, SockOpts, Timeout); _ -> IpFam = inet6, - connect2(IPv6, Port, IpFam, Timeout) + connect2(IPv6, Port, IpFam, SockOpts, Timeout) end; {ok, IPv6} -> IpFam = inet6, - connect2(IPv6, Port, IpFam, Timeout); + connect2(IPv6, Port, IpFam, SockOpts, Timeout); _ -> case inet:getaddr(Host, inet) of {ok, IPv4} -> IpFam = inet, - connect2(IPv4, Port, IpFam, Timeout); + connect2(IPv4, Port, IpFam, SockOpts, Timeout); Error -> Error end end. -connect2(Host, Port, IpFam, Timeout) -> - Opts = [IpFam, binary, {packet, 0}, {active, false}], +connect2(Host, Port, IpFam, SockOpts, Timeout) -> + Opts = [IpFam, binary, {packet, 0}, {active, false} | SockOpts], case gen_tcp:connect(Host, Port, Opts, Timeout) of {ok, Sock} -> {ok, IpFam, Sock}; @@ -2553,6 +2549,32 @@ open_options(Options) -> {ftp_extension, ValidateFtpExtension, false, ?FTP_EXT_DEFAULT}], validate_options(Options, ValidOptions, []). +socket_options(Options) -> + CtrlOpts = proplists:get_value(sock_ctrl, Options, []), + DataActOpts = proplists:get_value(sock_data_act, Options, CtrlOpts), + DataPassOpts = proplists:get_value(sock_data_pass, Options, CtrlOpts), + case [O || O <- lists:usort(CtrlOpts++DataPassOpts++DataActOpts), + not valid_socket_option(O)] of + [] -> + {ok, {CtrlOpts, DataPassOpts, DataActOpts}}; + Invalid -> + throw({error,{sock_opts,Invalid}}) + end. + + +valid_socket_option(inet ) -> false; +valid_socket_option(inet6 ) -> false; +valid_socket_option({ipv6_v6only, _}) -> false; +valid_socket_option({active,_} ) -> false; +valid_socket_option({packet,_} ) -> false; +valid_socket_option({mode,_} ) -> false; +valid_socket_option(binary ) -> false; +valid_socket_option(list ) -> false; +valid_socket_option({header,_} ) -> false; +valid_socket_option({packet_size,_} ) -> false; +valid_socket_option(_) -> true. + + tls_options(Options) -> %% Options will be validated by ssl application proplists:get_value(tls, Options, undefined). diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml index f4bf4b1e1f..d967f56576 100644 --- a/lib/inets/doc/src/notes.xml +++ b/lib/inets/doc/src/notes.xml @@ -79,7 +79,6 @@ </list> </section> - <section><title>Improvements and New Features</title> <list> <item> @@ -91,7 +90,26 @@ </list> </section> -</section> + </section> + + + <section><title>Inets 6.5.2.1</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Options added for setting low-level properties on the + underlying TCP connections. The options are: + <c>sock_ctrl</c>, <c>sock_data_act</c> and + <c>sock_data_pass</c>. See the manual for details.</p> + <p> + Own Id: OTP-15120 Aux Id: ERIERL-192 </p> + </item> + </list> + </section> + + </section> <section><title>Inets 6.5.2</title> diff --git a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml index c9aa877a7f..2478a8950b 100644 --- a/lib/ssh/doc/src/notes.xml +++ b/lib/ssh/doc/src/notes.xml @@ -31,7 +31,6 @@ </header> <section><title>Ssh 4.7</title> - <section><title>Fixed Bugs and Malfunctions</title> <list> <item> @@ -190,6 +189,33 @@ </item> </list> </section> +</section> + +<section><title>Ssh 4.6.9.1</title> + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + SFTP clients reported the error reason <c>""</c> if a + non-OTP sftp server was killed during a long file + transmission.</p> + <p> + Now the signal name (for example <c>"KILL"</c>) will be + the error reason if the server's reason is empty.</p> + <p> + The documentation also lacked type information about this + class of errors.</p> + <p> + Own Id: OTP-15148 Aux Id: ERIERL-194 </p> + </item> + <item> + <p> + Fix ssh_sftp decode error for sftp protocol version 4</p> + <p> + Own Id: OTP-15149 Aux Id: ERIERL-199 </p> + </item> + </list> + </section> </section> diff --git a/lib/ssh/doc/src/ssh_sftp.xml b/lib/ssh/doc/src/ssh_sftp.xml index 4d2337c30a..ea55126cb3 100644 --- a/lib/ssh/doc/src/ssh_sftp.xml +++ b/lib/ssh/doc/src/ssh_sftp.xml @@ -46,9 +46,9 @@ <taglist> <tag><c>reason()</c></tag> <item> - <p>= <c>atom()</c> A description of the reason why an operation failed.</p> + <p>= <c>atom() | string() | tuple() </c>A description of the reason why an operation failed.</p> <p> - The value is formed from the sftp error codes in the protocol-level responses as defined in + 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> @@ -57,6 +57,10 @@ 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> </item> <tag><c>connection_ref() =</c></tag> diff --git a/lib/ssh/src/ssh_sftp.erl b/lib/ssh/src/ssh_sftp.erl index 6e720a47b7..1b2ba5a50b 100644 --- a/lib/ssh/src/ssh_sftp.erl +++ b/lib/ssh/src/ssh_sftp.erl @@ -798,13 +798,22 @@ handle_ssh_msg({ssh_cm, _, {signal, _, _}}, State) -> %% Ignore signals according to RFC 4254 section 6.9. {ok, State}; -handle_ssh_msg({ssh_cm, _, {exit_signal, ChannelId, _, Error, _}}, +handle_ssh_msg({ssh_cm, _, {exit_signal, ChannelId, Signal, Error0, _}}, State0) -> + Error = + case Error0 of + "" -> Signal; + _ -> Error0 + end, State = reply_all(State0, {error, Error}), {stop, ChannelId, State}; handle_ssh_msg({ssh_cm, _, {exit_status, ChannelId, Status}}, State0) -> - State = reply_all(State0, {error, {exit_status, Status}}), + State = + case State0 of + 0 -> State0; + _ -> reply_all(State0, {error, {exit_status, Status}}) + end, {stop, ChannelId, State}. %%-------------------------------------------------------------------- diff --git a/lib/ssh/src/ssh_sftpd.erl b/lib/ssh/src/ssh_sftpd.erl index 7ee762dcee..278f6a9780 100644 --- a/lib/ssh/src/ssh_sftpd.erl +++ b/lib/ssh/src/ssh_sftpd.erl @@ -125,9 +125,9 @@ handle_ssh_msg({ssh_cm, _, {signal, _, _}}, State) -> %% Ignore signals according to RFC 4254 section 6.9. {ok, State}; -handle_ssh_msg({ssh_cm, _, {exit_signal, ChannelId, _, Error, _}}, State) -> - Report = io_lib:format("Connection closed by peer ~n Error ~p~n", - [Error]), +handle_ssh_msg({ssh_cm, _, {exit_signal, ChannelId, Signal, Error, _}}, State) -> + Report = io_lib:format("Connection closed by peer signal ~p~n Error ~p~n", + [Signal,Error]), error_logger:error_report(Report), {stop, ChannelId, State}; diff --git a/lib/ssh/src/ssh_xfer.erl b/lib/ssh/src/ssh_xfer.erl index e1680c120e..7bb9c2d101 100644 --- a/lib/ssh/src/ssh_xfer.erl +++ b/lib/ssh/src/ssh_xfer.erl @@ -734,7 +734,7 @@ decode_ATTR(Vsn, <<?UINT32(Flags), Tail/binary>>) -> {Type,Tail2} = if Vsn =< 3 -> {?SSH_FILEXFER_TYPE_UNKNOWN, Tail}; - Vsn >= 5 -> + true -> <<?BYTE(T), TL/binary>> = Tail, {T, TL} end, diff --git a/lib/ssh/vsn.mk b/lib/ssh/vsn.mk index 6060e50d31..c3572b5b70 100644 --- a/lib/ssh/vsn.mk +++ b/lib/ssh/vsn.mk @@ -1,5 +1,4 @@ #-*-makefile-*- ; force emacs to enter makefile-mode SSH_VSN = 4.7 - APP_VSN = "ssh-$(SSH_VSN)" diff --git a/lib/stdlib/src/beam_lib.erl b/lib/stdlib/src/beam_lib.erl index ef4c7d255c..01181b1097 100644 --- a/lib/stdlib/src/beam_lib.erl +++ b/lib/stdlib/src/beam_lib.erl @@ -690,30 +690,31 @@ chunk_to_data(debug_info=Id, Chunk, File, _Cs, AtomTable, Mod) -> <<0:8,N:8,Mode0:N/binary,Rest/binary>> -> Mode = binary_to_atom(Mode0, utf8), Term = decrypt_chunk(Mode, Mod, File, Id, Rest), - {AtomTable, {Id, Term}}; + {AtomTable, {Id, anno_from_term(Term)}}; _ -> case catch binary_to_term(Chunk) of {'EXIT', _} -> error({invalid_chunk, File, chunk_name_to_id(Id, File)}); Term -> - {AtomTable, {Id, Term}} + {AtomTable, {Id, anno_from_term(Term)}} end end; chunk_to_data(abstract_code=Id, Chunk, File, _Cs, AtomTable, Mod) -> + %% Before Erlang/OTP 20.0. case Chunk of <<>> -> {AtomTable, {Id, no_abstract_code}}; <<0:8,N:8,Mode0:N/binary,Rest/binary>> -> Mode = binary_to_atom(Mode0, utf8), Term = decrypt_chunk(Mode, Mod, File, Id, Rest), - {AtomTable, {Id, anno_from_term(Term)}}; + {AtomTable, {Id, old_anno_from_term(Term)}}; _ -> case catch binary_to_term(Chunk) of {'EXIT', _} -> error({invalid_chunk, File, chunk_name_to_id(Id, File)}); Term -> try - {AtomTable, {Id, anno_from_term(Term)}} + {AtomTable, {Id, old_anno_from_term(Term)}} catch _:_ -> error({invalid_chunk, File, @@ -947,14 +948,24 @@ decrypt_chunk(Type, Module, File, Id, Bin) -> error({key_missing_or_invalid, File, Id}) end. -anno_from_term({raw_abstract_v1, Forms}) -> +old_anno_from_term({raw_abstract_v1, Forms}) -> {raw_abstract_v1, anno_from_forms(Forms)}; -anno_from_term({Tag, Forms}) when Tag =:= abstract_v1; Tag =:= abstract_v2 -> +old_anno_from_term({Tag, Forms}) when Tag =:= abstract_v1; + Tag =:= abstract_v2 -> try {Tag, anno_from_forms(Forms)} catch _:_ -> {Tag, Forms} end; +old_anno_from_term(T) -> + T. + +anno_from_term({debug_info_v1=Tag1, erl_abstract_code=Tag2, {Forms, Opts}}) -> + try {Tag1, Tag2, {anno_from_forms(Forms), Opts}} + catch + _:_ -> + {Tag1, Tag2, {Forms, Opts}} + end; anno_from_term(T) -> T. diff --git a/lib/stdlib/src/erl_eval.erl b/lib/stdlib/src/erl_eval.erl index 31c0e60fe1..2066b2f60f 100644 --- a/lib/stdlib/src/erl_eval.erl +++ b/lib/stdlib/src/erl_eval.erl @@ -329,7 +329,8 @@ expr({'fun',Line,{clauses,Cs}} = Ex, Bs, Lf, Ef, RBs) -> 20 -> fun (A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T) -> eval_fun([A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T], Info) end; _Other -> - erlang:raise(error, {'argument_limit',{'fun',Line,Cs}}, + L = erl_anno:location(Line), + erlang:raise(error, {'argument_limit',{'fun',L,to_terms(Cs)}}, ?STACKTRACE) end, ret_expr(F, Bs, RBs); @@ -381,7 +382,9 @@ expr({named_fun,Line,Name,Cs} = Ex, Bs, Lf, Ef, RBs) -> eval_named_fun([A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T], RF, Info) end; _Other -> - erlang:raise(error, {'argument_limit',{named_fun,Line,Name,Cs}}, + L = erl_anno:location(Line), + erlang:raise(error, {'argument_limit', + {named_fun,L,Name,to_terms(Cs)}}, ?STACKTRACE) end, ret_expr(F, Bs, RBs); @@ -1092,7 +1095,7 @@ match(Pat, Term, Bs) -> match(Pat, Term, Bs, BBs) -> case catch match1(Pat, Term, Bs, BBs) of invalid -> - erlang:raise(error, {illegal_pattern,Pat}, ?STACKTRACE); + erlang:raise(error, {illegal_pattern,to_term(Pat)}, ?STACKTRACE); Other -> Other end. @@ -1288,6 +1291,12 @@ merge_bindings(Bs1, Bs2) -> %% end %% end, Bs2, Bs1). +to_terms(Abstrs) -> + [to_term(Abstr) || Abstr <- Abstrs]. + +to_term(Abstr) -> + erl_parse:anno_to_term(Abstr). + %% Substitute {value, A, Item} for {var, A, Var}, preserving A. %% {value, A, Item} is a shell/erl_eval convention, and for example %% the linter cannot handle it. diff --git a/lib/stdlib/src/erl_pp.erl b/lib/stdlib/src/erl_pp.erl index 367dbefb82..dd302a2880 100644 --- a/lib/stdlib/src/erl_pp.erl +++ b/lib/stdlib/src/erl_pp.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2017. All Rights Reserved. +%% Copyright Ericsson AB 1996-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. @@ -59,7 +59,7 @@ _ -> ?TEST(T) end). -define(EXPRS_TEST(L), - [?TEST(E) || E <- L]). + _ = [?TEST(E) || E <- L]). -define(TEST(T), %% Assumes that erl_anno has been compiled with DEBUG=true. %% erl_pp does not use the annoations, but test it anyway. diff --git a/lib/syntax_tools/doc/src/notes.xml b/lib/syntax_tools/doc/src/notes.xml index 7ba90a6495..76c2d6ecbd 100644 --- a/lib/syntax_tools/doc/src/notes.xml +++ b/lib/syntax_tools/doc/src/notes.xml @@ -33,29 +33,28 @@ application.</p> <section><title>Syntax_Tools 2.1.5</title> - - <section><title>Fixed Bugs and Malfunctions</title> + <section><title>Improvements and New Features</title> <list> <item> - <p> Fix a bug regarding reverting map types. </p> <p> - Own Id: OTP-15098 Aux Id: ERIERL-177 </p> + Update to use the new string api instead of the old.</p> + <p> + Own Id: OTP-15036</p> </item> </list> </section> +</section> - - <section><title>Improvements and New Features</title> +<section><title>Syntax_Tools 2.1.4.1</title> + <section><title>Fixed Bugs and Malfunctions</title> <list> <item> + <p> Fix a bug regarding reverting map types. </p> <p> - Update to use the new string api instead of the old.</p> - <p> - Own Id: OTP-15036</p> + Own Id: OTP-15098 Aux Id: ERIERL-177 </p> </item> </list> </section> - </section> <section><title>Syntax_Tools 2.1.4</title> diff --git a/otp_versions.table b/otp_versions.table index 07a0416a98..78b19f0805 100644 --- a/otp_versions.table +++ b/otp_versions.table @@ -1,4 +1,6 @@ +OTP-21.0.1 : compiler-7.2.1 erts-10.0.1 # asn1-5.0.6 common_test-1.16 crypto-4.3 debugger-4.2.5 dialyzer-3.3 diameter-2.1.5 edoc-0.9.3 eldap-1.2.4 erl_docgen-0.8 erl_interface-3.10.3 et-1.6.2 eunit-2.3.6 ftp-1.0 hipe-3.18 inets-7.0 jinterface-1.9 kernel-6.0 megaco-3.18.3 mnesia-4.15.4 observer-2.8 odbc-2.12.1 os_mon-2.4.5 otp_mibs-1.2 parsetools-2.1.7 public_key-1.6 reltool-0.7.6 runtime_tools-1.13 sasl-3.2 snmp-5.2.11 ssh-4.7 ssl-9.0 stdlib-3.5 syntax_tools-2.1.5 tftp-1.0 tools-3.0 wx-1.8.4 xmerl-1.3.17 : OTP-21.0 : asn1-5.0.6 common_test-1.16 compiler-7.2 crypto-4.3 debugger-4.2.5 dialyzer-3.3 diameter-2.1.5 edoc-0.9.3 eldap-1.2.4 erl_docgen-0.8 erl_interface-3.10.3 erts-10.0 et-1.6.2 eunit-2.3.6 ftp-1.0 hipe-3.18 inets-7.0 jinterface-1.9 kernel-6.0 mnesia-4.15.4 observer-2.8 os_mon-2.4.5 otp_mibs-1.2 parsetools-2.1.7 public_key-1.6 reltool-0.7.6 runtime_tools-1.13 sasl-3.2 ssh-4.7 ssl-9.0 stdlib-3.5 syntax_tools-2.1.5 tftp-1.0 tools-3.0 wx-1.8.4 xmerl-1.3.17 # megaco-3.18.3 odbc-2.12.1 snmp-5.2.11 : +OTP-20.3.8.1 : inets-6.5.2.1 ssh-4.6.9.1 syntax_tools-2.1.4.1 # asn1-5.0.5 common_test-1.15.4 compiler-7.1.5 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 crypto-4.2.2 debugger-4.2.4 dialyzer-3.2.4 diameter-2.1.4 edoc-0.9.2 eldap-1.2.3 erl_docgen-0.7.3 erl_interface-3.10.2 erts-9.3.3 et-1.6.1 eunit-2.3.5 hipe-3.17.1 ic-4.4.4 jinterface-1.8.1 kernel-5.4.3 megaco-3.18.3 mnesia-4.15.3 observer-2.7 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 runtime_tools-1.12.5 sasl-3.1.2 snmp-5.2.11 ssl-8.2.6 stdlib-3.4.5 tools-2.11.2 wx-1.8.3 xmerl-1.3.16 : OTP-20.3.8 : erts-9.3.3 snmp-5.2.11 # asn1-5.0.5 common_test-1.15.4 compiler-7.1.5 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 crypto-4.2.2 debugger-4.2.4 dialyzer-3.2.4 diameter-2.1.4 edoc-0.9.2 eldap-1.2.3 erl_docgen-0.7.3 erl_interface-3.10.2 et-1.6.1 eunit-2.3.5 hipe-3.17.1 ic-4.4.4 inets-6.5.2 jinterface-1.8.1 kernel-5.4.3 megaco-3.18.3 mnesia-4.15.3 observer-2.7 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 runtime_tools-1.12.5 sasl-3.1.2 ssh-4.6.9 ssl-8.2.6 stdlib-3.4.5 syntax_tools-2.1.4 tools-2.11.2 wx-1.8.3 xmerl-1.3.16 : OTP-20.3.7 : erl_docgen-0.7.3 erts-9.3.2 inets-6.5.2 # asn1-5.0.5 common_test-1.15.4 compiler-7.1.5 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 crypto-4.2.2 debugger-4.2.4 dialyzer-3.2.4 diameter-2.1.4 edoc-0.9.2 eldap-1.2.3 erl_interface-3.10.2 et-1.6.1 eunit-2.3.5 hipe-3.17.1 ic-4.4.4 jinterface-1.8.1 kernel-5.4.3 megaco-3.18.3 mnesia-4.15.3 observer-2.7 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 runtime_tools-1.12.5 sasl-3.1.2 snmp-5.2.10 ssh-4.6.9 ssl-8.2.6 stdlib-3.4.5 syntax_tools-2.1.4 tools-2.11.2 wx-1.8.3 xmerl-1.3.16 : OTP-20.3.6 : crypto-4.2.2 ssh-4.6.9 # asn1-5.0.5 common_test-1.15.4 compiler-7.1.5 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 debugger-4.2.4 dialyzer-3.2.4 diameter-2.1.4 edoc-0.9.2 eldap-1.2.3 erl_docgen-0.7.2 erl_interface-3.10.2 erts-9.3.1 et-1.6.1 eunit-2.3.5 hipe-3.17.1 ic-4.4.4 inets-6.5.1 jinterface-1.8.1 kernel-5.4.3 megaco-3.18.3 mnesia-4.15.3 observer-2.7 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 runtime_tools-1.12.5 sasl-3.1.2 snmp-5.2.10 ssl-8.2.6 stdlib-3.4.5 syntax_tools-2.1.4 tools-2.11.2 wx-1.8.3 xmerl-1.3.16 : diff --git a/scripts/pre-push b/scripts/pre-push index 0349378056..945fb3519a 100755 --- a/scripts/pre-push +++ b/scripts/pre-push @@ -22,10 +22,10 @@ # <local ref> <local sha1> <remote ref> <remote sha1> # -RELEASES="20 19 18 17 r16 r15 r14 r13" +RELEASES="21 20 19 18 17 r16 r15 r14 r13" # First commit on master, not allowed in other branches -MASTER_ONLY=f52748254f17ba42e344798e8c787a1e3361fa33 +MASTER_ONLY=aea2a053e28a11497796879715be29ab0c3cd1a0 # Number of commits and files allowed in one push by this script NCOMMITS_MAX=100 @@ -158,7 +158,7 @@ then exit 1 fi ;; - refs/tags/OTP-20.* | refs/tags/OTP-19.* | refs/tags/OTP-18.* | refs/tags/OTP-17.*) + refs/tags/OTP-21.* | refs/tags/OTP-20.* | refs/tags/OTP-19.* | refs/tags/OTP-18.* | refs/tags/OTP-17.*) tag=${remote_ref#refs/tags/} if [ "$remote_sha" != $null ] then |