diff options
204 files changed, 4389 insertions, 2192 deletions
diff --git a/bootstrap/bin/start.boot b/bootstrap/bin/start.boot Binary files differindex cc0edd5427..ef962190a4 100644 --- a/bootstrap/bin/start.boot +++ b/bootstrap/bin/start.boot diff --git a/bootstrap/bin/start_clean.boot b/bootstrap/bin/start_clean.boot Binary files differindex cc0edd5427..ef962190a4 100644 --- a/bootstrap/bin/start_clean.boot +++ b/bootstrap/bin/start_clean.boot diff --git a/bootstrap/lib/compiler/ebin/beam_block.beam b/bootstrap/lib/compiler/ebin/beam_block.beam Binary files differindex cccf21d9ab..ea91a62b56 100644 --- a/bootstrap/lib/compiler/ebin/beam_block.beam +++ b/bootstrap/lib/compiler/ebin/beam_block.beam diff --git a/bootstrap/lib/compiler/ebin/beam_validator.beam b/bootstrap/lib/compiler/ebin/beam_validator.beam Binary files differindex 5722578840..fcb2202d2a 100644 --- a/bootstrap/lib/compiler/ebin/beam_validator.beam +++ b/bootstrap/lib/compiler/ebin/beam_validator.beam diff --git a/bootstrap/lib/compiler/ebin/rec_env.beam b/bootstrap/lib/compiler/ebin/rec_env.beam Binary files differindex a7c78175c3..068108dab2 100644 --- a/bootstrap/lib/compiler/ebin/rec_env.beam +++ b/bootstrap/lib/compiler/ebin/rec_env.beam diff --git a/bootstrap/lib/compiler/ebin/sys_core_fold.beam b/bootstrap/lib/compiler/ebin/sys_core_fold.beam Binary files differindex 7d01ed81d5..68cc4e3ff3 100644 --- a/bootstrap/lib/compiler/ebin/sys_core_fold.beam +++ b/bootstrap/lib/compiler/ebin/sys_core_fold.beam diff --git a/bootstrap/lib/compiler/ebin/v3_codegen.beam b/bootstrap/lib/compiler/ebin/v3_codegen.beam Binary files differindex 648cd3dea1..6afb674a9f 100644 --- a/bootstrap/lib/compiler/ebin/v3_codegen.beam +++ b/bootstrap/lib/compiler/ebin/v3_codegen.beam diff --git a/bootstrap/lib/compiler/ebin/v3_core.beam b/bootstrap/lib/compiler/ebin/v3_core.beam Binary files differindex 110d376a0a..e8e120e145 100644 --- a/bootstrap/lib/compiler/ebin/v3_core.beam +++ b/bootstrap/lib/compiler/ebin/v3_core.beam diff --git a/bootstrap/lib/compiler/ebin/v3_life.beam b/bootstrap/lib/compiler/ebin/v3_life.beam Binary files differindex d8822f929f..b220e475ee 100644 --- a/bootstrap/lib/compiler/ebin/v3_life.beam +++ b/bootstrap/lib/compiler/ebin/v3_life.beam diff --git a/bootstrap/lib/kernel/ebin/gen_sctp.beam b/bootstrap/lib/kernel/ebin/gen_sctp.beam Binary files differindex fe2ff17ff1..fca569c779 100644 --- a/bootstrap/lib/kernel/ebin/gen_sctp.beam +++ b/bootstrap/lib/kernel/ebin/gen_sctp.beam diff --git a/bootstrap/lib/kernel/ebin/gen_tcp.beam b/bootstrap/lib/kernel/ebin/gen_tcp.beam Binary files differindex e40f1f2e45..cf4757a568 100644 --- a/bootstrap/lib/kernel/ebin/gen_tcp.beam +++ b/bootstrap/lib/kernel/ebin/gen_tcp.beam diff --git a/bootstrap/lib/kernel/ebin/gen_udp.beam b/bootstrap/lib/kernel/ebin/gen_udp.beam Binary files differindex ec4eda12f7..fec430304e 100644 --- a/bootstrap/lib/kernel/ebin/gen_udp.beam +++ b/bootstrap/lib/kernel/ebin/gen_udp.beam diff --git a/bootstrap/lib/kernel/ebin/inet.beam b/bootstrap/lib/kernel/ebin/inet.beam Binary files differindex 9058389555..8eaf2fbe74 100644 --- a/bootstrap/lib/kernel/ebin/inet.beam +++ b/bootstrap/lib/kernel/ebin/inet.beam diff --git a/bootstrap/lib/kernel/ebin/inet6_sctp.beam b/bootstrap/lib/kernel/ebin/inet6_sctp.beam Binary files differindex 5c827589bd..c3aac09fbe 100644 --- a/bootstrap/lib/kernel/ebin/inet6_sctp.beam +++ b/bootstrap/lib/kernel/ebin/inet6_sctp.beam diff --git a/bootstrap/lib/kernel/ebin/inet6_tcp.beam b/bootstrap/lib/kernel/ebin/inet6_tcp.beam Binary files differindex f1ab2912bb..80e8e389a2 100644 --- a/bootstrap/lib/kernel/ebin/inet6_tcp.beam +++ b/bootstrap/lib/kernel/ebin/inet6_tcp.beam diff --git a/bootstrap/lib/kernel/ebin/inet6_udp.beam b/bootstrap/lib/kernel/ebin/inet6_udp.beam Binary files differindex 2eb8c09701..39b9f0343b 100644 --- a/bootstrap/lib/kernel/ebin/inet6_udp.beam +++ b/bootstrap/lib/kernel/ebin/inet6_udp.beam diff --git a/bootstrap/lib/kernel/ebin/inet_sctp.beam b/bootstrap/lib/kernel/ebin/inet_sctp.beam Binary files differindex 835c7b2928..e2f574b514 100644 --- a/bootstrap/lib/kernel/ebin/inet_sctp.beam +++ b/bootstrap/lib/kernel/ebin/inet_sctp.beam diff --git a/bootstrap/lib/kernel/ebin/inet_tcp.beam b/bootstrap/lib/kernel/ebin/inet_tcp.beam Binary files differindex 698a94770b..d0c9f09f6f 100644 --- a/bootstrap/lib/kernel/ebin/inet_tcp.beam +++ b/bootstrap/lib/kernel/ebin/inet_tcp.beam diff --git a/bootstrap/lib/kernel/ebin/inet_udp.beam b/bootstrap/lib/kernel/ebin/inet_udp.beam Binary files differindex 6876ba3892..11eb4f840f 100644 --- a/bootstrap/lib/kernel/ebin/inet_udp.beam +++ b/bootstrap/lib/kernel/ebin/inet_udp.beam diff --git a/bootstrap/lib/kernel/ebin/kernel.app b/bootstrap/lib/kernel/ebin/kernel.app index 15bc34e789..a4600713e5 100644 --- a/bootstrap/lib/kernel/ebin/kernel.app +++ b/bootstrap/lib/kernel/ebin/kernel.app @@ -55,6 +55,8 @@ inet_tcp_dist, kernel, kernel_config, + local_tcp, + local_udp, net, net_adm, net_kernel, diff --git a/bootstrap/lib/kernel/ebin/local_tcp.beam b/bootstrap/lib/kernel/ebin/local_tcp.beam Binary files differnew file mode 100644 index 0000000000..153aad2936 --- /dev/null +++ b/bootstrap/lib/kernel/ebin/local_tcp.beam diff --git a/bootstrap/lib/kernel/ebin/local_udp.beam b/bootstrap/lib/kernel/ebin/local_udp.beam Binary files differnew file mode 100644 index 0000000000..29c9b01faa --- /dev/null +++ b/bootstrap/lib/kernel/ebin/local_udp.beam diff --git a/bootstrap/lib/stdlib/ebin/zip.beam b/bootstrap/lib/stdlib/ebin/zip.beam Binary files differindex 1a19b1f28a..6ef3732965 100644 --- a/bootstrap/lib/stdlib/ebin/zip.beam +++ b/bootstrap/lib/stdlib/ebin/zip.beam diff --git a/erts/configure.in b/erts/configure.in index a34dfc6dbd..81ecad4f51 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -2116,7 +2116,7 @@ fi dnl Need by run_erl. AC_CHECK_FUNCS([openpty]) -AC_CHECK_HEADERS(net/if_dl.h ifaddrs.h netpacket/packet.h) +AC_CHECK_HEADERS(net/if_dl.h ifaddrs.h netpacket/packet.h sys/un.h) AC_CHECK_FUNCS([getifaddrs]) dnl Checks for variables in6addr_any and in6addr_loopback, @@ -3858,15 +3858,15 @@ if test "$enable_lttng_test" = "yes" ; then AC_COMPILE_IFELSE( [AC_LANG_PROGRAM( [#include <lttng/tracepoint.h> - #define TRACEPOINT_PROVIDER com_ericsson_otp + #define TRACEPOINT_PROVIDER org_erlang_otp TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, dummy, TP_ARGS(int, my_int), TP_FIELDS(ctf_integer(int, my_int, my_int))) #define TRACEPOINT_CREATE_PROBES #define TRACEPOINT_DEFINE], - [if(tracepoint_enabled(com_ericsson_otp,dummy)) do {} while(0)])], + [if(tracepoint_enabled(org_erlang_otp,dummy)) do {} while(0)])], [AC_MSG_RESULT([yes])], [AC_MSG_ERROR([no (available in lttng-ust v2.7)])]) if test "x$ac_cv_header_lttng_tracepoint_h" = "xyes" \ diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index 4efd155b09..b2e2254a65 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -1101,15 +1101,6 @@ typedef enum { Erlang operators <c>=:=</c> and <c>=/=</c>.</p></desc> </func> - <func><name><ret>int</ret><nametext>enif_is_on_dirty_scheduler(ErlNifEnv* env)</nametext></name> - <fsummary>Check to see if executing on a dirty scheduler thread</fsummary> - <desc> - <p>Check to see if the current NIF is executing on a dirty scheduler thread. If - executing on a dirty scheduler thread true returned; otherwise false.</p> - <p>This function can only be used from a NIF-calling thread, and with an - environment corresponding to currently executing processes.</p> - </desc> - </func> <func><name><ret>int</ret><nametext>enif_is_pid(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name> <fsummary>Determine if a term is a pid</fsummary> <desc><p>Return true if <c>term</c> is a pid.</p></desc> @@ -1820,7 +1811,25 @@ enif_map_iterator_destroy(env, &iter); <desc><p>Same as <seealso marker="erl_driver#erl_drv_thread_self">erl_drv_thread_self</seealso>. </p></desc> </func> - + <func><name><ret>int</ret><nametext>enif_thread_type(void)</nametext></name> + <fsummary>Determine type of current thread</fsummary> + <desc> + <p>Determine the type of currently executing thread. A positive value + indicates a scheduler thread while a negative value or zero indicates + another type of thread. Currently the following specific types exist + (which may be extended in the future):</p> + <taglist> + <tag><c>ERL_NIF_THR_UNDEFINED</c></tag> + <value><p>Undefined thread that is not a scheduler thread.</p></value> + <tag><c>ERL_NIF_THR_NORMAL_SCHEDULER</c></tag> + <value><p>A normal scheduler thread.</p></value> + <tag><c>ERL_NIF_THR_DIRTY_CPU_SCHEDULER</c></tag> + <value><p>A dirty CPU scheduler thread.</p></value> + <tag><c>ERL_NIF_THR_DIRTY_IO_SCHEDULER</c></tag> + <value><p>A dirty I/O scheduler thread.</p></value> + </taglist> + </desc> + </func> <func> <name><ret>ErlNifTime</ret><nametext>enif_time_offset(ErlNifTimeUnit time_unit)</nametext></name> <fsummary>Get current Time Offset</fsummary> diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 0a59f8785c..4716460a6b 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -5318,10 +5318,14 @@ void erts_dirty_process_main(ErtsSchedulerData *esdp) ASSERT(BeamOp(op_call_nif) == (BeamInstr *) *I); - if (ERTS_PROC_GET_SAVED_CALLS_BUF(c_p) - && (ERTS_TRACE_FLAGS(c_p) & F_SENSITIVE) == 0) { - c_p->fcalls = REDS_IN(c_p) = 0; - } + /* + * Set fcalls even though we ignore it, so we don't + * confuse code accessing it... + */ + if (ERTS_PROC_GET_SAVED_CALLS_BUF(c_p)) + c_p->fcalls = 0; + else + c_p->fcalls = CONTEXT_REDS; SWAPIN; @@ -5341,7 +5345,7 @@ void erts_dirty_process_main(ErtsSchedulerData *esdp) NULL, fun_buf); } else { erts_snprintf(fun_buf, sizeof(DTRACE_CHARBUF_NAME(fun_buf)), - "<unknown/%p>", next); + "<unknown/%p>", *I); } } @@ -5351,8 +5355,10 @@ void erts_dirty_process_main(ErtsSchedulerData *esdp) } { - Eterm nif_bif_result; - Eterm bif_nif_arity; +#ifdef DEBUG + Eterm result; +#endif + Eterm arity; { /* @@ -5374,7 +5380,7 @@ void erts_dirty_process_main(ErtsSchedulerData *esdp) c_p->current = I-3; /* current and vbf set to please handle_error */ SWAPOUT; PROCESS_MAIN_CHK_LOCKS(c_p); - bif_nif_arity = I[-1]; + arity = I[-1]; ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p); ASSERT(!ERTS_PROC_IS_EXITING(c_p)); @@ -5383,50 +5389,39 @@ void erts_dirty_process_main(ErtsSchedulerData *esdp) NifF* fp = vbf = (NifF*) I[1]; struct enif_environment_t env; ASSERT(!c_p->scheduler_data); - erts_pre_dirty_nif(esdp, &env, c_p, (struct erl_module_nif*)I[2], NULL); - nif_bif_result = (*fp)(&env, bif_nif_arity, reg); - if (env.exception_thrown) - nif_bif_result = THE_NON_VALUE; + + erts_pre_dirty_nif(esdp, &env, c_p, + (struct erl_module_nif*)I[2], NULL); + +#ifdef DEBUG + result = +#else + (void) +#endif + (*fp)(&env, arity, reg); + erts_post_nif(&env); + + ASSERT(!is_value(result)); + ASSERT(c_p->freason == TRAP); + ASSERT(!(c_p->flags & F_HIBERNATE_SCHED)); + PROCESS_MAIN_CHK_LOCKS(c_p); + ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_EMULATOR); - if (env.exiting) { - ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p); + if (env.exiting) goto do_dirty_schedule; - } ASSERT(!ERTS_PROC_IS_EXITING(c_p)); } + DTRACE_NIF_RETURN(c_p, (Eterm)I[-3], (Eterm)I[-2], (Uint)I[-1]); - ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p); ERTS_HOLE_CHECK(c_p); - if (ERTS_IS_GC_DESIRED(c_p)) { - nif_bif_result = erts_gc_after_bif_call(c_p, - nif_bif_result, - reg, bif_nif_arity); - } - SWAPIN; /* There might have been a garbage collection. */ - if (is_value(nif_bif_result)) { - r(0) = nif_bif_result; - CHECK_TERM(r(0)); - I = c_p->cp; - c_p->cp = 0; - Goto(*I); - } else if (c_p->freason == TRAP) { - I = c_p->i; - ASSERT(!(c_p->flags & F_HIBERNATE_SCHED)); - goto context_switch; - } - I = handle_error(c_p, c_p->cp, reg, vbf); + SWAPIN; + I = c_p->i; + goto context_switch; } } - if (I == 0) { - goto do_dirty_schedule; - } else { - ASSERT(!is_value(r(0))); - SWAPIN; - goto context_switch; - } #endif /* ERTS_DIRTY_SCHEDULERS */ } diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c index 37f4e1de49..fefa9d8391 100644 --- a/erts/emulator/beam/erl_bif_port.c +++ b/erts/emulator/beam/erl_bif_port.c @@ -1411,7 +1411,7 @@ BIF_RETTYPE decode_packet_3(BIF_ALIST_3) trunc_len = val; goto next_option; case am_line_delimiter: - if (type == TCP_PB_LINE_LF && val >= 0 && val <= 255) { + if (type == TCP_PB_LINE_LF && val <= 255) { delimiter = (char)val; goto next_option; } diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c index 74979f984a..074ac6d64e 100644 --- a/erts/emulator/beam/erl_db_hash.c +++ b/erts/emulator/beam/erl_db_hash.c @@ -95,7 +95,8 @@ /* * The following symbols can be manipulated to "tune" the linear hash array */ -#define CHAIN_LEN 6 /* Medium bucket chain len */ +#define GROW_LIMIT(NACTIVE) ((NACTIVE)*2) +#define SHRINK_LIMIT(NACTIVE) ((NACTIVE) / 2) /* Number of slots per segment */ #define SEGSZ_EXP 8 @@ -463,7 +464,7 @@ db_finalize_dbterm_hash(int cret, DbUpdateHandle* handle); static ERTS_INLINE void try_shrink(DbTableHash* tb) { int nactive = NACTIVE(tb); - if (nactive > SEGSZ && NITEMS(tb) < (nactive * CHAIN_LEN) + if (nactive > SEGSZ && NITEMS(tb) < SHRINK_LIMIT(nactive) && !IS_FIXED(tb)) { shrink(tb, nactive); } @@ -670,8 +671,8 @@ int db_create_hash(Process *p, DbTable *tbl) tb->nsegs = NSEG_1; tb->nslots = SEGSZ; - erts_smp_atomic_init_nob(&tb->is_resizing, 0); #ifdef ERTS_SMP + erts_smp_atomic_init_nob(&tb->is_resizing, 0); if (tb->common.type & DB_FINE_LOCKED) { erts_smp_rwmtx_opt_t rwmtx_opt = ERTS_SMP_RWMTX_OPT_DEFAULT_INITER; int i; @@ -862,7 +863,7 @@ Lnew: WUNLOCK_HASH(lck); { int nactive = NACTIVE(tb); - if (nitems > nactive * (CHAIN_LEN+1) && !IS_FIXED(tb)) { + if (nitems > GROW_LIMIT(nactive) && !IS_FIXED(tb)) { grow(tb, nactive); } } @@ -2250,12 +2251,12 @@ static int db_free_table_continue_hash(DbTable *tbl) done /= 2; while(tb->nslots != 0) { - free_seg(tb, 1); + done += 1 + SEGSZ/64 + free_seg(tb, 1); /* * If we have done enough work, get out here. */ - if (++done >= (DELETE_RECORD_LIMIT / CHAIN_LEN / SEGSZ)) { + if (done >= DELETE_RECORD_LIMIT) { return 0; /* Not done */ } } @@ -2604,23 +2605,22 @@ static Eterm build_term_list(Process* p, HashDbTerm* ptr1, HashDbTerm* ptr2, static ERTS_INLINE int begin_resizing(DbTableHash* tb) { +#ifdef ERTS_SMP if (DB_USING_FINE_LOCKING(tb)) - return !erts_smp_atomic_xchg_acqb(&tb->is_resizing, 1); - else { - if (erts_smp_atomic_read_nob(&tb->is_resizing)) - return 0; - erts_smp_atomic_set_nob(&tb->is_resizing, 1); - return 1; - } + return !erts_atomic_xchg_acqb(&tb->is_resizing, 1); + else + ERTS_LC_ASSERT(erts_lc_rwmtx_is_rwlocked(&tb->common.rwlock)); +#endif + return 1; } static ERTS_INLINE void done_resizing(DbTableHash* tb) { +#ifdef ERTS_SMP if (DB_USING_FINE_LOCKING(tb)) - erts_smp_atomic_set_relb(&tb->is_resizing, 0); - else - erts_smp_atomic_set_nob(&tb->is_resizing, 0); + erts_atomic_set_relb(&tb->is_resizing, 0); +#endif } /* Grow table with one new bucket. @@ -2871,7 +2871,7 @@ db_lookup_dbterm_hash(Process *p, DbTable *tbl, Eterm key, Eterm obj, int nitems = erts_smp_atomic_inc_read_nob(&tb->common.nitems); int nactive = NACTIVE(tb); - if (nitems > nactive * (CHAIN_LEN + 1) && !IS_FIXED(tb)) { + if (nitems > GROW_LIMIT(nactive) && !IS_FIXED(tb)) { grow(tb, nactive); } } diff --git a/erts/emulator/beam/erl_db_hash.h b/erts/emulator/beam/erl_db_hash.h index e654363cd5..081ff8fafc 100644 --- a/erts/emulator/beam/erl_db_hash.h +++ b/erts/emulator/beam/erl_db_hash.h @@ -60,8 +60,8 @@ typedef struct db_table_hash { /* List of slots where elements have been deleted while table was fixed */ erts_smp_atomic_t fixdel; /* (FixedDeletion*) */ erts_smp_atomic_t nactive; /* Number of "active" slots */ - erts_smp_atomic_t is_resizing; /* grow/shrink in progress */ #ifdef ERTS_SMP + erts_smp_atomic_t is_resizing; /* grow/shrink in progress */ DbTableHashFineLocks* locks; #endif #ifdef VALGRIND diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index f1c35cd5a5..039f97ef43 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -803,19 +803,13 @@ ERL_NIF_TERM enif_make_copy(ErlNifEnv* dst_env, ERL_NIF_TERM src_term) { Uint sz; Eterm* hp; -#ifdef SHCOPY - erts_shcopy_t info; - INITIALIZE_SHCOPY(info); - sz = copy_shared_calculate(src_term, &info); - hp = alloc_heap(dst_env, sz); - src_term = copy_shared_perform(src_term, sz, &info, &hp, &MSO(dst_env->proc)); - DESTROY_SHCOPY(info); - return src_term; -#else + /* + * No preserved sharing allowed as long as literals are also preserved. + * Process independent environment can not be reached by purge. + */ sz = size_object(src_term); hp = alloc_heap(dst_env, sz); return copy_struct(src_term, sz, &hp, &MSO(dst_env->proc)); -#endif } @@ -2664,18 +2658,21 @@ done: } int -enif_is_on_dirty_scheduler(ErlNifEnv* env) +enif_thread_type(void) { - int scheduler; - Process *c_p; + ErtsSchedulerData *esdp = erts_get_scheduler_data(); - execution_state(env, &c_p, &scheduler); + if (!esdp) + return ERL_NIF_THR_UNDEFINED; + + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) + return ERL_NIF_THR_NORMAL_SCHEDULER; - if (!c_p || !scheduler) - erts_exit(ERTS_ABORT_EXIT, "enif_is_on_dirty_scheduler: " - "Invalid env"); + if (ERTS_SCHEDULER_IS_DIRTY_CPU(esdp)) + return ERL_NIF_THR_DIRTY_CPU_SCHEDULER; - return scheduler < 0; + ASSERT(ERTS_SCHEDULER_IS_DIRTY_IO(esdp)); + return ERL_NIF_THR_DIRTY_IO_SCHEDULER; } /* Maps */ diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h index da7a754757..494971e118 100644 --- a/erts/emulator/beam/erl_nif.h +++ b/erts/emulator/beam/erl_nif.h @@ -209,6 +209,17 @@ typedef enum { ERL_NIF_BIN2TERM_SAFE = 0x20000000 } ErlNifBinaryToTerm; +/* + * Return values from enif_thread_type(). Negative values + * reserved for specific types of non-scheduler threads. + * Positive values reserved for scheduler thread types. + */ + +#define ERL_NIF_THR_UNDEFINED 0 +#define ERL_NIF_THR_NORMAL_SCHEDULER 1 +#define ERL_NIF_THR_DIRTY_CPU_SCHEDULER 2 +#define ERL_NIF_THR_DIRTY_IO_SCHEDULER 3 + #if (defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_)) # define ERL_NIF_API_FUNC_DECL(RET_TYPE, NAME, ARGS) RET_TYPE (*NAME) ARGS typedef struct { diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h index b211ab4b16..9a8f216773 100644 --- a/erts/emulator/beam/erl_nif_api_funcs.h +++ b/erts/emulator/beam/erl_nif_api_funcs.h @@ -173,7 +173,7 @@ ERL_NIF_API_FUNC_DECL(int, enif_get_local_port, (ErlNifEnv* env, ERL_NIF_TERM, E ERL_NIF_API_FUNC_DECL(int, enif_term_to_binary, (ErlNifEnv *env, ERL_NIF_TERM term, ErlNifBinary *bin)); ERL_NIF_API_FUNC_DECL(size_t, enif_binary_to_term, (ErlNifEnv *env, const unsigned char* data, size_t sz, ERL_NIF_TERM *term, unsigned int opts)); ERL_NIF_API_FUNC_DECL(int, enif_port_command, (ErlNifEnv *env, const ErlNifPort* to_port, ErlNifEnv *msg_env, ERL_NIF_TERM msg)); -ERL_NIF_API_FUNC_DECL(int,enif_is_on_dirty_scheduler,(ErlNifEnv*)); +ERL_NIF_API_FUNC_DECL(int,enif_thread_type,(void)); ERL_NIF_API_FUNC_DECL(int,enif_snprintf,(char * buffer, size_t size, const char *format, ...)); /* @@ -330,7 +330,7 @@ ERL_NIF_API_FUNC_DECL(int,enif_snprintf,(char * buffer, size_t size, const char # define enif_term_to_binary ERL_NIF_API_FUNC_MACRO(enif_term_to_binary) # define enif_binary_to_term ERL_NIF_API_FUNC_MACRO(enif_binary_to_term) # define enif_port_command ERL_NIF_API_FUNC_MACRO(enif_port_command) -# define enif_is_on_dirty_scheduler ERL_NIF_API_FUNC_MACRO(enif_is_on_dirty_scheduler) +# define enif_thread_type ERL_NIF_API_FUNC_MACRO(enif_thread_type) # define enif_snprintf ERL_NIF_API_FUNC_MACRO(enif_snprintf) /* diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index c0b1d7246c..5193be85b4 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -10082,7 +10082,7 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls) } } - if (ERTS_IS_GC_DESIRED(p)) { + if (ERTS_IS_GC_DESIRED(p) && !ERTS_SCHEDULER_IS_DIRTY_IO(esdp)) { if (!(state & ERTS_PSFLG_EXITING) && !(p->flags & (F_DELAY_GC|F_DISABLE_GC))) { int cost = scheduler_gc_proc(p, reds); calls += cost; diff --git a/erts/emulator/beam/erl_process_dump.c b/erts/emulator/beam/erl_process_dump.c index eeaa9a569c..a70dfb8e73 100644 --- a/erts/emulator/beam/erl_process_dump.c +++ b/erts/emulator/beam/erl_process_dump.c @@ -560,6 +560,11 @@ dump_externally(int to, void *to_arg, Eterm term) } } + /* Do not handle maps */ + if (is_map(term)) { + term = am_undefined; + } + s = p = sbuf; erts_encode_ext(term, &p); erts_print(to, to_arg, "E%X:", p-s); diff --git a/erts/emulator/beam/erlang_lttng.h b/erts/emulator/beam/erlang_lttng.h index 12f68e477b..4e869671f7 100644 --- a/erts/emulator/beam/erlang_lttng.h +++ b/erts/emulator/beam/erlang_lttng.h @@ -20,7 +20,7 @@ #ifdef USE_LTTNG #undef TRACEPOINT_PROVIDER -#define TRACEPOINT_PROVIDER com_ericsson_otp +#define TRACEPOINT_PROVIDER org_erlang_otp #undef TRACEPOINT_INCLUDE #define TRACEPOINT_INCLUDE "erlang_lttng.h" @@ -33,7 +33,7 @@ /* Schedulers */ TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, scheduler_poll, TP_ARGS( int, id, @@ -62,7 +62,7 @@ typedef struct { /* Port and Driver Scheduling */ TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_start, TP_ARGS( char*, pid, @@ -77,7 +77,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_init, TP_ARGS( char*, driver, @@ -94,7 +94,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_outputv, TP_ARGS( char*, pid, @@ -111,7 +111,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_output, TP_ARGS( char*, pid, @@ -128,7 +128,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_ready_input, TP_ARGS( char*, pid, @@ -143,7 +143,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_ready_output, TP_ARGS( char*, pid, @@ -158,7 +158,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_event, TP_ARGS( char*, pid, @@ -173,7 +173,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_timeout, TP_ARGS( char*, pid, @@ -188,7 +188,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_stop_select, TP_ARGS( char*, driver @@ -199,7 +199,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_flush, TP_ARGS( char*, pid, @@ -214,7 +214,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_stop, TP_ARGS( char*, pid, @@ -229,7 +229,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_process_exit, TP_ARGS( char*, pid, @@ -244,7 +244,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_ready_async, TP_ARGS( char*, pid, @@ -259,7 +259,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_finish, TP_ARGS( char*, driver @@ -270,7 +270,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_call, TP_ARGS( char*, pid, @@ -289,7 +289,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_control, TP_ARGS( char*, pid, @@ -310,7 +310,7 @@ TRACEPOINT_EVENT( /* Async pool */ TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, aio_pool_get, TP_ARGS( char*, port, @@ -323,7 +323,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, aio_pool_put, TP_ARGS( char*, port, @@ -339,7 +339,7 @@ TRACEPOINT_EVENT( /* Memory Allocator */ TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, carrier_create, TP_ARGS( const char*, type, @@ -365,7 +365,7 @@ TRACEPOINT_EVENT( TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, carrier_destroy, TP_ARGS( const char*, type, @@ -390,7 +390,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, carrier_pool_put, TP_ARGS( const char*, name, @@ -405,7 +405,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, carrier_pool_get, TP_ARGS( const char*, name, diff --git a/erts/emulator/beam/lttng-wrapper.h b/erts/emulator/beam/lttng-wrapper.h index 294872c365..0bc75c1552 100644 --- a/erts/emulator/beam/lttng-wrapper.h +++ b/erts/emulator/beam/lttng-wrapper.h @@ -77,23 +77,23 @@ (RQ)->scheduler->no #define LTTNG_ENABLED(Name) \ - tracepoint_enabled(com_ericsson_otp, Name) + tracepoint_enabled(org_erlang_otp, Name) /* include a special LTTNG_DO for do_tracepoint ? */ #define LTTNG1(Name, Arg1) \ - tracepoint(com_ericsson_otp, Name, (Arg1)) + tracepoint(org_erlang_otp, Name, (Arg1)) #define LTTNG2(Name, Arg1, Arg2) \ - tracepoint(com_ericsson_otp, Name, (Arg1), (Arg2)) + tracepoint(org_erlang_otp, Name, (Arg1), (Arg2)) #define LTTNG3(Name, Arg1, Arg2, Arg3) \ - tracepoint(com_ericsson_otp, Name, (Arg1), (Arg2), (Arg3)) + tracepoint(org_erlang_otp, Name, (Arg1), (Arg2), (Arg3)) #define LTTNG4(Name, Arg1, Arg2, Arg3, Arg4) \ - tracepoint(com_ericsson_otp, Name, (Arg1), (Arg2), (Arg3), (Arg4)) + tracepoint(org_erlang_otp, Name, (Arg1), (Arg2), (Arg3), (Arg4)) #define LTTNG5(Name, Arg1, Arg2, Arg3, Arg4, Arg5) \ - tracepoint(com_ericsson_otp, Name, (Arg1), (Arg2), (Arg3), (Arg4), (Arg5)) + tracepoint(org_erlang_otp, Name, (Arg1), (Arg2), (Arg3), (Arg4), (Arg5)) #else /* USE_LTTNG */ diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index 9a205d50d3..dfe82cab44 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -154,8 +154,9 @@ typedef ERTS_SYS_FD_TYPE ErtsSysFdType; # define ERTS_WRITE_UNLIKELY(X) X #endif +/* clang may have too low __GNUC__ versions but can handle it */ #ifdef __GNUC__ -# if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 5) +# if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 5) || defined(__clang__) # define ERTS_DECLARE_DUMMY(X) X __attribute__ ((unused)) # else # define ERTS_DECLARE_DUMMY(X) X diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index e87d141ddb..93ea9f5dcb 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -34,6 +34,7 @@ #include <stdio.h> #include <stdlib.h> +#include <stddef.h> #include <ctype.h> #include <sys/types.h> #include <errno.h> @@ -58,6 +59,9 @@ #ifdef HAVE_NETPACKET_PACKET_H #include <netpacket/packet.h> #endif +#ifdef HAVE_SYS_UN_H +#include <sys/un.h> +#endif /* All platforms fail on malloc errors. */ #define FATAL_MALLOC @@ -573,7 +577,7 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) #ifdef HAVE_SOCKLEN_T # define SOCKLEN_T socklen_t #else -# define SOCKLEN_T int +# define SOCKLEN_T size_t #endif #include "packet_parser.h" @@ -587,6 +591,22 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) (((unsigned char*) (s))[1] << 8) | \ (((unsigned char*) (s))[0])) +/* strnlen doesn't exist everywhere */ +static size_t my_strnlen(const char *s, size_t maxlen) +{ + size_t i = 0; + while (i < maxlen && s[i] != '\0') + i++; + return i; +} + +/* Check that some character in the buffer != '\0' */ +static int is_nonzero(const char *s, size_t n) +{ + size_t i; + for (i = 0; i < n; i++) if (s[i] != '\0') return !0; + return 0; +} #ifdef VALGRIND # include <valgrind/memcheck.h> @@ -607,10 +627,13 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) */ /* general address encode/decode tag */ +#define INET_AF_UNSPEC 0 #define INET_AF_INET 1 #define INET_AF_INET6 2 #define INET_AF_ANY 3 /* INADDR_ANY or IN6ADDR_ANY_INIT */ #define INET_AF_LOOPBACK 4 /* INADDR_LOOPBACK or IN6ADDR_LOOPBACK_INIT */ +#define INET_AF_LOCAL 5 +#define INET_AF_UNDEFINED 6 /* Unknown */ /* open and INET_REQ_GETTYPE enumeration */ #define INET_TYPE_STREAM 1 @@ -633,7 +656,7 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) /* INET_REQ_GETSTATUS enumeration */ #define INET_F_OPEN 0x0001 -#define INET_F_BOUND 0x0002 +/* INET_F_BOUND removed - renumber when there comes a bigger rewrite */ #define INET_F_ACTIVE 0x0004 #define INET_F_LISTEN 0x0008 #define INET_F_CON 0x0010 @@ -831,19 +854,15 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n) #define INET_STATE_CLOSED (0) #define INET_STATE_OPEN (INET_F_OPEN) -#define INET_STATE_BOUND (INET_STATE_OPEN | INET_F_BOUND) -#define INET_STATE_CONNECTED (INET_STATE_BOUND | INET_F_ACTIVE) -#define INET_STATE_LISTENING (INET_STATE_BOUND | INET_F_LISTEN) -#define INET_STATE_CONNECTING (INET_STATE_BOUND | INET_F_CON) +#define INET_STATE_CONNECTED (INET_STATE_OPEN | INET_F_ACTIVE) +#define INET_STATE_LISTENING (INET_STATE_OPEN | INET_F_LISTEN) +#define INET_STATE_CONNECTING (INET_STATE_OPEN | INET_F_CON) #define INET_STATE_ACCEPTING (INET_STATE_LISTENING | INET_F_ACC) #define INET_STATE_MULTI_ACCEPTING (INET_STATE_ACCEPTING | INET_F_MULTI_CLIENT) #define IS_OPEN(d) \ (((d)->state & INET_F_OPEN) == INET_F_OPEN) -#define IS_BOUND(d) \ - (((d)->state & INET_F_BOUND) == INET_F_BOUND) - #define IS_CONNECTED(d) \ (((d)->state & INET_STATE_CONNECTED) == INET_STATE_CONNECTED) @@ -896,19 +915,35 @@ typedef union { #ifdef HAVE_IN6 struct sockaddr_in6 sai6; #endif +#ifdef HAVE_SYS_UN_H + struct sockaddr_un sal; +#endif } inet_address; -/* for AF_INET & AF_INET6 */ -#define inet_address_port(x) ((x)->sai.sin_port) +#define inet_address_port(x) \ + ((((x)->sai.sin_family == AF_INET) || \ + ((x)->sai.sin_family == AF_INET6)) ? \ + ((x)->sai.sin_port) : -1) + +#ifdef HAVE_SYS_UN_H +#define localaddrlen(data) \ + ((((unsigned char*)(data))[0] == INET_AF_LOCAL) ? \ + (1 + 1 + ((unsigned char*)(data))[1]) : 1) +#else +#define localaddrlen(data) (1) +#endif #if defined(HAVE_IN6) && defined(AF_INET6) -#define addrlen(family) \ - ((family == AF_INET) ? sizeof(struct in_addr) : \ - ((family == AF_INET6) ? sizeof(struct in6_addr) : 0)) +#define addrlen(data) \ + ((((unsigned char*)(data))[0] == INET_AF_INET) ? \ + (1 + 2 + 4) : \ + ((((unsigned char*)(data))[0] == INET_AF_INET6) ? \ + (1 + 2 + 16) : localaddrlen(data))) #else -#define addrlen(family) \ - ((family == AF_INET) ? sizeof(struct in_addr) : 0) +#define addrlen(data) \ + ((((unsigned char*)(data))[0] == INET_AF_INET) ? \ + (1 + 2 + 4) : localaddrlen(data)) #endif typedef struct _multi_timer_data { @@ -999,8 +1034,10 @@ typedef struct { inet_address peer_addr; /* fake peer address */ inet_address name_addr; /* fake local address */ - inet_address* peer_ptr; /* fake peername or NULL */ - inet_address* name_ptr; /* fake sockname or NULL */ + inet_address* peer_ptr; /* fake peername or NULL */ + inet_address* name_ptr; /* fake sockname or NULL */ + SOCKLEN_T peer_addr_len; /* fake peername size */ + SOCKLEN_T name_addr_len; /* fake sockname size */ int bufsz; /* minimum buffer constraint */ unsigned int hsz; /* the list header size, -1 is large !!! */ @@ -1246,6 +1283,8 @@ static int async_ref = 0; /* async reference id generator */ } while (0) static ErlDrvTermData am_ok; +static ErlDrvTermData am_undefined; +static ErlDrvTermData am_unspec; static ErlDrvTermData am_tcp; static ErlDrvTermData am_error; static ErlDrvTermData am_einval; @@ -1262,6 +1301,7 @@ static ErlDrvTermData am_ssl_tls; static ErlDrvTermData am_udp; static ErlDrvTermData am_udp_passive; static ErlDrvTermData am_udp_error; +static ErlDrvTermData am_local; #endif #ifdef HAVE_SCTP static ErlDrvTermData am_sctp; @@ -1471,40 +1511,67 @@ static void *realloc_wrapper(void *current, ErlDrvSizeT size){ #endif #ifdef HAVE_UDP -static int load_ip_port(ErlDrvTermData* spec, int i, char* buf) -{ - spec[i++] = ERL_DRV_INT; - spec[i++] = (ErlDrvTermData) get_int16(buf); - return i; -} - -static int load_ip_address(ErlDrvTermData* spec, int i, int family, char* buf) +static int load_address(ErlDrvTermData* spec, int i, char* buf) { int n; - if (family == AF_INET) { - for (n = 0; n < 4; n++) { + switch (*buf++) { /* Family */ + case INET_AF_INET: { + for (n = 2; n < 2+4; n++) { spec[i++] = ERL_DRV_INT; spec[i++] = (ErlDrvTermData) ((unsigned char)buf[n]); } spec[i++] = ERL_DRV_TUPLE; spec[i++] = 4; + spec[i++] = ERL_DRV_INT; + spec[i++] = (ErlDrvTermData) get_int16(buf); + break; } #if defined(HAVE_IN6) && defined(AF_INET6) - else if (family == AF_INET6) { - for (n = 0; n < 16; n += 2) { + case INET_AF_INET6: { + for (n = 2; n < 2+16; n += 2) { spec[i++] = ERL_DRV_INT; spec[i++] = (ErlDrvTermData) get_int16(buf+n); } spec[i++] = ERL_DRV_TUPLE; spec[i++] = 8; + spec[i++] = ERL_DRV_INT; + spec[i++] = (ErlDrvTermData) get_int16(buf); + break; } #endif - else { +#ifdef HAVE_SYS_UN_H + case INET_AF_LOCAL: { + int len = *(unsigned char*)buf++; + i = LOAD_ATOM(spec, i, am_local); + i = LOAD_BUF2BINARY(spec, i, buf, len); + spec[i++] = ERL_DRV_TUPLE; + spec[i++] = 2; + spec[i++] = ERL_DRV_INT; + spec[i++] = 0; + break; + } +#endif + case INET_AF_UNSPEC: { + i = LOAD_ATOM(spec, i, am_unspec); + i = LOAD_BUF2BINARY(spec, i, buf, 0); spec[i++] = ERL_DRV_TUPLE; + spec[i++] = 2; + spec[i++] = ERL_DRV_INT; spec[i++] = 0; + break; + } + default: { /* INET_AF_UNDEFINED */ + i = LOAD_ATOM(spec, i, am_undefined); + i = LOAD_BUF2BINARY(spec, i, buf, 0); + spec[i++] = ERL_DRV_TUPLE; + spec[i++] = 2; + spec[i++] = ERL_DRV_INT; + spec[i++] = 0; + break; + } } return i; -} + } #endif @@ -1512,10 +1579,13 @@ static int load_ip_address(ErlDrvTermData* spec, int i, int family, char* buf) /* For SCTP, we often need to return {IP, Port} tuples: */ static int inet_get_address(char* dst, inet_address* src, unsigned int* len); -#define LOAD_IP_AND_PORT_CNT \ +/* Max of {{int()*8},int()} | {{int()*4},int()} | + * {{'local',binary()},int()} + */ +#define LOAD_INET_GET_ADDRESS_CNT \ (8*LOAD_INT_CNT + LOAD_TUPLE_CNT + LOAD_INT_CNT + LOAD_TUPLE_CNT) -static int load_ip_and_port +static int load_inet_get_address (ErlDrvTermData* spec, int i, inet_descriptor* desc, struct sockaddr_storage* addr) { @@ -1533,8 +1603,7 @@ static int load_ip_and_port /* NB: the following functions are safe to use, as they create tuples of copied Ints on the "spec", and do not install any String pts -- a ptr to "abuf" would be dangling upon exiting this function: */ - i = load_ip_address(spec, i, desc->sfamily, abuf+3); - i = load_ip_port (spec, i, abuf+1); + i = load_address(spec, i, abuf); /* IP,Port | Family,Addr */ i = LOAD_TUPLE (spec, i, 2); return i; } @@ -2226,7 +2295,6 @@ static ErlDrvTermData am_http_error; static ErlDrvTermData am_abs_path; static ErlDrvTermData am_absoluteURI; static ErlDrvTermData am_star; -static ErlDrvTermData am_undefined; static ErlDrvTermData am_http; static ErlDrvTermData am_https; static ErlDrvTermData am_scheme; @@ -2917,7 +2985,7 @@ static int sctp_parse_async_event ASSERT(sptr->spc_length <= sz); /* No buffer overrun */ i = LOAD_ATOM (spec, i, am_sctp_paddr_change); - i = load_ip_and_port(spec, i, desc, &sptr->spc_aaddr); + i = load_inet_get_address(spec, i, desc, &sptr->spc_aaddr); switch (sptr->spc_state) { @@ -3344,10 +3412,11 @@ static int tcp_error_message(tcp_descriptor* desc, int err) #ifdef HAVE_UDP /* ** active mode message: -** {udp, S, IP, Port, [H1,...Hsz | Data]} or -** {sctp, S, IP, Port, {[AncilData], Event_or_Data}} +** {udp, S, IP, Port, [H1,...Hsz | Data]} or +** {sctp, S, IP, Port, {[AncilData], Event_or_Data}} ** where ** [H1,...,HSz] are msg headers (without IP/Port, UDP only), +** [AddrLen, H2,...,HSz] are msg headers for UDP AF_UNIX only ** Data : List() | Binary() */ static int packet_binary_message @@ -3357,6 +3426,7 @@ static int packet_binary_message ErlDrvTermData spec [PACKET_ERL_DRV_TERM_DATA_LEN]; int i = 0; int alen; + char* data = bin->orig_bytes+offs; DEBUGF(("packet_binary_message(%ld): len = %d\r\n", (long)desc->port, len)); @@ -3366,13 +3436,12 @@ static int packet_binary_message i = LOAD_ATOM(spec, i, am_udp ); /* UDP only */ # endif i = LOAD_PORT(spec, i, desc->dport); /* S */ - - alen = addrlen(desc->sfamily); - i = load_ip_address(spec, i, desc->sfamily, bin->orig_bytes+offs+3); - i = load_ip_port(spec, i, bin->orig_bytes+offs+1); /* IP, Port */ - - offs += (alen + 3); - len -= (alen + 3); + + alen = addrlen(data); + i = load_address(spec, i, data); /* IP,Port | Family,Addr */ + + offs += alen; + len -= alen; # ifdef HAVE_SCTP if (!IS_SCTP(desc)) @@ -3759,6 +3828,8 @@ static int inet_init() # endif INIT_ATOM(ok); + INIT_ATOM(undefined); + INIT_ATOM(unspec); INIT_ATOM(tcp); #ifdef HAVE_UDP INIT_ATOM(udp); @@ -3775,6 +3846,7 @@ static int inet_init() #ifdef HAVE_UDP INIT_ATOM(udp_passive); INIT_ATOM(udp_error); + INIT_ATOM(local); #endif INIT_ATOM(empty_out_q); INIT_ATOM(ssl_tls); @@ -3787,7 +3859,6 @@ static int inet_init() INIT_ATOM(abs_path); INIT_ATOM(absoluteURI); am_star = driver_mk_atom("*"); - INIT_ATOM(undefined); INIT_ATOM(http); INIT_ATOM(https); INIT_ATOM(scheme); @@ -3883,7 +3954,7 @@ static int inet_init() /* -** Set a inaddr structure: +** Set an inaddr structure: ** src = [P1,P0,X1,X2,.....] ** dst points to a structure large enugh to keep any kind ** of inaddr. @@ -3923,6 +3994,18 @@ static char* inet_set_address(int family, inet_address* dst, return src + 2+16; } #endif +#ifdef HAVE_SYS_UN_H + else if ((family == AF_UNIX) && (*len >= 1)) { + int n = *((unsigned char*)src); + if ((*len < 1+n) || (sizeof(dst->sal.sun_path) < n+1)) + return NULL; + sys_memzero((char*)dst, sizeof(struct sockaddr_un)); + dst->sal.sun_family = family; + sys_memcpy(dst->sal.sun_path, src+1, n); + *len = offsetof(struct sockaddr_un, sun_path) + n; + return src + 1 + n; + } +#endif return NULL; } @@ -3931,7 +4014,7 @@ static char* inet_set_address(int family, inet_address* dst, ** or from argument if source data specifies constant address. ** ** src = [TAG,P1,P0] when TAG = INET_AF_ANY | INET_AF_LOOPBACK -** src = [TAG,P1,P0,X1,X2,...] when TAG = INET_AF_INET | INET_AF_INET6 +** src = [TAG,P1,P0,X1,X2,...] when TAG = INET_AF_INET | INET_AF_INET6 | INET_AF_LOCAL */ static char *inet_set_faddress(int family, inet_address* dst, char *src, ErlDrvSizeT* len) { @@ -3949,6 +4032,12 @@ static char *inet_set_faddress(int family, inet_address* dst, family = AF_INET6; break; # endif +# ifdef HAVE_SYS_UN_H + case INET_AF_LOCAL: { + family = AF_UNIX; + break; + } +# endif case INET_AF_ANY: case INET_AF_LOOPBACK: { int port; @@ -4012,7 +4101,6 @@ static char *inet_set_faddress(int family, inet_address* dst, return inet_set_address(family, dst, src, len); } - /* Get a inaddr structure ** src = inaddr structure ** *len is the lenght of structure @@ -4023,6 +4111,7 @@ static char *inet_set_faddress(int family, inet_address* dst, */ static int inet_get_address(char* dst, inet_address* src, unsigned int* len) { + /* Compare the code with inet_address_to_erlang() */ int family; short port; @@ -4045,6 +4134,36 @@ static int inet_get_address(char* dst, inet_address* src, unsigned int* len) return 0; } #endif +#ifdef HAVE_SYS_UN_H + else if (family == AF_UNIX) { + size_t n, m; + if (*len < offsetof(struct sockaddr_un, sun_path)) return -1; + n = *len - offsetof(struct sockaddr_un, sun_path); + if (255 < n) return -1; + /* Portability fix: Assume that the address is a zero terminated + * string, except when the first byte is \0 i.e the + * string length is 0. Then use the reported length instead. + * This fix handles Linux's abstract socket address + * nonportable extension. + */ + m = my_strnlen(src->sal.sun_path, n); + if ((m == 0) && is_nonzero(src->sal.sun_path, n)) + m = n; + dst[0] = INET_AF_LOCAL; + dst[1] = (char) ((unsigned char) m); + sys_memcpy(dst+2, src->sal.sun_path, m); + *len = 1 + 1 + m; + return 0; + } +#endif + else if (family == AF_UNSPEC) { + dst[0] = INET_AF_UNSPEC; + *len = 1; + } + else { + dst[0] = INET_AF_UNDEFINED; + *len = 1; + } return -1; } @@ -4053,7 +4172,9 @@ static int inet_get_address(char* dst, inet_address* src, unsigned int* len) ** according to the size of the current, ** and return the resulting encoded size */ -static int inet_address_to_erlang(char *dst, inet_address **src) { +static int +inet_address_to_erlang(char *dst, inet_address **src, SOCKLEN_T sz) { + /* Compare the code with inet_get_address() */ short port; switch ((*src)->sa.sa_family) { @@ -4078,6 +4199,30 @@ static int inet_address_to_erlang(char *dst, inet_address **src) { (*src) = (inet_address *) (&(*src)->sai6 + 1); return 1 + 2 + 16; #endif +#ifdef HAVE_SYS_UN_H + case AF_UNIX: { + size_t n, m; + if (sz < offsetof(struct sockaddr_un, sun_path)) return -1; + n = sz - offsetof(struct sockaddr_un, sun_path); + if (255 < n) return -1; + /* Portability fix: Assume that the address is a zero terminated + * string, except when the first byte is \0 i.e the + * string length is 0. Then use the reported length instead. + * This fix handles Linux's abstract socket address + * nonportable extension. + */ + m = my_strnlen((*src)->sal.sun_path, n); + if ((m == 0) && is_nonzero((*src)->sal.sun_path, n)) + m = n; + if (dst) { + dst[0] = INET_AF_LOCAL; + dst[1] = (char) ((unsigned char) m); + sys_memcpy(dst+2, (*src)->sal.sun_path, m); + } + (*src) = (inet_address *) (&(*src)->sal + 1); + return 1 + 1 + m; + } +#endif default: return -1; } @@ -4086,7 +4231,7 @@ static int inet_address_to_erlang(char *dst, inet_address **src) { /* Encode n encoded addresses from addrs in the result buffer */ static ErlDrvSizeT reply_inet_addrs -(int n, inet_address *addrs, char **rbuf, ErlDrvSizeT rsize) { +(int n, inet_address *addrs, char **rbuf, ErlDrvSizeT rsize, SOCKLEN_T sz) { inet_address *ia; int i, s; ErlDrvSizeT rlen; @@ -4094,11 +4239,19 @@ static ErlDrvSizeT reply_inet_addrs if (IS_SOCKET_ERROR(n)) return ctl_error(sock_errno(), rbuf, rsize); if (n == 0) return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize); + /* The sz argument is only used when we have got an actual size + * of addrs[0] from e.g getsockname() and then n == 1 + * so we will loop over 1 element below. Otherwise sz + * would be expected to differ between addresses but that + * can only happen for AF_UNIX and we will only be called with + * n > 1 for SCTP and that will never (?) happen with AF_UNIX + */ + /* Calculate result length */ rlen = 1; ia = addrs; for (i = 0; i < n; i++) { - s = inet_address_to_erlang(NULL, &ia); + s = inet_address_to_erlang(NULL, &ia, sz); if (s < 0) break; rlen += s; } @@ -4109,7 +4262,7 @@ static ErlDrvSizeT reply_inet_addrs rlen = 1; ia = addrs; for (i = 0; i < n; i++) { - s = inet_address_to_erlang((*rbuf)+rlen, &ia); + s = inet_address_to_erlang((*rbuf)+rlen, &ia, sz); if (s < 0) break; rlen += s; } @@ -4180,6 +4333,7 @@ static ErlDrvSSizeT inet_ctl_open(inet_descriptor* desc, int domain, int type, char** rbuf, ErlDrvSizeT rsize) { int save_errno; + int protocol; #ifdef HAVE_SETNS int current_ns, new_ns; current_ns = new_ns = 0; @@ -4218,7 +4372,11 @@ static ErlDrvSSizeT inet_ctl_open(inet_descriptor* desc, int domain, int type, } } #endif - if ((desc->s = sock_open(domain, type, desc->sprotocol)) == INVALID_SOCKET) + protocol = desc->sprotocol; +#ifdef HAVE_SYS_UN_H + if (domain == AF_UNIX) protocol = 0; +#endif + if ((desc->s = sock_open(domain, type, protocol)) == INVALID_SOCKET) save_errno = sock_errno(); #ifdef HAVE_SETNS if (desc->netns != NULL) { @@ -4272,10 +4430,12 @@ static ErlDrvSSizeT inet_ctl_fdopen(inet_descriptor* desc, int domain, int type, char** rbuf, ErlDrvSizeT rsize) { inet_address name; - unsigned int sz = sizeof(name); + unsigned int sz; if (bound) { /* check that it is a socket and that the socket is bound */ + sz = sizeof(name); + sys_memzero((char *) &name, sz); if (IS_SOCKET_ERROR(sock_name(s, (struct sockaddr*) &name, &sz))) return ctl_error(sock_errno(), rbuf, rsize); if (name.sa.sa_family != domain) @@ -4290,10 +4450,7 @@ static ErlDrvSSizeT inet_ctl_fdopen(inet_descriptor* desc, int domain, int type, driver_select(desc->port, desc->event, ERL_DRV_READ, 1); #endif - if (bound) - desc->state = INET_STATE_BOUND; - else - desc->state = INET_STATE_OPEN; + desc->state = INET_STATE_OPEN; if (type == SOCK_STREAM) { /* check if connected */ sz = sizeof(name); @@ -7119,14 +7276,14 @@ static ErlDrvSSizeT inet_fill_opts(inet_descriptor* desc, #ifdef HAVE_SCTP #define LOAD_PADDRINFO_CNT \ - (2*LOAD_ATOM_CNT + LOAD_ASSOC_ID_CNT + LOAD_IP_AND_PORT_CNT + \ + (2*LOAD_ATOM_CNT + LOAD_ASSOC_ID_CNT + LOAD_INET_GET_ADDRESS_CNT + \ 4*LOAD_INT_CNT + LOAD_TUPLE_CNT) static int load_paddrinfo (ErlDrvTermData * spec, int i, inet_descriptor* desc, struct sctp_paddrinfo* pai) { i = LOAD_ATOM (spec, i, am_sctp_paddrinfo); i = LOAD_ASSOC_ID (spec, i, pai->spinfo_assoc_id); - i = load_ip_and_port(spec, i, desc, &pai->spinfo_address); + i = load_inet_get_address(spec, i, desc, &pai->spinfo_address); switch(pai->spinfo_state) { case SCTP_ACTIVE: @@ -7542,7 +7699,7 @@ static ErlDrvSSizeT sctp_fill_opts(inet_descriptor* desc, /* Fill in the response: */ PLACE_FOR(spec, i, 2*LOAD_ATOM_CNT + LOAD_ASSOC_ID_CNT + - LOAD_IP_AND_PORT_CNT + 2*LOAD_TUPLE_CNT); + LOAD_INET_GET_ADDRESS_CNT + 2*LOAD_TUPLE_CNT); switch (eopt) { case SCTP_OPT_PRIMARY_ADDR: i = LOAD_ATOM(spec, i, am_sctp_primary_addr); @@ -7556,7 +7713,7 @@ static ErlDrvSSizeT sctp_fill_opts(inet_descriptor* desc, ASSERT(0); } i = LOAD_ASSOC_ID (spec, i, sp.sspp_assoc_id); - i = load_ip_and_port(spec, i, desc, &sp.sspp_addr); + i = load_inet_get_address(spec, i, desc, &sp.sspp_addr); i = LOAD_TUPLE (spec, i, 3); i = LOAD_TUPLE (spec, i, 2); break; @@ -7603,11 +7760,11 @@ static ErlDrvSSizeT sctp_fill_opts(inet_descriptor* desc, /* Fill in the response: */ PLACE_FOR(spec, i, 2*LOAD_ATOM_CNT + LOAD_ASSOC_ID_CNT + - LOAD_IP_AND_PORT_CNT + 4*LOAD_INT_CNT); + LOAD_INET_GET_ADDRESS_CNT + 4*LOAD_INT_CNT); i = LOAD_ATOM (spec, i, am_sctp_peer_addr_params); i = LOAD_ATOM (spec, i, am_sctp_paddrparams); i = LOAD_ASSOC_ID (spec, i, ap.spp_assoc_id); - i = load_ip_and_port(spec, i, desc, &ap.spp_address); + i = load_inet_get_address(spec, i, desc, &ap.spp_address); i = LOAD_INT (spec, i, ap.spp_hbinterval); i = LOAD_INT (spec, i, ap.spp_pathmaxrxt); @@ -8248,6 +8405,11 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, put_int32(INET_AF_INET6, &tbuf[0]); } #endif +#ifdef HAVE_SYS_UN_H + else if (desc->sfamily == AF_UNIX) { + put_int32(INET_AF_LOCAL, &tbuf[0]); + } +#endif else return ctl_error(EINVAL, rbuf, rsize); @@ -8296,7 +8458,6 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, if (len != 4) return ctl_error(EINVAL, rbuf, rsize); if (! IS_OPEN(desc)) return ctl_xerror(EXBADPORT, rbuf, rsize); - if (! IS_BOUND(desc)) return ctl_xerror(EXBADSEQ, rbuf, rsize); #ifdef HAVE_SCTP if (IS_SCTP(desc) && p_sctp_getpaddrs) { @@ -8307,19 +8468,19 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, assoc_id = get_int32(buf); n = p_sctp_getpaddrs(desc->s, assoc_id, &sa); - rlen = reply_inet_addrs(n, (inet_address *) sa, rbuf, rsize); + rlen = reply_inet_addrs(n, (inet_address *) sa, rbuf, rsize, 0); if (n > 0) p_sctp_freepaddrs(sa); return rlen; } #endif { /* Fallback to sock_peer */ inet_address addr; - unsigned int sz; + SOCKLEN_T sz; int i; sz = sizeof(addr); i = sock_peer(desc->s, (struct sockaddr *) &addr, &sz); - return reply_inet_addrs(i >= 0 ? 1 : i, &addr, rbuf, rsize); + return reply_inet_addrs(i >= 0 ? 1 : i, &addr, rbuf, rsize, sz); } } @@ -8327,15 +8488,21 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, char tbuf[sizeof(inet_address)]; inet_address peer; inet_address* ptr; - unsigned int sz = sizeof(peer); + unsigned int sz; DEBUGF(("inet_ctl(%ld): PEER\r\n", (long)desc->port)); if (!(desc->state & INET_F_ACTIVE)) return ctl_error(ENOTCONN, rbuf, rsize); - if ((ptr = desc->peer_ptr) == NULL) { + if ((ptr = desc->peer_ptr) != NULL) { + sz = desc->peer_addr_len; + } + else { ptr = &peer; - if (IS_SOCKET_ERROR(sock_peer(desc->s, (struct sockaddr*)ptr,&sz))) + sz = sizeof(peer); + if (IS_SOCKET_ERROR + (sock_peer + (desc->s, (struct sockaddr*)ptr, &sz))) return ctl_error(sock_errno(), rbuf, rsize); } if (inet_get_address(tbuf, ptr, &sz) < 0) @@ -8350,11 +8517,12 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, } else if (len < 2) return ctl_error(EINVAL, rbuf, rsize); - else if (inet_set_address(desc->sfamily, &desc->peer_addr, - buf, &len) == NULL) + else if (inet_set_faddress + (desc->sfamily, &desc->peer_addr, buf, &len) == NULL) return ctl_error(EINVAL, rbuf, rsize); else { desc->peer_ptr = &desc->peer_addr; + desc->peer_addr_len = (SOCKLEN_T) len; return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize); } } @@ -8365,7 +8533,6 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, if (len != 4) return ctl_error(EINVAL, rbuf, rsize); if (! IS_OPEN(desc)) return ctl_xerror(EXBADPORT, rbuf, rsize); - if (! IS_BOUND(desc)) return ctl_xerror(EXBADSEQ, rbuf, rsize); #ifdef HAVE_SCTP if (IS_SCTP(desc) && p_sctp_getladdrs) { @@ -8376,19 +8543,20 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, assoc_id = get_int32(buf); n = p_sctp_getladdrs(desc->s, assoc_id, &sa); - rlen = reply_inet_addrs(n, (inet_address *) sa, rbuf, rsize); + rlen = reply_inet_addrs(n, (inet_address *) sa, rbuf, rsize, 0); if (n > 0) p_sctp_freeladdrs(sa); return rlen; } #endif { /* Fallback to sock_name */ inet_address addr; - unsigned int sz; + SOCKLEN_T sz; int i; sz = sizeof(addr); + sys_memzero((char *) &addr, sz); i = sock_name(desc->s, (struct sockaddr *) &addr, &sz); - return reply_inet_addrs(i >= 0 ? 1 : i, &addr, rbuf, rsize); + return reply_inet_addrs(i >= 0 ? 1 : i, &addr, rbuf, rsize, sz); } } @@ -8396,16 +8564,19 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, char tbuf[sizeof(inet_address)]; inet_address name; inet_address* ptr; - unsigned int sz = sizeof(name); + unsigned int sz; DEBUGF(("inet_ctl(%ld): NAME\r\n", (long)desc->port)); - if (!IS_BOUND(desc)) - return ctl_error(EINVAL, rbuf, rsize); /* address is not valid */ - - if ((ptr = desc->name_ptr) == NULL) { + if ((ptr = desc->name_ptr) != NULL) { + sz = desc->name_addr_len; + } + else { ptr = &name; - if (IS_SOCKET_ERROR(sock_name(desc->s, (struct sockaddr*)ptr, &sz))) + sz = sizeof(name); + sys_memzero((char *) &name, sz); + if (IS_SOCKET_ERROR + (sock_name(desc->s, (struct sockaddr*)ptr, &sz))) return ctl_error(sock_errno(), rbuf, rsize); } if (inet_get_address(tbuf, ptr, &sz) < 0) @@ -8413,18 +8584,19 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, return ctl_reply(INET_REP_OK, tbuf, sz, rbuf, rsize); } - case INET_REQ_SETNAME: { /* set fake peername Port Address */ + case INET_REQ_SETNAME: { /* set fake sockname Port Address */ if (len == 0) { desc->name_ptr = NULL; return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize); } else if (len < 2) return ctl_error(EINVAL, rbuf, rsize); - else if (inet_set_address(desc->sfamily, &desc->name_addr, - buf, &len) == NULL) + else if (inet_set_faddress + (desc->sfamily, &desc->name_addr, buf, &len) == NULL) return ctl_error(EINVAL, rbuf, rsize); else { desc->name_ptr = &desc->name_addr; + desc->name_addr_len = (SOCKLEN_T) len; return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize); } } @@ -8432,7 +8604,7 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, case INET_REQ_BIND: { /* bind socket */ char tbuf[2]; inet_address local; - short port; + int port; DEBUGF(("inet_ctl(%ld): BIND\r\n", (long)desc->port)); @@ -8447,15 +8619,17 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, if (IS_SOCKET_ERROR(sock_bind(desc->s,(struct sockaddr*) &local, len))) return ctl_error(sock_errno(), rbuf, rsize); - desc->state = INET_STATE_BOUND; + desc->state = INET_STATE_OPEN; - if ((port = inet_address_port(&local)) == 0) { + port = inet_address_port(&local); + if (port == 0) { SOCKLEN_T adrlen = sizeof(local); + sys_memzero((char *) &local, adrlen); sock_name(desc->s, &local.sa, &adrlen); port = inet_address_port(&local); } - port = sock_ntohs(port); - put_int16(port, tbuf); + else if (port == -1) port = 0; + put_int16(sock_ntohs((Uint16) port), tbuf); return ctl_reply(INET_REP_OK, tbuf, 2, rbuf, rsize); } @@ -8912,6 +9086,11 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, return ctl_xerror("eafnosupport", rbuf, rsize); break; #endif +#ifdef HAVE_SYS_UN_H + case INET_AF_LOCAL: + domain = AF_UNIX; + break; +#endif default: return ctl_error(EINVAL, rbuf, rsize); } @@ -8938,6 +9117,11 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, return ctl_xerror("eafnosupport", rbuf, rsize); break; #endif +#ifdef HAVE_SYS_UN_H + case INET_AF_LOCAL: + domain = AF_UNIX; + break; +#endif default: return ctl_error(EINVAL, rbuf, rsize); } @@ -8960,8 +9144,6 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, return ctl_xerror(EXBADPORT, rbuf, rsize); if (!IS_OPEN(INETP(desc))) return ctl_xerror(EXBADPORT, rbuf, rsize); - if (!IS_BOUND(INETP(desc))) - return ctl_xerror(EXBADSEQ, rbuf, rsize); if (len != 2) return ctl_error(EINVAL, rbuf, rsize); backlog = get_int16(buf); @@ -8984,8 +9166,6 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, return ctl_xerror(EXBADPORT, rbuf, rsize); if (IS_CONNECTED(INETP(desc))) return ctl_error(EISCONN, rbuf, rsize); - if (!IS_BOUND(INETP(desc))) - return ctl_xerror(EXBADSEQ, rbuf, rsize); if (IS_CONNECTING(INETP(desc))) return ctl_error(EINVAL, rbuf, rsize); if (len < 6) @@ -8993,8 +9173,8 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, timeout = get_int32(buf); buf += 4; len -= 4; - if (inet_set_address(desc->inet.sfamily, &desc->inet.remote, - buf, &len) == NULL) + if (inet_set_faddress + (desc->inet.sfamily, &desc->inet.remote, buf, &len) == NULL) return ctl_error(EINVAL, rbuf, rsize); code = sock_connect(desc->inet.s, @@ -9086,6 +9266,7 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, return ctl_reply(INET_REP_OK, tbuf, 2, rbuf, rsize); } else { n = sizeof(desc->inet.remote); + sys_memzero((char *) &remote, n); s = sock_accept(desc->inet.s, (struct sockaddr*) &remote, &n); if (s == INVALID_SOCKET) { if (sock_errno() == ERRNO_BLOCK) { @@ -9971,6 +10152,7 @@ static int tcp_inet_input(tcp_descriptor* desc, HANDLE event) inet_async_op *this_op = desc->inet.opt; len = sizeof(desc->inet.remote); + sys_memzero((char *) &remote, len); s = sock_accept(desc->inet.s, (struct sockaddr*) &remote, &len); if (s == INVALID_SOCKET && sock_errno() == ERRNO_BLOCK) { /* Just try again, no real error, just a ghost trigger from poll, @@ -10037,6 +10219,7 @@ static int tcp_inet_input(tcp_descriptor* desc, HANDLE event) while (desc->inet.state == INET_STATE_MULTI_ACCEPTING) { len = sizeof(desc->inet.remote); + sys_memzero((char *) &remote, len); s = sock_accept(desc->inet.s, (struct sockaddr*) &remote, &len); if (s == INVALID_SOCKET && sock_errno() == ERRNO_BLOCK) { /* Just try again, no real error, keep the last return code */ @@ -10477,7 +10660,7 @@ static int tcp_inet_output(tcp_descriptor* desc, HANDLE event) (struct sockaddr*) &desc->inet.remote, &sz); if (IS_SOCKET_ERROR(code)) { - desc->inet.state = INET_STATE_BOUND; /* restore state */ + desc->inet.state = INET_STATE_OPEN; /* restore state */ ret = async_error(INETP(desc), sock_errno()); goto done; } @@ -10490,7 +10673,7 @@ static int tcp_inet_output(tcp_descriptor* desc, HANDLE event) (void *)&error, &sz); if ((code < 0) || error) { - desc->inet.state = INET_STATE_BOUND; /* restore state */ + desc->inet.state = INET_STATE_OPEN; /* restore state */ ret = async_error(INETP(desc), error); goto done; } @@ -10777,6 +10960,9 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, return ctl_xerror("eafnosupport", rbuf, rsize); break; #endif +#ifdef HAVE_SYS_UN_H + case INET_AF_LOCAL: af = AF_UNIX; break; +#endif default: return ctl_error(EINVAL, rbuf, rsize); } @@ -10827,6 +11013,9 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, return ctl_xerror("eafnosupport", rbuf, rsize); break; #endif +#ifdef HAVE_SYS_UN_H + case INET_AF_LOCAL: af = AF_UNIX; break; +#endif default: return ctl_error(EINVAL, rbuf, rsize); } @@ -10893,8 +11082,6 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, if (!IS_OPEN(desc)) return ctl_xerror(EXBADPORT, rbuf, rsize); - if (!IS_BOUND(desc)) - return ctl_xerror(EXBADSEQ, rbuf, rsize); #ifdef HAVE_SCTP if (IS_SCTP(desc)) { inet_address remote; @@ -10909,7 +11096,7 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, /* For SCTP, we do not set the peer's addr in desc->remote, as multiple peers are possible: */ - if (inet_set_address(desc->sfamily, &remote, buf, &len) == NULL) + if (inet_set_faddress(desc->sfamily, &remote, buf, &len) == NULL) return ctl_error(EINVAL, rbuf, rsize); sock_select(desc, FD_CONNECT, 1); @@ -10949,8 +11136,8 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, /* Ignore timeout */ buf += 4; len -= 4; - if (inet_set_address(desc->sfamily, - &desc->remote, buf, &len) == NULL) + if (inet_set_faddress + (desc->sfamily, &desc->remote, buf, &len) == NULL) return ctl_error(EINVAL, rbuf, rsize); code = sock_connect(desc->s, @@ -10981,8 +11168,6 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, return ctl_xerror(EXBADPORT, rbuf, rsize); if (!IS_OPEN(desc)) return ctl_xerror(EXBADPORT, rbuf, rsize); - if (!IS_BOUND(desc)) - return ctl_xerror(EXBADSEQ, rbuf, rsize); if (len != 2) return ctl_error(EINVAL, rbuf, rsize); @@ -11029,7 +11214,7 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, return ctl_error(sock_errno(), rbuf, rsize); } - desc->state = INET_STATE_BOUND; + desc->state = INET_STATE_OPEN; return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize); } @@ -11046,8 +11231,6 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, return ctl_xerror(EXBADPORT, rbuf, rsize); if (!IS_OPEN(desc)) return ctl_xerror(EXBADPORT, rbuf, rsize); - if (!IS_BOUND(desc)) - return ctl_xerror(EXBADSEQ, rbuf, rsize); if (! p_sctp_peeloff) return ctl_error(ENOTSUP, rbuf, rsize); @@ -11088,8 +11271,6 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, /* INPUT: Timeout(4), Length(4) */ if (!IS_OPEN(desc)) return ctl_xerror(EXBADPORT, rbuf, rsize); - if (!IS_BOUND(desc)) - return ctl_error(EINVAL, rbuf, rsize); if (desc->active || (len != 8)) return ctl_error(EINVAL, rbuf, rsize); timeout = get_int32(buf); @@ -11130,12 +11311,12 @@ static void packet_inet_timeout(ErlDrvData e) /* THIS IS A "send*" REQUEST; on the Erlang side: "port_command". -** input should be: P1 P0 Address buffer . +** input should be: Family Address buffer . ** For UDP, buffer (after Address) is just data to be sent. ** For SCTP, buffer contains a list representing 2 items: ** (1) 6 parms for sctp_sndrcvinfo, as in sctp_get_sendparams(); ** (2) 0+ real data bytes. -** There is no destination address -- SCTYP send is performed over +** There is no destination address -- SCTP send is performed over ** an existing association, using "sctp_sndrcvinfo" specified. */ static void packet_inet_command(ErlDrvData e, char* buf, ErlDrvSizeT len) @@ -11154,10 +11335,6 @@ static void packet_inet_command(ErlDrvData e, char* buf, ErlDrvSizeT len) inet_reply_error(desc, EINVAL); return; } - if (!IS_BOUND(desc)) { - inet_reply_error(desc, EINVAL); - return; - } #ifdef HAVE_SCTP if (IS_SCTP(desc)) @@ -11210,7 +11387,7 @@ static void packet_inet_command(ErlDrvData e, char* buf, ErlDrvSizeT len) /* UDP socket. Even if it is connected, there is an address prefix here -- ignored for connected sockets: */ sz = len; - qtr = inet_set_address(desc->sfamily, &other, ptr, &sz); + qtr = inet_set_faddress(desc->sfamily, &other, ptr, &sz); if (qtr == NULL) { inet_reply_error(desc, EINVAL); return; @@ -11288,6 +11465,8 @@ static int packet_inet_input(udp_descriptor* udesc, HANDLE event) while(packet_count--) { unsigned int len = sizeof(other); + sys_memzero((char *) &other, sizeof(other)); + /* udesc->i_buf is only kept between SCTP fragments */ if (udesc->i_buf == NULL) { udesc->i_bufsz = desc->bufsz + len; @@ -11483,7 +11662,7 @@ static int packet_inet_output(udp_descriptor* udesc, HANDLE event) (struct sockaddr*) &desc->remote, &sz); if (IS_SOCKET_ERROR(code)) { - desc->state = INET_STATE_BOUND; /* restore state */ + desc->state = INET_STATE_OPEN; /* restore state */ ret = async_error(desc, sock_errno()); goto done; } @@ -11496,7 +11675,7 @@ static int packet_inet_output(udp_descriptor* udesc, HANDLE event) (void *)&error, &sz); if ((code < 0) || error) { - desc->state = INET_STATE_BOUND; /* restore state */ + desc->state = INET_STATE_OPEN; /* restore state */ ret = async_error(desc, error); goto done; } @@ -11809,9 +11988,8 @@ int erts_sock_connect(erts_sock_t socket, byte *ip_addr, int len, Uint16 port) if (!inet_set_address(AF_INET, &addr, buf, &blen)) return 0; - if (IS_SOCKET_ERROR(sock_connect(s, - (struct sockaddr *) &addr, - sizeof(struct sockaddr_in)))) + if (IS_SOCKET_ERROR + (sock_connect(s, (struct sockaddr *) &addr, blen))) return 0; return 1; } diff --git a/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c b/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c index e38bececde..d92933a096 100644 --- a/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c +++ b/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c @@ -48,7 +48,8 @@ static ERL_NIF_TERM dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[ char s[10]; ErlNifBinary b; if (have_dirty_schedulers()) { - assert(enif_is_on_dirty_scheduler(env)); + assert(ERL_NIF_THR_DIRTY_CPU_SCHEDULER == enif_thread_type() + || ERL_NIF_THR_DIRTY_IO_SCHEDULER == enif_thread_type()); } assert(argc == 3); enif_get_int(env, argv[0], &n); @@ -65,7 +66,7 @@ static ERL_NIF_TERM call_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM int n; char s[10]; ErlNifBinary b; - assert(!enif_is_on_dirty_scheduler(env)); + assert(ERL_NIF_THR_NORMAL_SCHEDULER == enif_thread_type()); if (argc != 3) return enif_make_badarg(env); if (have_dirty_schedulers()) { @@ -151,7 +152,8 @@ dirty_sleeper(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) ErlNifPid pid; ErlNifEnv* msg_env = NULL; - assert(enif_is_on_dirty_scheduler(env)); + assert(ERL_NIF_THR_DIRTY_CPU_SCHEDULER == enif_thread_type() + || ERL_NIF_THR_DIRTY_IO_SCHEDULER == enif_thread_type()); /* If we get a pid argument, it indicates a process involved in the test wants a message from us. Prior to the sleep we send a 'ready' @@ -221,7 +223,8 @@ static ERL_NIF_TERM dirty_heap_access_nif(ErlNifEnv* env, int argc, const ERL_NI { ERL_NIF_TERM res = enif_make_list(env, 0); int i; - assert(enif_is_on_dirty_scheduler(env)); + assert(ERL_NIF_THR_DIRTY_CPU_SCHEDULER == enif_thread_type() + || ERL_NIF_THR_DIRTY_IO_SCHEDULER == enif_thread_type()); for (i = 0; i < 1000; i++) res = enif_make_list_cell(env, enif_make_copy(env, argv[0]), res); diff --git a/erts/emulator/test/lttng_SUITE.erl b/erts/emulator/test/lttng_SUITE.erl index c64ddc40da..6b7ad836f5 100644 --- a/erts/emulator/test/lttng_SUITE.erl +++ b/erts/emulator/test/lttng_SUITE.erl @@ -44,6 +44,7 @@ suite() -> all() -> [t_lttng_list, + t_memory_carrier, t_carrier_pool, t_async_io_pool, t_driver_start_stop, @@ -52,8 +53,7 @@ all() -> t_driver_timeout, t_driver_caller, t_driver_flush, - t_scheduler_poll, - t_memory_carrier]. + t_scheduler_poll]. init_per_suite(Config) -> @@ -80,34 +80,34 @@ end_per_testcase(Case, _Config) -> ok. %% Not tested yet -%% com_ericsson_otp:driver_process_exit -%% com_ericsson_otp:driver_event +%% org_erlang_otp:driver_process_exit +%% org_erlang_otp:driver_event %% tracepoints %% -%% com_ericsson_otp:carrier_pool_get -%% com_ericsson_otp:carrier_pool_put -%% com_ericsson_otp:carrier_destroy -%% com_ericsson_otp:carrier_create -%% com_ericsson_otp:aio_pool_put -%% com_ericsson_otp:aio_pool_get -%% com_ericsson_otp:driver_control -%% com_ericsson_otp:driver_call -%% com_ericsson_otp:driver_finish -%% com_ericsson_otp:driver_ready_async -%% com_ericsson_otp:driver_process_exit -%% com_ericsson_otp:driver_stop -%% com_ericsson_otp:driver_flush -%% com_ericsson_otp:driver_stop_select -%% com_ericsson_otp:driver_timeout -%% com_ericsson_otp:driver_event -%% com_ericsson_otp:driver_ready_output -%% com_ericsson_otp:driver_ready_input -%% com_ericsson_otp:driver_output -%% com_ericsson_otp:driver_outputv -%% com_ericsson_otp:driver_init -%% com_ericsson_otp:driver_start -%% com_ericsson_otp:scheduler_poll +%% org_erlang_otp:carrier_pool_get +%% org_erlang_otp:carrier_pool_put +%% org_erlang_otp:carrier_destroy +%% org_erlang_otp:carrier_create +%% org_erlang_otp:aio_pool_put +%% org_erlang_otp:aio_pool_get +%% org_erlang_otp:driver_control +%% org_erlang_otp:driver_call +%% org_erlang_otp:driver_finish +%% org_erlang_otp:driver_ready_async +%% org_erlang_otp:driver_process_exit +%% org_erlang_otp:driver_stop +%% org_erlang_otp:driver_flush +%% org_erlang_otp:driver_stop_select +%% org_erlang_otp:driver_timeout +%% org_erlang_otp:driver_event +%% org_erlang_otp:driver_ready_output +%% org_erlang_otp:driver_ready_input +%% org_erlang_otp:driver_output +%% org_erlang_otp:driver_outputv +%% org_erlang_otp:driver_init +%% org_erlang_otp:driver_start +%% org_erlang_otp:scheduler_poll %% %% Testcases @@ -117,48 +117,48 @@ t_lttng_list(_Config) -> {ok, _} = cmd("lttng list -u"), ok. -%% com_ericsson_otp:carrier_pool_get -%% com_ericsson_otp:carrier_pool_put +%% org_erlang_otp:carrier_pool_get +%% org_erlang_otp:carrier_pool_put t_carrier_pool(Config) -> case have_carriers(ets_alloc) of false -> {skip, "No Memory Carriers configured on system."}; true -> - ok = lttng_start_event("com_ericsson_otp:carrier_pool*", Config), + ok = lttng_start_event("org_erlang_otp:carrier_pool*", Config), ok = ets_load(), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_otp:carrier_pool_get", Res), - ok = check_tracepoint("com_ericsson_otp:carrier_pool_put", Res), + ok = check_tracepoint("org_erlang_otp:carrier_pool_get", Res), + ok = check_tracepoint("org_erlang_otp:carrier_pool_put", Res), ok end. -%% com_ericsson_otp:carrier_destroy -%% com_ericsson_otp:carrier_create +%% org_erlang_otp:carrier_destroy +%% org_erlang_otp:carrier_create t_memory_carrier(Config) -> case have_carriers(ets_alloc) of false -> {skip, "No Memory Carriers configured on system."}; true -> - ok = lttng_start_event("com_ericsson_otp:carrier_*", Config), + ok = lttng_start_event("org_erlang_otp:carrier_*", Config), ok = ets_load(), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_otp:carrier_destroy", Res), - ok = check_tracepoint("com_ericsson_otp:carrier_create", Res), + ok = check_tracepoint("org_erlang_otp:carrier_destroy", Res), + ok = check_tracepoint("org_erlang_otp:carrier_create", Res), ok end. -%% com_ericsson_otp:aio_pool_put -%% com_ericsson_otp:aio_pool_get +%% 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("com_ericsson_otp:aio_pool_*", Config), + ok = lttng_start_event("org_erlang_otp:aio_pool_*", Config), Path1 = proplists:get_value(priv_dir, Config), {ok, [[Path2]]} = init:get_argument(home), @@ -168,51 +168,54 @@ t_async_io_pool(Config) -> {ok, _} = file:list_dir(Path2), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_otp:aio_pool_put", Res), - ok = check_tracepoint("com_ericsson_otp:aio_pool_get", Res), + ok = check_tracepoint("org_erlang_otp:aio_pool_put", Res), + ok = check_tracepoint("org_erlang_otp:aio_pool_get", Res), ok end. -%% com_ericsson_otp:driver_start -%% com_ericsson_otp:driver_stop +%% org_erlang_otp:driver_start +%% org_erlang_otp:driver_stop t_driver_start_stop(Config) -> - ok = lttng_start_event("com_ericsson_otp:driver_*", 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), + timer:sleep(500), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_otp:driver_start", Res), - ok = check_tracepoint("com_ericsson_otp:driver_stop", Res), - ok = check_tracepoint("com_ericsson_otp:driver_control", Res), - ok = check_tracepoint("com_ericsson_otp:driver_outputv", Res), - ok = check_tracepoint("com_ericsson_otp:driver_ready_async", Res), + 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. -%% com_ericsson_otp:driver_control -%% com_ericsson_otp:driver_outputv -%% com_ericsson_otp:driver_ready_async +%% org_erlang_otp:driver_control +%% org_erlang_otp:driver_outputv +%% org_erlang_otp:driver_ready_async t_driver_control_ready_async(Config) -> - ok = lttng_start_event("com_ericsson_otp:driver_control", Config), - ok = lttng_start_event("com_ericsson_otp:driver_outputv", Config), - ok = lttng_start_event("com_ericsson_otp:driver_ready_async", 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), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_otp:driver_control", Res), - ok = check_tracepoint("com_ericsson_otp:driver_outputv", Res), - ok = check_tracepoint("com_ericsson_otp:driver_ready_async", 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. -%% com_ericsson_otp:driver_ready_input -%% com_ericsson_otp:driver_ready_output +%% org_erlang_otp:driver_ready_input +%% org_erlang_otp:driver_ready_output t_driver_ready_input_output(Config) -> - ok = lttng_start_event("com_ericsson_otp:driver_ready_*", 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), receive {Pid, accept} -> ok end, @@ -225,16 +228,17 @@ t_driver_ready_input_output(Config) -> ok = gen_tcp:close(Sock), receive {Pid, done} -> ok end, + timer:sleep(500), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_otp:driver_ready_input", Res), - ok = check_tracepoint("com_ericsson_otp:driver_ready_output", Res), + ok = check_tracepoint("org_erlang_otp:driver_ready_input", Res), + ok = check_tracepoint("org_erlang_otp:driver_ready_output", Res), ok. -%% com_ericsson_otp:driver_stop_select -%% com_ericsson_otp:driver_timeout +%% org_erlang_otp:driver_stop_select +%% org_erlang_otp:driver_timeout t_driver_timeout(Config) -> - ok = lttng_start_event("com_ericsson_otp:driver_*", Config), + ok = lttng_start_event("org_erlang_otp:driver_*", Config), Me = self(), Pid = spawn_link(fun() -> tcp_server(Me, timeout) end), receive {Pid, accept} -> ok end, @@ -243,16 +247,16 @@ t_driver_timeout(Config) -> receive {Pid, done} -> ok end, ok = gen_tcp:close(Sock), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_otp:driver_timeout", Res), - ok = check_tracepoint("com_ericsson_otp:driver_stop_select", Res), + ok = check_tracepoint("org_erlang_otp:driver_timeout", Res), + ok = check_tracepoint("org_erlang_otp:driver_stop_select", Res), ok. -%% com_ericsson_otp:driver_call -%% com_ericsson_otp:driver_output -%% com_ericsson_otp:driver_init -%% com_ericsson_otp:driver_finish +%% org_erlang_otp:driver_call +%% org_erlang_otp:driver_output +%% org_erlang_otp:driver_init +%% org_erlang_otp:driver_finish t_driver_caller(Config) -> - ok = lttng_start_event("com_ericsson_otp:driver_*", Config), + ok = lttng_start_event("org_erlang_otp:driver_*", Config), Drv = 'caller_drv', os:putenv("CALLER_DRV_USE_OUTPUTV", "false"), @@ -278,25 +282,25 @@ t_driver_caller(Config) -> erl_ddll:unload_driver(Drv), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_otp:driver_call", Res), - ok = check_tracepoint("com_ericsson_otp:driver_output", Res), - ok = check_tracepoint("com_ericsson_otp:driver_init", Res), - ok = check_tracepoint("com_ericsson_otp:driver_finish", Res), + ok = check_tracepoint("org_erlang_otp:driver_call", Res), + ok = check_tracepoint("org_erlang_otp:driver_output", Res), + ok = check_tracepoint("org_erlang_otp:driver_init", Res), + ok = check_tracepoint("org_erlang_otp:driver_finish", Res), ok. -%% com_ericsson_otp:scheduler_poll +%% org_erlang_otp:scheduler_poll t_scheduler_poll(Config) -> - ok = lttng_start_event("com_ericsson_otp:scheduler_poll", Config), + ok = lttng_start_event("org_erlang_otp:scheduler_poll", Config), ok = memory_load(), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_otp:scheduler_poll", Res), + ok = check_tracepoint("org_erlang_otp:scheduler_poll", Res), ok. -%% com_ericsson_otp:driver_flush +%% org_erlang_otp:driver_flush t_driver_flush(Config) -> - ok = lttng_start_event("com_ericsson_otp:driver_flush", Config), + ok = lttng_start_event("org_erlang_otp:driver_flush", Config), Me = self(), Pid = spawn_link(fun() -> tcp_server(Me, passive_no_read) end), @@ -320,7 +324,7 @@ t_driver_flush(Config) -> receive {Pid, done} -> ok end, Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_otp:driver_flush", Res), + ok = check_tracepoint("org_erlang_otp:driver_flush", Res), ok. %% @@ -412,29 +416,29 @@ tcp_server(Pid, Type) -> txt() -> <<"%% tracepoints\n" "%%\n" - "%% com_ericsson_otp:carrier_pool_get\n" - "%% com_ericsson_otp:carrier_pool_put\n" - "%% com_ericsson_otp:carrier_destroy\n" - "%% com_ericsson_otp:carrier_create\n" - "%% com_ericsson_otp:aio_pool_put\n" - "%% com_ericsson_otp:aio_pool_get\n" - "%% com_ericsson_otp:driver_control\n" - "%% com_ericsson_otp:driver_call\n" - "%% com_ericsson_otp:driver_finish\n" - "%% com_ericsson_otp:driver_ready_async\n" - "%% com_ericsson_otp:driver_process_exit\n" - "%% com_ericsson_otp:driver_stop\n" - "%% com_ericsson_otp:driver_flush\n" - "%% com_ericsson_otp:driver_stop_select\n" - "%% com_ericsson_otp:driver_timeout\n" - "%% com_ericsson_otp:driver_event\n" - "%% com_ericsson_otp:driver_ready_output\n" - "%% com_ericsson_otp:driver_ready_input\n" - "%% com_ericsson_otp:driver_output\n" - "%% com_ericsson_otp:driver_outputv\n" - "%% com_ericsson_otp:driver_init\n" - "%% com_ericsson_otp:driver_start\n" - "%% com_ericsson_otp:scheduler_poll">>. + "%% org_erlang_otp:carrier_pool_get\n" + "%% org_erlang_otp:carrier_pool_put\n" + "%% org_erlang_otp:carrier_destroy\n" + "%% org_erlang_otp:carrier_create\n" + "%% org_erlang_otp:aio_pool_put\n" + "%% org_erlang_otp:aio_pool_get\n" + "%% org_erlang_otp:driver_control\n" + "%% org_erlang_otp:driver_call\n" + "%% org_erlang_otp:driver_finish\n" + "%% org_erlang_otp:driver_ready_async\n" + "%% org_erlang_otp:driver_process_exit\n" + "%% org_erlang_otp:driver_stop\n" + "%% org_erlang_otp:driver_flush\n" + "%% org_erlang_otp:driver_stop_select\n" + "%% org_erlang_otp:driver_timeout\n" + "%% org_erlang_otp:driver_event\n" + "%% org_erlang_otp:driver_ready_output\n" + "%% org_erlang_otp:driver_ready_input\n" + "%% org_erlang_otp:driver_output\n" + "%% org_erlang_otp:driver_outputv\n" + "%% org_erlang_otp:driver_init\n" + "%% org_erlang_otp:driver_start\n" + "%% org_erlang_otp:scheduler_poll">>. load_driver(Dir, Driver) -> case erl_ddll:load_driver(Dir, Driver) of diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl index eaa4026a8a..4ebc1f5782 100644 --- a/erts/emulator/test/process_SUITE.erl +++ b/erts/emulator/test/process_SUITE.erl @@ -147,11 +147,7 @@ spawn_with_binaries(Config) when is_list(Config) -> TwoMeg = lists:duplicate(1024, L), Fun = fun() -> spawn(?MODULE, binary_owner, [list_to_binary(TwoMeg)]), receive after 1 -> ok end end, - Iter = case test_server:purify_is_running() of - true -> 10; - false -> 150 - end, - test_server:do_times(Iter, Fun), + test_server:do_times(150, Fun), ok. binary_owner(Bin) when is_binary(Bin) -> diff --git a/erts/etc/common/heart.c b/erts/etc/common/heart.c index e931ae4641..a4008186c4 100644 --- a/erts/etc/common/heart.c +++ b/erts/etc/common/heart.c @@ -119,6 +119,8 @@ #define HEART_COMMAND_ENV "HEART_COMMAND" #define ERL_CRASH_DUMP_SECONDS_ENV "ERL_CRASH_DUMP_SECONDS" #define HEART_KILL_SIGNAL "HEART_KILL_SIGNAL" +#define HEART_NO_KILL "HEART_NO_KILL" + #define MSG_HDR_SIZE (2) #define MSG_HDR_PLUS_OP_SIZE (3) @@ -524,6 +526,12 @@ static void kill_old_erlang(void){ HANDLE erlh; DWORD exit_code; + char* envvar = NULL; + + envvar = get_env(HEART_NO_KILL); + if (!envvar || strcmp(envvar, "TRUE") == 0) + return; + if(heart_beat_kill_pid != 0){ if((erlh = OpenProcess(PROCESS_TERMINATE | SYNCHRONIZE | @@ -555,10 +563,14 @@ kill_old_erlang(void){ pid_t pid; int i, res; int sig = SIGKILL; - char *sigenv = NULL; + char *envvar = NULL; + + envvar = get_env(HEART_NO_KILL); + if (!envvar || strcmp(envvar, "TRUE") == 0) + return; - sigenv = get_env(HEART_KILL_SIGNAL); - if (sigenv && strcmp(sigenv, "SIGABRT") == 0) { + envvar = get_env(HEART_KILL_SIGNAL); + if (envvar && strcmp(envvar, "SIGABRT") == 0) { print_error("kill signal SIGABRT requested"); sig = SIGABRT; } diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam Binary files differindex 0521060e34..fdf8bbf4e2 100644 --- a/erts/preloaded/ebin/prim_inet.beam +++ b/erts/preloaded/ebin/prim_inet.beam diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl index 4872ffd00c..560810d222 100644 --- a/erts/preloaded/src/prim_inet.erl +++ b/erts/preloaded/src/prim_inet.erl @@ -70,11 +70,13 @@ open(Protocol, Family, Type) -> open(Protocol, Family, Type, Opts) -> open(Protocol, Family, Type, Opts, ?INET_REQ_OPEN, []). +%% FDOPEN(tcp|udp|sctp, inet|inet6|local, stream|dgram|seqpacket, integer()) + fdopen(Protocol, Family, Type, Fd) when is_integer(Fd) -> fdopen(Protocol, Family, Type, Fd, true). fdopen(Protocol, Family, Type, Fd, Bound) - when is_integer(Fd), Bound == true orelse Bound == false -> + when is_integer(Fd), is_boolean(Bound) -> open(Protocol, Family, Type, [], ?INET_REQ_FDOPEN, [?int32(Fd), enc_value_2(bool, Bound)]). @@ -104,8 +106,9 @@ open(Protocol, Family, Type, Opts, Req, Data) -> error:system_limit -> {error, system_limit} end. -enc_family(inet) -> ?INET_AF_INET; -enc_family(inet6) -> ?INET_AF_INET6. +enc_family(inet) -> ?INET_AF_INET; +enc_family(inet6) -> ?INET_AF_INET6; +enc_family(local) -> ?INET_AF_LOCAL. enc_type(stream) -> ?INET_TYPE_STREAM; enc_type(dgram) -> ?INET_TYPE_DGRAM; @@ -189,41 +192,52 @@ close_port(S) -> %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -bind(S,IP,Port) when is_port(S), is_integer(Port), Port >= 0, Port =< 65535 -> - case ctl_cmd(S,?INET_REQ_BIND,enc_value(set, addr, {IP,Port})) of - {ok, [P1,P0]} -> {ok, ?u16(P1, P0)}; - {error,_}=Error -> Error - end; - %% Multi-homed "bind": sctp_bindx(). The Op is 'add' or 'remove'. %% If no addrs are specified, it just does nothing. %% Function returns {ok, S} on success, unlike TCP/UDP "bind": -bind(S, Op, Addrs) when is_port(S), is_list(Addrs) -> - case Op of - add -> - bindx(S, 1, Addrs); - remove -> - bindx(S, 0, Addrs); - _ -> {error, einval} +bind(S, add, Addrs) when is_port(S), is_list(Addrs) -> + bindx(S, 1, Addrs); +bind(S, remove, Addrs) when is_port(S), is_list(Addrs) -> + bindx(S, 0, Addrs); +bind(S, Addr, _) when is_port(S), tuple_size(Addr) =:= 2 -> + case type_value(set, addr, Addr) of + true -> + case ctl_cmd(S,?INET_REQ_BIND,enc_value(set, addr, Addr)) of + {ok, [P1,P0]} -> {ok, ?u16(P1, P0)}; + {error, _} = Error -> Error + end; + false -> + {error, einval} end; -bind(_, _, _) -> {error, einval}. +bind(S, IP, Port) -> + bind(S, {IP, Port}, 0). bindx(S, AddFlag, Addrs) -> case getprotocol(S) of sctp -> - %% Really multi-homed "bindx". Stringified args: - %% [AddFlag, (AddrBytes see enc_value_2(addr,X))+]: - Args = - [?int8(AddFlag)| - [enc_value(set, addr, {IP,Port}) || - {IP, Port} <- Addrs]], - case ctl_cmd(S, ?SCTP_REQ_BINDX, Args) of - {ok,_} -> {ok, S}; - {error,_}=Error -> Error + case bindx_check_addrs(Addrs) of + true -> + %% Really multi-homed "bindx". Stringified args: + %% [AddFlag, (AddrBytes see enc_value_2(addr,X))+]: + Args = + [?int8(AddFlag)| + [enc_value(set, addr, Addr) || Addr <- Addrs]], + case ctl_cmd(S, ?SCTP_REQ_BINDX, Args) of + {ok, _} -> {ok, S}; + {error, _}=Error -> Error + end; + false -> + {error, einval} end; - _ -> {error, einval} + _ -> + {error, einval} end. +bindx_check_addrs([Addr|Addrs]) -> + type_value(set, addr, Addr) andalso bindx_check_addrs(Addrs); +bindx_check_addrs([]) -> + true. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% CONNECT(insock(), IP, Port [,Timeout]) -> ok | {error, Reason} @@ -242,14 +256,24 @@ bindx(S, AddFlag, Addrs) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% For TCP, UDP or SCTP sockets. %% -connect(S, IP, Port) -> connect0(S, IP, Port, -1). -connect(S, IP, Port, infinity) -> connect0(S, IP, Port, -1); -connect(S, IP, Port, Time) -> connect0(S, IP, Port, Time). +connect(S, IP, Port) -> + connect(S, IP, Port, infinity). +%% +connect(S, Addr, _, Time) when is_port(S), tuple_size(Addr) =:= 2 -> + case type_value(set, addr, Addr) of + true when Time =:= infinity -> + connect0(S, Addr, -1); + true when is_integer(Time) -> + connect0(S, Addr, Time); + false -> + {error, einval} + end; +connect(S, IP, Port, Time) -> + connect(S, {IP, Port}, 0, Time). -connect0(S, IP, Port, Time) when is_port(S), Port > 0, Port =< 65535, - is_integer(Time) -> - case async_connect(S, IP, Port, Time) of +connect0(S, Addr, Time) -> + case async_connect0(S, Addr, Time) of {ok, S, Ref} -> receive {inet_async, S, Ref, Status} -> @@ -258,11 +282,27 @@ connect0(S, IP, Port, Time) when is_port(S), Port > 0, Port =< 65535, Error -> Error end. + +async_connect(S, Addr, _, Time) when is_port(S), tuple_size(Addr) =:= 2 -> + case type_value(set, addr, Addr) of + true when Time =:= infinity -> + async_connect0(S, Addr, -1); + true when is_integer(Time) -> + async_connect0(S, Addr, Time); + false -> + {error, einval} + end; +%% async_connect(S, IP, Port, Time) -> - case ctl_cmd(S, ?INET_REQ_CONNECT, - [enc_time(Time),?int16(Port),ip_to_bytes(IP)]) of + async_connect(S, {IP, Port}, 0, Time). + +async_connect0(S, Addr, Time) -> + case ctl_cmd( + S, ?INET_REQ_CONNECT, + [enc_time(Time),enc_value(set, addr, Addr)]) + of {ok, [R1,R0]} -> {ok, S, ?u16(R1,R0)}; - {error,_}=Error -> Error + {error, _}=Error -> Error end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -400,20 +440,34 @@ send(S, Data) -> %% "sendto" is for UDP. IP and Port are set by the caller to 0 if the socket %% is known to be connected. -sendto(S, IP, Port, Data) when is_port(S), Port >= 0, Port =< 65535 -> - ?DBG_FORMAT("prim_inet:sendto(~p, ~p, ~p, ~p)~n", [S,IP,Port,Data]), - try erlang:port_command(S, [?int16(Port),ip_to_bytes(IP),Data]) of - true -> - receive - {inet_reply,S,Reply} -> - ?DBG_FORMAT("prim_inet:sendto() -> ~p~n", [Reply]), - Reply - end - catch - error:_ -> - ?DBG_FORMAT("prim_inet:sendto() -> {error,einval}~n", []), - {error,einval} - end. +sendto(S, Addr, _, Data) when is_port(S), tuple_size(Addr) =:= 2 -> + case type_value(set, addr, Addr) of + true -> + ?DBG_FORMAT("prim_inet:sendto(~p, ~p, ~p)~n", [S,Addr,Data]), + try + erlang:port_command(S, [enc_value(set, addr, Addr),Data]) + of + true -> + receive + {inet_reply,S,Reply} -> + ?DBG_FORMAT( + "prim_inet:sendto() -> ~p~n", [Reply]), + Reply + end + catch + error:_ -> + ?DBG_FORMAT( + "prim_inet:sendto() -> {error,einval}~n", []), + {error,einval} + end; + false -> + ?DBG_FORMAT( + "prim_inet:sendto() -> {error,einval}~n", []), + {error,einval} + end; +sendto(S, IP, Port, Data) -> + sendto(S, {IP, Port}, 0, Data). + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% @@ -484,29 +538,37 @@ async_recv(S, Length, Time) -> %% oriented: preserved here only for API compatibility. %% recvfrom(S, Length) -> - recvfrom0(S, Length, -1). + recvfrom(S, Length, infinity). -recvfrom(S, Length, infinity) -> +recvfrom(S, Length, infinity) when is_port(S) -> recvfrom0(S, Length, -1); -recvfrom(S, Length, Time) when is_integer(Time), Time < 16#ffffffff -> - recvfrom0(S, Length, Time); -recvfrom(_, _, _) -> {error,einval}. +recvfrom(S, Length, Time) when is_port(S) -> + if + is_integer(Time), 0 =< Time, Time < 16#ffffffff -> + recvfrom0(S, Length, Time); + true -> + {error, einval} + end. recvfrom0(S, Length, Time) - when is_port(S), is_integer(Length), Length >= 0, Length =< 16#ffffffff -> + when is_integer(Length), 0 =< Length, Length =< 16#ffffffff -> case ctl_cmd(S, ?PACKET_REQ_RECV,[enc_time(Time),?int32(Length)]) of {ok,[R1,R0]} -> Ref = ?u16(R1,R0), receive % Success, UDP: - {inet_async, S, Ref, {ok, [F,P1,P0 | AddrData]}} -> - {IP,Data} = get_ip(F, AddrData), - {ok, {IP, ?u16(P1,P0), Data}}; + {inet_async, S, Ref, {ok, [F | AddrData]}} -> + case get_addr(F, AddrData) of + {{Family, _} = Addr, Data} when is_atom(Family) -> + {ok, {Addr, 0, Data}}; + {{IP, Port}, Data} -> + {ok, {IP, Port, Data}} + end; % Success, SCTP: {inet_async, S, Ref, {ok, {[F,P1,P0 | Addr], AncData, DE}}} -> - {IP, _} = get_ip(F, Addr), - {ok, {IP, ?u16(P1,P0), AncData, DE}}; + {IP, _} = get_ip(F, Addr), + {ok, {IP, ?u16(P1, P0), AncData, DE}}; % Back-end error: {inet_async, S, Ref, Error={error, _}} -> @@ -525,21 +587,26 @@ recvfrom0(_, _, _) -> {error,einval}. peername(S) when is_port(S) -> case ctl_cmd(S, ?INET_REQ_PEER, []) of - {ok, [F, P1,P0 | Addr]} -> - {IP, _} = get_ip(F, Addr), - {ok, { IP, ?u16(P1, P0) }}; - {error,_}=Error -> Error + {ok, [F | Addr]} -> + {A, _} = get_addr(F, Addr), + {ok, A}; + {error, _} = Error -> Error end. -setpeername(S, {IP,Port}) when is_port(S) -> - case ctl_cmd(S, ?INET_REQ_SETPEER, [?int16(Port),ip_to_bytes(IP)]) of - {ok,[]} -> ok; - {error,_}=Error -> Error - end; setpeername(S, undefined) when is_port(S) -> case ctl_cmd(S, ?INET_REQ_SETPEER, []) of - {ok,[]} -> ok; - {error,_}=Error -> Error + {ok, []} -> ok; + {error, _} = Error -> Error + end; +setpeername(S, Addr) when is_port(S) -> + case type_value(set, addr, Addr) of + true -> + case ctl_cmd(S, ?INET_REQ_SETPEER, enc_value(set, addr, Addr)) of + {ok, []} -> ok; + {error, _} = Error -> Error + end; + false -> + {error, einval} end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -580,21 +647,28 @@ peernames(S, AssocId) sockname(S) when is_port(S) -> case ctl_cmd(S, ?INET_REQ_NAME, []) of - {ok, [F, P1, P0 | Addr]} -> - {IP, _} = get_ip(F, Addr), - {ok, { IP, ?u16(P1, P0) }}; - {error,_}=Error -> Error + {ok, [F | Addr]} -> + {A, _} = get_addr(F, Addr), + {ok, A}; + {error, _} = Error -> Error end. -setsockname(S, {IP,Port}) when is_port(S) -> - case ctl_cmd(S, ?INET_REQ_SETNAME, [?int16(Port),ip_to_bytes(IP)]) of - {ok,[]} -> ok; - {error,_}=Error -> Error - end; setsockname(S, undefined) when is_port(S) -> case ctl_cmd(S, ?INET_REQ_SETNAME, []) of - {ok,[]} -> ok; - {error,_}=Error -> Error + {ok, []} -> ok; + {error, _} = Error -> Error + end; +setsockname(S, Addr) when is_port(S) -> + case type_value(set, addr, Addr) of + true -> + case + ctl_cmd(S, ?INET_REQ_SETNAME, enc_value(set, addr, Addr)) + of + {ok, []} -> ok; + {error, _} = Error -> Error + end; + false -> + {error, einval} end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -1496,14 +1570,49 @@ type_value_2(uint8, X) when X band 16#ff =:= X -> true; type_value_2(time, infinity) -> true; type_value_2(time, X) when is_integer(X), X >= 0 -> true; type_value_2(ip,{A,B,C,D}) when ?ip(A,B,C,D) -> true; +%% type_value_2(addr, {any,Port}) -> type_value_2(uint16, Port); type_value_2(addr, {loopback,Port}) -> type_value_2(uint16, Port); -type_value_2(addr, {{A,B,C,D},Port}) when ?ip(A,B,C,D) -> +type_value_2(addr, {IP,_} = Addr) when tuple_size(IP) =:= 4 -> + type_value_2(addr, {inet,Addr}); +type_value_2(addr, {IP,_} = Addr) when tuple_size(IP) =:= 8 -> + type_value_2(addr, {inet6,Addr}); +type_value_2(addr, {Local,_}) when is_list(Local); is_binary(Local) -> + type_value_2(addr, {local,Local}); +%% +type_value_2(addr, {Family,{Tag,Port}}) + when (Family =:= inet orelse Family =:= inet6) andalso + (Tag =:= any orelse Tag =:= loopback) -> + type_value_2(uint16, Port); +type_value_2(addr, {inet,{{A,B,C,D},Port}}) + when ?ip(A,B,C,D) -> type_value_2(uint16, Port); -type_value_2(addr, {{A,B,C,D,E,F,G,H},Port}) when ?ip6(A,B,C,D,E,F,G,H) -> +type_value_2(addr, {inet6,{{A,B,C,D,E,F,G,H},Port}}) + when ?ip6(A,B,C,D,E,F,G,H) -> type_value_2(uint16, Port); +type_value_2(addr, {local,Addr}) -> + if + is_binary(Addr) -> + byte_size(Addr) =< 255; + true -> + try + %% We either get a badarg from byte_size + %% or from characters_to_binary + byte_size( + unicode:characters_to_binary( + Addr, file:native_name_encoding())) + of + N when N =< 255 -> + true; + _ -> + false + catch error:badarg -> + false + end + end; +%% type_value_2(ether,[X1,X2,X3,X4,X5,X6]) when ?ether(X1,X2,X3,X4,X5,X6) -> true; type_value_2({enum,List}, Enum) -> @@ -1611,6 +1720,7 @@ enc_value_2(time, Val) -> ?int32(Val); enc_value_2(ip,{A,B,C,D}) -> [A,B,C,D]; enc_value_2(ip, any) -> [0,0,0,0]; enc_value_2(ip, loopback) -> [127,0,0,1]; +%% enc_value_2(addr, {any,Port}) -> [?INET_AF_ANY|?int16(Port)]; enc_value_2(addr, {loopback,Port}) -> @@ -1619,6 +1729,35 @@ enc_value_2(addr, {IP,Port}) when tuple_size(IP) =:= 4 -> [?INET_AF_INET,?int16(Port)|ip4_to_bytes(IP)]; enc_value_2(addr, {IP,Port}) when tuple_size(IP) =:= 8 -> [?INET_AF_INET6,?int16(Port)|ip6_to_bytes(IP)]; +enc_value_2(addr, {File,_}) when is_list(File); is_binary(File) -> + [?INET_AF_LOCAL,iolist_size(File)|File]; +%% +enc_value_2(addr, {inet,{any,Port}}) -> + [?INET_AF_INET,?int16(Port),0,0,0,0]; +enc_value_2(addr, {inet,{loopback,Port}}) -> + [?INET_AF_INET,?int16(Port),127,0,0,1]; +enc_value_2(addr, {inet,{IP,Port}}) -> + [?INET_AF_INET,?int16(Port)|ip4_to_bytes(IP)]; +enc_value_2(addr, {inet6,{any,Port}}) -> + [?INET_AF_INET6,?int16(Port),0,0,0,0,0,0,0,0]; +enc_value_2(addr, {inet6,{loopback,Port}}) -> + [?INET_AF_INET6,?int16(Port),0,0,0,0,0,0,0,1]; +enc_value_2(addr, {inet6,{IP,Port}}) -> + [?INET_AF_INET6,?int16(Port)|ip6_to_bytes(IP)]; +enc_value_2(addr, {local,Addr}) -> + %% A binary is passed as is, but anything else will be + %% regarded as a filename and therefore encoded according to + %% the current system filename encoding mode. + Bin = + if + is_binary(Addr) -> + Addr; + true -> + unicode:characters_to_binary( + Addr, file:native_name_encoding()) + end, + [?INET_AF_LOCAL,byte_size(Bin),Bin]; +%% enc_value_2(ether, [_,_,_,_,_,_]=Xs) -> Xs; enc_value_2(sockaddr, any) -> [?INET_AF_ANY]; @@ -2249,9 +2388,6 @@ utf8_to_characters(Bs, U, 0) -> utf8_to_characters([B|Bs], U, N) when ((B band 16#3F) bor 16#80) =:= B -> utf8_to_characters(Bs, (U bsl 6) bor (B band 16#3F), N-1). -ip_to_bytes(IP) when tuple_size(IP) =:= 4 -> ip4_to_bytes(IP); -ip_to_bytes(IP) when tuple_size(IP) =:= 8 -> ip6_to_bytes(IP). - ip4_to_bytes({A,B,C,D}) -> [A band 16#ff, B band 16#ff, C band 16#ff, D band 16#ff]. @@ -2261,18 +2397,32 @@ ip6_to_bytes({A,B,C,D,E,F,G,H}) -> get_addrs([]) -> []; -get_addrs([F,P1,P0|Addr]) -> - {IP,Addrs} = get_ip(F, Addr), - [{IP,?u16(P1, P0)}|get_addrs(Addrs)]. - -get_ip(?INET_AF_INET, Addr) -> get_ip4(Addr); -get_ip(?INET_AF_INET6, Addr) -> get_ip6(Addr). +get_addrs([F|Addrs]) -> + {Addr,Rest} = get_addr(F, Addrs), + [Addr|get_addrs(Rest)]. + +get_addr(?INET_AF_LOCAL, [0]) -> + {{local,<<>>},[]}; +get_addr(?INET_AF_LOCAL, [N|Addr]) -> + {A,Rest} = lists:split(N, Addr), + {{local,iolist_to_binary(A)},Rest}; +get_addr(?INET_AF_UNDEFINED, Rest) -> + {{undefined,0},Rest}; +get_addr(Family, [P1,P0|Addr]) -> + {IP,Rest} = get_ip(Family, Addr), + {{IP,?u16(P1, P0)},Rest}. + +get_ip(?INET_AF_INET, Addr) -> + get_ip4(Addr); +get_ip(?INET_AF_INET6, Addr) -> + get_ip6(Addr). get_ip4([A,B,C,D | T]) -> {{A,B,C,D},T}. get_ip6([X1,X2,X3,X4,X5,X6,X7,X8,X9,X10,X11,X12,X13,X14,X15,X16 | T]) -> { { ?u16(X1,X2),?u16(X3,X4),?u16(X5,X6),?u16(X7,X8), - ?u16(X9,X10),?u16(X11,X12),?u16(X13,X14),?u16(X15,X16)}, T}. + ?u16(X9,X10),?u16(X11,X12),?u16(X13,X14),?u16(X15,X16)}, + T }. %% Control command diff --git a/lib/common_test/src/ct.erl b/lib/common_test/src/ct.erl index cae7bea406..d7ae81a5ce 100644 --- a/lib/common_test/src/ct.erl +++ b/lib/common_test/src/ct.erl @@ -282,7 +282,7 @@ step(TestDir,Suite,Case,Opts) -> %%% <c>> ct_telnet:cmd(unix_telnet, "ls .").</c><br/> %%% <c>{ok,["ls","file1 ...",...]}</c></p> start_interactive() -> - ct_util:start(interactive), + _ = ct_util:start(interactive), ok. %%%----------------------------------------------------------------- diff --git a/lib/common_test/src/ct_config.erl b/lib/common_test/src/ct_config.erl index b499bc8b05..99de311570 100644 --- a/lib/common_test/src/ct_config.erl +++ b/lib/common_test/src/ct_config.erl @@ -119,7 +119,8 @@ call(Msg) -> end. return({To,Ref},Result) -> - To ! {Ref, Result}. + To ! {Ref, Result}, + ok. loop(StartDir) -> receive @@ -128,11 +129,11 @@ loop(StartDir) -> return(From,Result), loop(StartDir); {{set_default_config,{Config,Scope}},From} -> - set_config(Config,{true,Scope}), + _ = set_config(Config,{true,Scope}), return(From,ok), loop(StartDir); {{set_default_config,{Name,Config,Scope}},From} -> - set_config(Name,Config,{true,Scope}), + _ = set_config(Name,Config,{true,Scope}), return(From,ok), loop(StartDir); {{delete_default_config,Scope},From} -> @@ -149,7 +150,7 @@ loop(StartDir) -> loop(StartDir); {{stop},From} -> ets:delete(?attr_table), - file:set_cwd(StartDir), + ok = file:set_cwd(StartDir), return(From,ok) end. @@ -257,7 +258,7 @@ read_config_files(Opts) -> read_config_files_int([{Callback, File}|Files], FunToSave) -> case Callback:read_config(File) of {ok, Config} -> - FunToSave(Config, Callback, File), + _ = FunToSave(Config, Callback, File), read_config_files_int(Files, FunToSave); {error, {ErrorName, ErrorDetail}} -> {user_error, {ErrorName, File, ErrorDetail}}; @@ -267,6 +268,15 @@ read_config_files_int([{Callback, File}|Files], FunToSave) -> read_config_files_int([], _FunToSave) -> ok. + +read_config_files(ConfigFiles, FunToSave) -> + case read_config_files_int(ConfigFiles, FunToSave) of + {user_error, Error} -> + {error, Error}; + ok -> + ok + end. + store_config(Config, Callback, File) when is_tuple(Config) -> store_config([Config], Callback, File); @@ -455,8 +465,12 @@ reload_conf(KeyOrName) -> undefined; HandlerList -> HandlerList2 = lists:usort(HandlerList), - read_config_files_int(HandlerList2, fun rewrite_config/3), - get_config(KeyOrName) + case read_config_files(HandlerList2, fun rewrite_config/3) of + ok -> + get_config(KeyOrName); + Error -> + Error + end end. release_allocated() -> @@ -490,16 +504,16 @@ associate(Name,_Key,Configs) -> associate_int(Name,Configs,os:getenv("COMMON_TEST_ALIAS_TOP")). associate_int(Name,Configs,"true") -> - lists:map(fun({K,_Config}) -> + lists:foreach(fun({K,_Config}) -> Cs = ets:match_object( ?attr_table, #ct_conf{key=element(1,K), name='_UNDEF',_='_'}), [ets:insert(?attr_table,C#ct_conf{name=Name}) || C <- Cs] - end,Configs); + end,Configs); associate_int(Name,Configs,_) -> - lists:map(fun({K,Config}) -> + lists:foreach(fun({K,Config}) -> Key = if is_tuple(K) -> element(1,K); is_atom(K) -> K end, @@ -511,7 +525,7 @@ associate_int(Name,Configs,_) -> [ets:insert(?attr_table,C#ct_conf{name=Name, value=Config}) || C <- Cs] - end,Configs). + end,Configs). @@ -576,7 +590,7 @@ encrypt_config_file(SrcFileName, EncryptFileName, {file,KeyFile}) -> end; encrypt_config_file(SrcFileName, EncryptFileName, {key,Key}) -> - crypto:start(), + _ = crypto:start(), {Key,IVec} = make_crypto_key(Key), case file:read_file(SrcFileName) of {ok,Bin0} -> @@ -615,7 +629,7 @@ decrypt_config_file(EncryptFileName, TargetFileName, {file,KeyFile}) -> end; decrypt_config_file(EncryptFileName, TargetFileName, {key,Key}) -> - crypto:start(), + _ = crypto:start(), {Key,IVec} = make_crypto_key(Key), case file:read_file(EncryptFileName) of {ok,Bin} -> @@ -778,14 +792,13 @@ prepare_config_list(Args) -> % TODO: add logging of the loaded configuration file to the CT FW log!!! add_config(Callback, []) -> - read_config_files_int([{Callback, []}], fun store_config/3); + read_config_files([{Callback, []}], fun store_config/3); add_config(Callback, [File|_Files]=Config) when is_list(File) -> lists:foreach(fun(CfgStr) -> - read_config_files_int([{Callback, CfgStr}], fun store_config/3) end, + read_config_files([{Callback, CfgStr}], fun store_config/3) end, Config); add_config(Callback, [C|_]=Config) when is_integer(C) -> - read_config_files_int([{Callback, Config}], fun store_config/3), - ok. + read_config_files([{Callback, Config}], fun store_config/3). remove_config(Callback, Config) -> ets:match_delete(?attr_table, diff --git a/lib/common_test/src/ct_framework.erl b/lib/common_test/src/ct_framework.erl index eb32f7f3fc..104515e57e 100644 --- a/lib/common_test/src/ct_framework.erl +++ b/lib/common_test/src/ct_framework.erl @@ -128,7 +128,7 @@ init_tc1(?MODULE,_,error_in_suite,_,[Config0]) when is_list(Config0) -> ct_event:notify(#event{name=tc_start, node=node(), data={?MODULE,error_in_suite}}), - ct_suite_init(?MODULE,error_in_suite,[],Config0), + _ = ct_suite_init(?MODULE,error_in_suite,[],Config0), case ?val(error,Config0) of undefined -> {fail,"unknown_error_in_suite"}; @@ -212,7 +212,7 @@ init_tc2(Mod,Suite,Func,HookFunc,SuiteInfo,MergeResult,Config) -> %% timetrap must be handled before require MergedInfo = timetrap_first(MergeResult, [], []), %% tell logger to use specified style sheet - case lists:keysearch(stylesheet,1,MergeResult++Config) of + _ = case lists:keysearch(stylesheet,1,MergeResult++Config) of {value,{stylesheet,SSFile}} -> ct_logs:set_stylesheet(Func,add_data_dir(SSFile,Config)); _ -> @@ -632,10 +632,10 @@ try_set_default(Name,Key,Info,Where) -> {_,[]} -> no_default; {'_UNDEF',_} -> - [ct_config:set_default_config([CfgVal],Where) || CfgVal <- CfgElems], + _ = [ct_config:set_default_config([CfgVal],Where) || CfgVal <- CfgElems], ok; _ -> - [ct_config:set_default_config(Name,[CfgVal],Where) || CfgVal <- CfgElems], + _ = [ct_config:set_default_config(Name,[CfgVal],Where) || CfgVal <- CfgElems], ok end. @@ -1315,7 +1315,7 @@ report(What,Data) -> %% top level test index page needs to be refreshed TestName = filename:basename(?val(topdir, Data), ".logs"), RunDir = ?val(rundir, Data), - ct_logs:make_all_suites_index({TestName,RunDir}), + _ = ct_logs:make_all_suites_index({TestName,RunDir}), ok; tests_start -> ok; diff --git a/lib/common_test/src/ct_ftp.erl b/lib/common_test/src/ct_ftp.erl index 48914864e4..84e664b387 100644 --- a/lib/common_test/src/ct_ftp.erl +++ b/lib/common_test/src/ct_ftp.erl @@ -292,7 +292,7 @@ init(KeyOrName,{IP,Port},{Username,Password}) -> end. ftp_connect(IP,Port,Username,Password) -> - inets:start(), + _ = inets:start(), case inets:start(ftpc,[{host,IP},{port,Port}]) of {ok,FtpPid} -> case ftp:user(FtpPid,Username,Password) of diff --git a/lib/common_test/src/ct_groups.erl b/lib/common_test/src/ct_groups.erl index dd04c5410a..1375e7dcc7 100644 --- a/lib/common_test/src/ct_groups.erl +++ b/lib/common_test/src/ct_groups.erl @@ -402,12 +402,7 @@ expand(Mod, Name, Defs) -> end. make_all_conf(Dir, Mod, Props, TestSpec) -> - case code:is_loaded(Mod) of - false -> - code:load_abs(filename:join(Dir,atom_to_list(Mod))); - _ -> - ok - end, + _ = load_abs(Dir, Mod), make_all_conf(Mod, Props, TestSpec). make_all_conf(Mod, Props, TestSpec) -> @@ -428,16 +423,19 @@ make_all_conf(Mod, Props, TestSpec) -> end. make_conf(Dir, Mod, Name, Props, TestSpec) -> + _ = load_abs(Dir, Mod), + make_conf(Mod, Name, Props, TestSpec). + +load_abs(Dir, Mod) -> case code:is_loaded(Mod) of false -> code:load_abs(filename:join(Dir,atom_to_list(Mod))); _ -> ok - end, - make_conf(Mod, Name, Props, TestSpec). + end. make_conf(Mod, Name, Props, TestSpec) -> - case code:is_loaded(Mod) of + _ = case code:is_loaded(Mod) of false -> code:load_file(Mod); _ -> diff --git a/lib/common_test/src/ct_hooks.erl b/lib/common_test/src/ct_hooks.erl index 5422d449fd..c9a4abb5ee 100644 --- a/lib/common_test/src/ct_hooks.erl +++ b/lib/common_test/src/ct_hooks.erl @@ -408,7 +408,8 @@ catch_apply(M,F,A, Default) -> maybe_start_locker(Mod,GroupName,Opts) -> case lists:member(parallel,Opts) of true -> - {ok, _Pid} = ct_hooks_lock:start({Mod,GroupName}); + {ok, _Pid} = ct_hooks_lock:start({Mod,GroupName}), + ok; false -> ok end. diff --git a/lib/common_test/src/ct_hooks_lock.erl b/lib/common_test/src/ct_hooks_lock.erl index f41f259f7b..fea298e535 100644 --- a/lib/common_test/src/ct_hooks_lock.erl +++ b/lib/common_test/src/ct_hooks_lock.erl @@ -82,7 +82,7 @@ init(Id) -> %% @doc Handling call messages handle_call({stop,Id}, _From, #state{ id = Id, requests = Reqs } = State) -> - [gen_server:reply(Req, locker_stopped) || {Req,_ReqId} <- Reqs], + _ = [gen_server:reply(Req, locker_stopped) || {Req,_ReqId} <- Reqs], {stop, normal, stopped, State}; handle_call({stop,_Id}, _From, State) -> {reply, stopped, State}; diff --git a/lib/common_test/src/ct_logs.erl b/lib/common_test/src/ct_logs.erl index e6d683c8a9..455864efb6 100644 --- a/lib/common_test/src/ct_logs.erl +++ b/lib/common_test/src/ct_logs.erl @@ -149,7 +149,7 @@ close(Info, StartDir) -> ok; CacheBin -> %% save final version of the log cache to file - file:write_file(?log_cache_name,CacheBin), + _ = file:write_file(?log_cache_name,CacheBin), put(ct_log_cache,undefined) end end, @@ -175,12 +175,12 @@ close(Info, StartDir) -> Error -> io:format("Warning! Cleanup failed: ~p~n", [Error]) end, - make_all_suites_index(stop), + _ = make_all_suites_index(stop), make_all_runs_index(stop), Cache2File(); true -> - file:set_cwd(".."), - make_all_suites_index(stop), + ok = file:set_cwd(".."), + _ = make_all_suites_index(stop), make_all_runs_index(stop), Cache2File(), case ct_util:get_profile_data(browser, StartDir) of @@ -251,16 +251,30 @@ call(Msg) -> end. return({To,Ref},Result) -> - To ! {Ref, Result}. + To ! {Ref, Result}, + ok. cast(Msg) -> case whereis(?MODULE) of undefined -> - {error,does_not_exist}; + io:format("Warning: ct_logs not started~n"), + {_,_,_,_,_,_,Content,_} = Msg, + FormatArgs = get_format_args(Content), + _ = [io:format(Format, Args) || {Format, Args} <- FormatArgs], + ok; _Pid -> - ?MODULE ! Msg + ?MODULE ! Msg, + ok end. +get_format_args(Content) -> + lists:map(fun(C) -> + case C of + {_, FA, _} -> FA; + {_, _} -> C + end + end, Content). + %%%----------------------------------------------------------------- %%% @spec init_tc(RefreshLog) -> ok %%% @@ -631,7 +645,7 @@ logger(Parent, Mode, Verbosity) -> end, %%! <--- - file:make_dir(Dir), + _ = file:make_dir(Dir), AbsDir = ?abs(Dir), put(ct_run_dir, AbsDir), @@ -671,7 +685,7 @@ logger(Parent, Mode, Verbosity) -> end end, - test_server_io:start_link(), + _ = test_server_io:start_link(), MiscIoName = filename:join(Dir, ?misc_io_log), {ok,MiscIoFd} = file:open(MiscIoName, [write,{encoding,utf8}]), @@ -701,13 +715,13 @@ logger(Parent, Mode, Verbosity) -> ct_event:notify(#event{name=start_logging,node=node(), data=AbsDir}), make_all_runs_index(start), - make_all_suites_index(start), + _ = make_all_suites_index(start), case Mode of interactive -> interactive_link(); _ -> ok end, - file:set_cwd(Dir), - make_last_run_index(Time), + ok = file:set_cwd(Dir), + _ = make_last_run_index(Time), CtLogFd = open_ctlog(?misc_io_log), io:format(CtLogFd,int_header()++int_footer(), [log_timestamp(?now),"Common Test Logger started"]), @@ -721,13 +735,13 @@ logger(Parent, Mode, Verbosity) -> GenLvl -> io:format(CtLogFd, "~-25s~3w~n", ["general level",GenLvl]) end, - [begin put({verbosity,Cat},VLvl), - if Cat == '$unspecified' -> + _ = [begin put({verbosity,Cat},VLvl), + if Cat == '$unspecified' -> ok; - true -> + true -> io:format(CtLogFd, "~-25w~3w~n", [Cat,VLvl]) - end - end || {Cat,VLvl} <- Verbosity], + end + end || {Cat,VLvl} <- Verbosity], io:nl(CtLogFd), TcEscChars = case application:get_env(common_test, esc_chars) of {ok,ECBool} -> ECBool; @@ -804,7 +818,7 @@ logger_loop(State) -> print_style(GL, IoFormat, State#logger_state.stylesheet), set_evmgr_gl(GL), TCGLs = add_tc_gl(TCPid,GL,State), - if not RefreshLog -> + _ = if not RefreshLog -> ok; true -> make_last_run_index(State#logger_state.start_time) @@ -831,7 +845,7 @@ logger_loop(State) -> return(From,{ok,filename:basename(State#logger_state.log_dir)}), logger_loop(State); {make_last_run_index,From} -> - make_last_run_index(State#logger_state.start_time), + _ = make_last_run_index(State#logger_state.start_time), return(From,get(ct_log_cache)), logger_loop(State); {set_stylesheet,_,SSFile} when State#logger_state.stylesheet == @@ -1169,7 +1183,7 @@ print_style_error(Fd, IoFormat, StyleSheet, Reason) -> close_ctlog(Fd) -> io:format(Fd, "\n</pre>\n", []), io:format(Fd, [xhtml("<br><br>\n", "<br /><br />\n") | footer()], []), - file:close(Fd). + ok = file:close(Fd). %%%----------------------------------------------------------------- %%% tc_io_format/3 @@ -1773,7 +1787,7 @@ count_cases(Dir) -> %% file yet. {0,0,0,0}; Summary -> - write_summary(SumFile, Summary), + _ = write_summary(SumFile, Summary), Summary end; {error, Reason} -> @@ -2088,7 +2102,7 @@ interactive_link() -> "</body>\n", "</html>\n" ], - file:write_file("last_interactive.html",unicode:characters_to_binary(Body)), + _ = file:write_file("last_interactive.html",unicode:characters_to_binary(Body)), io:format("~n~nUpdated ~ts\n" "Any CT activities will be logged here\n", [?abs("last_interactive.html")]). @@ -2219,9 +2233,9 @@ runentry(Dir, _, _) -> write_totals_file(Name,Label,Logs,Totals) -> AbsName = ?abs(Name), notify_and_lock_file(AbsName), - force_write_file(AbsName, - term_to_binary({atom_to_list(node()), - Label,Logs,Totals})), + _ = force_write_file(AbsName, + term_to_binary({atom_to_list(node()), + Label,Logs,Totals})), notify_and_unlock_file(AbsName). %% this function needs to convert from old formats to new so that old @@ -2266,7 +2280,7 @@ read_totals_file(Name) -> Result. force_write_file(Name,Contents) -> - force_delete(Name), + _ = force_delete(Name), file:write_file(Name,Contents). force_delete(Name) -> @@ -2817,18 +2831,18 @@ get_cache_data({ok,CacheBin}) -> true -> {ok,CacheRec}; false -> - file:delete(?log_cache_name), + _ = file:delete(?log_cache_name), {error,old_cache_file} end; _ -> - file:delete(?log_cache_name), + _ = file:delete(?log_cache_name), {error,invalid_cache_file} end; get_cache_data(NoCache) -> NoCache. cache_vsn() -> - application:load(common_test), + _ = application:load(common_test), case application:get_key(common_test,vsn) of {ok,VSN} -> VSN; diff --git a/lib/common_test/src/ct_master.erl b/lib/common_test/src/ct_master.erl index c4905b316f..4eef27d2a5 100644 --- a/lib/common_test/src/ct_master.erl +++ b/lib/common_test/src/ct_master.erl @@ -376,7 +376,7 @@ init_master(Parent,NodeOptsList,EvHandlers,MasterLogDir,LogDirs, end, %% start master event manager and add default handler - ct_master_event:start_link(), + {ok, _} = start_ct_master_event(), ct_master_event:add_handler(), %% add user handlers for master event manager Add = fun({H,Args}) -> @@ -398,6 +398,14 @@ init_master(Parent,NodeOptsList,EvHandlers,MasterLogDir,LogDirs, end, init_master1(Parent,NodeOptsList,InitOptions,LogDirs). +start_ct_master_event() -> + case ct_master_event:start_link() of + {error, {already_started, Pid}} -> + {ok, Pid}; + Else -> + Else + end. + init_master1(Parent,NodeOptsList,InitOptions,LogDirs) -> {Inaccessible,NodeOptsList1,InitOptions1} = init_nodes(NodeOptsList, InitOptions), @@ -658,7 +666,7 @@ refresh_logs([D|Dirs],Refreshed) -> {ok,Cwd} = file:get_cwd(), case catch ct_run:refresh_logs(D) of {'EXIT',Reason} -> - file:set_cwd(Cwd), + ok = file:set_cwd(Cwd), refresh_logs(Dirs,[{D,{error,Reason}}|Refreshed]); Result -> refresh_logs(Dirs,[{D,Result}|Refreshed]) @@ -701,7 +709,7 @@ init_node_ctrl(MasterPid,Cookie,Opts) -> end, %% start a local event manager - ct_event:start_link(), + {ok, _} = start_ct_event(), ct_event:add_handler([{master,MasterPid}]), %% log("Running test with options: ~p~n", [Opts]), @@ -721,6 +729,14 @@ init_node_ctrl(MasterPid,Cookie,Opts) -> "Can't report result!~n~n", [MasterNode]) end. +start_ct_event() -> + case ct_event:start_link() of + {error, {already_started, Pid}} -> + {ok, Pid}; + Else -> + Else + end. + %%%----------------------------------------------------------------- %%% Event handling %%%----------------------------------------------------------------- @@ -778,7 +794,7 @@ reply(Result,To) -> ok. init_nodes(NodeOptions, InitOptions)-> - ping_nodes(NodeOptions), + _ = ping_nodes(NodeOptions), start_nodes(InitOptions), eval_on_nodes(InitOptions), {Inaccessible, NodeOptions1}=ping_nodes(NodeOptions), diff --git a/lib/common_test/src/ct_master_logs.erl b/lib/common_test/src/ct_master_logs.erl index 39f87a7f09..a2542171f8 100644 --- a/lib/common_test/src/ct_master_logs.erl +++ b/lib/common_test/src/ct_master_logs.erl @@ -91,8 +91,8 @@ init(Parent,LogDir,Nodes) -> Time = calendar:local_time(), RunDir = make_dirname(Time), RunDirAbs = filename:join(LogDir,RunDir), - file:make_dir(RunDirAbs), - write_details_file(RunDirAbs,{node(),Nodes}), + ok = make_dir(RunDirAbs), + _ = write_details_file(RunDirAbs,{node(),Nodes}), case basic_html() of true -> @@ -128,7 +128,7 @@ init(Parent,LogDir,Nodes) -> end end, - make_all_runs_index(LogDir), + _ = make_all_runs_index(LogDir), CtLogFd = open_ct_master_log(RunDirAbs), NodeStr = lists:flatten(lists:map(fun(N) -> @@ -181,7 +181,7 @@ loop(State) -> lists:foreach(Fun,List), loop(State); {make_all_runs_index,From} -> - make_all_runs_index(State#state.logdir), + _ = make_all_runs_index(State#state.logdir), return(From,State#state.logdir), loop(State); {{nodedir,Node,RunDir},From} -> @@ -189,12 +189,12 @@ loop(State) -> return(From,ok), loop(State); stop -> - make_all_runs_index(State#state.logdir), + _ = make_all_runs_index(State#state.logdir), io:format(State#state.log_fd, int_header()++int_footer(), [log_timestamp(?now),"Finished!"]), - close_ct_master_log(State#state.log_fd), - close_nodedir_index(State#state.nodedir_ix_fd), + _ = close_ct_master_log(State#state.log_fd), + _ = close_nodedir_index(State#state.nodedir_ix_fd), ok end. @@ -496,7 +496,7 @@ make_relative(Dir) -> ct_logs:make_relative(Dir). force_write_file(Name,Contents) -> - force_delete(Name), + _ = force_delete(Name), file:write_file(Name,Contents). force_delete(Name) -> @@ -534,13 +534,34 @@ call(Msg) -> end. return({To,Ref},Result) -> - To ! {Ref, Result}. + To ! {Ref, Result}, + ok. cast(Msg) -> case whereis(?MODULE) of undefined -> - {error,does_not_exist}; + io:format("Warning: ct_master_logs not started~n"), + {_,_,Content} = Msg, + FormatArgs = get_format_args(Content), + _ = [io:format(Format, Args) || {Format, Args} <- FormatArgs], + ok; _Pid -> - ?MODULE ! Msg + ?MODULE ! Msg, + ok end. +get_format_args(Content) -> + lists:map(fun(C) -> + case C of + {_, FA, _} -> FA; + _ -> C + end + end, Content). + +make_dir(Dir) -> + case file:make_dir(Dir) of + {error, exist} -> + ok; + Else -> + Else + end. diff --git a/lib/common_test/src/ct_property_test.erl b/lib/common_test/src/ct_property_test.erl index 7dc78e949a..12c3d726d3 100644 --- a/lib/common_test/src/ct_property_test.erl +++ b/lib/common_test/src/ct_property_test.erl @@ -161,7 +161,9 @@ property_tests_path(Dir, Config) -> add_code_pathz(Dir) -> case lists:member(Dir, code:get_path()) of true -> ok; - false -> code:add_pathz(Dir) + false -> + true = code:add_pathz(Dir), + ok end. compile_tests(Path, ToolModule) -> @@ -171,10 +173,10 @@ compile_tests(Path, ToolModule) -> {ok,FileNames} = file:list_dir("."), BeamFiles = [F || F<-FileNames, filename:extension(F) == ".beam"], - [file:delete(F) || F<-BeamFiles], + _ = [file:delete(F) || F<-BeamFiles], ct:pal("Compiling in ~p:~n Deleted ~p~n MacroDefs=~p",[Path,BeamFiles,MacroDefs]), Result = make:all([load|MacroDefs]), - file:set_cwd(Cwd), + ok = file:set_cwd(Cwd), Result. diff --git a/lib/common_test/src/ct_release_test.erl b/lib/common_test/src/ct_release_test.erl index 4e0f88cf5f..d783f8d04e 100644 --- a/lib/common_test/src/ct_release_test.erl +++ b/lib/common_test/src/ct_release_test.erl @@ -342,7 +342,7 @@ cleanup(Config) -> end end, AllNodes), - [rpc:call(Node,erlang,halt,[]) || Node <- Nodes], + _ = [rpc:call(Node,erlang,halt,[]) || Node <- Nodes], Config. %%----------------------------------------------------------------- @@ -552,14 +552,14 @@ target_system(Apps,CreateDir,InstallDir,{FromVsn,_,AllAppsVsns,Path}) -> %% Add bin and log dirs BinDir = filename:join([InstallDir, "bin"]), - file:make_dir(BinDir), - file:make_dir(filename:join(InstallDir,"log")), + ok = make_dir(BinDir), + ok = make_dir(filename:join(InstallDir,"log")), %% Delete start scripts - they will be added later ErtsBinDir = filename:join([InstallDir, "erts-" ++ ErtsVsn, "bin"]), - file:delete(filename:join([ErtsBinDir, "erl"])), - file:delete(filename:join([ErtsBinDir, "start"])), - file:delete(filename:join([ErtsBinDir, "start_erl"])), + ok = delete_file(filename:join([ErtsBinDir, "erl"])), + ok = delete_file(filename:join([ErtsBinDir, "start"])), + ok = delete_file(filename:join([ErtsBinDir, "start_erl"])), %% Copy .boot to bin/start.boot copy_file(RelName++".boot",filename:join([BinDir, "start.boot"])), @@ -680,7 +680,7 @@ do_upgrade({Cb,InitState},FromVsn,FromAppsVsns,ToRel,ToAppsVsns,InstallDir) -> %% even if install_release returned {ok,...} there might be an %% emulator restart (instruction restart_emulator), so we must %% always make sure the node is running. - wait_node_up(current,ToVsn,ToAppsVsns), + {ok, _} = wait_node_up(current,ToVsn,ToAppsVsns), [{"OTP upgrade test",ToVsn,_,current}, {"OTP upgrade test",FromVsn,_,permanent}] = @@ -703,7 +703,7 @@ do_upgrade({Cb,InitState},FromVsn,FromAppsVsns,ToRel,ToAppsVsns,InstallDir) -> %% even if install_release returned {ok,...} there might be an %% emulator restart (instruction restart_emulator), so we must %% always make sure the node is running. - wait_node_up(current,FromVsn,FromAppsVsns), + {ok, _} = wait_node_up(current,FromVsn,FromAppsVsns), [{"OTP upgrade test",ToVsn,_,permanent}, {"OTP upgrade test",FromVsn,_,current}] = @@ -854,7 +854,7 @@ copy_file(Src, Dest, Opts) -> case lists:member(preserve, Opts) of true -> {ok, FileInfo} = file:read_file_info(Src), - file:write_file_info(Dest, FileInfo); + ok = file:write_file_info(Dest, FileInfo); false -> ok end. @@ -862,8 +862,8 @@ copy_file(Src, Dest, Opts) -> write_file(FName, Conts) -> Enc = file:native_name_encoding(), {ok, Fd} = file:open(FName, [write]), - file:write(Fd, unicode:characters_to_binary(Conts,Enc,Enc)), - file:close(Fd). + ok = file:write(Fd, unicode:characters_to_binary(Conts,Enc,Enc)), + ok = file:close(Fd). %% Substitute all occurrences of %Var% for Val in the given scripts subst_src_scripts(Scripts, SrcDir, DestDir, Vars, Opts) -> @@ -944,3 +944,19 @@ rm_rf(Dir) -> _ -> ok end. + +delete_file(FileName) -> + case file:delete(FileName) of + {error, enoent} -> + ok; + Else -> + Else + end. + +make_dir(Dir) -> + case file:make_dir(Dir) of + {error, eexist} -> + ok; + Else -> + Else + end. diff --git a/lib/common_test/src/ct_repeat.erl b/lib/common_test/src/ct_repeat.erl index 31c5755c7e..dac596a135 100644 --- a/lib/common_test/src/ct_repeat.erl +++ b/lib/common_test/src/ct_repeat.erl @@ -44,13 +44,13 @@ loop_test(If,Args) when is_list(Args) -> false; E = {error,_} -> io:format("Common Test error: ~p\n\n",[E]), - file:set_cwd(Cwd), + ok = file:set_cwd(Cwd), E; {repeat,N} -> io:format("\nCommon Test: Will repeat tests ~w times.\n\n",[N]), Args1 = [{loop_info,[{repeat,1,N}]} | Args], Result = loop(If,repeat,0,N,undefined,Args1,undefined,[]), - file:set_cwd(Cwd), + ok = file:set_cwd(Cwd), Result; {stop_time,StopTime} -> Result = @@ -76,7 +76,7 @@ loop_test(If,Args) when is_list(Args) -> Args1 = [{loop_info,[{stop_time,Secs,StopTime,1}]} | Args], loop(If,stop_time,0,Secs,StopTime,Args1,TPid,[]) end, - file:set_cwd(Cwd), + ok = file:set_cwd(Cwd), Result end. diff --git a/lib/common_test/src/ct_rpc.erl b/lib/common_test/src/ct_rpc.erl index cbcc212bb8..b4a0bc9d74 100644 --- a/lib/common_test/src/ct_rpc.erl +++ b/lib/common_test/src/ct_rpc.erl @@ -81,7 +81,7 @@ app_node(App, [], _, _) -> app_node(App, _Candidates = [CandidateNode | Nodes], FailOnBadRPC, Cookie) -> Cookie0 = set_the_cookie(Cookie), Result = rpc:call(CandidateNode, application, which_applications, []), - set_the_cookie(Cookie0), + _ = set_the_cookie(Cookie0), case Result of {badrpc,Reason} when FailOnBadRPC == true -> ct:fail({Reason,CandidateNode}); @@ -145,7 +145,7 @@ call({Fun, FunArgs}, Module, Function, Args, TimeOut, Cookie) -> call(Node, Module, Function, Args, TimeOut, Cookie) when is_atom(Node) -> Cookie0 = set_the_cookie(Cookie), Result = rpc:call(Node, Module, Function, Args, TimeOut), - set_the_cookie(Cookie0), + _ = set_the_cookie(Cookie0), Result. %%% @spec cast(Node, Module, Function, Args) -> ok @@ -190,7 +190,7 @@ cast({Fun, FunArgs}, Module, Function, Args, Cookie) -> cast(Node, Module, Function, Args, Cookie) when is_atom(Node) -> Cookie0 = set_the_cookie(Cookie), true = rpc:cast(Node, Module, Function, Args), - set_the_cookie(Cookie0), + _ = set_the_cookie(Cookie0), ok. diff --git a/lib/common_test/src/ct_run.erl b/lib/common_test/src/ct_run.erl index 1e5f935198..fbb9c7ab60 100644 --- a/lib/common_test/src/ct_run.erl +++ b/lib/common_test/src/ct_run.erl @@ -400,21 +400,21 @@ run_or_refresh(Opts = #opts{logdir = LogDir}, Args) -> [RefreshDir] -> ?abs(RefreshDir) end, {ok,Cwd} = file:get_cwd(), - file:set_cwd(LogDir1), + ok = file:set_cwd(LogDir1), %% give the shell time to print version etc timer:sleep(500), io:nl(), case catch ct_logs:make_all_runs_index(refresh) of {'EXIT',ARReason} -> - file:set_cwd(Cwd), + ok = file:set_cwd(Cwd), {error,{all_runs_index,ARReason}}; _ -> case catch ct_logs:make_all_suites_index(refresh) of {'EXIT',ASReason} -> - file:set_cwd(Cwd), + ok = file:set_cwd(Cwd), {error,{all_suites_index,ASReason}}; _ -> - file:set_cwd(Cwd), + ok = file:set_cwd(Cwd), io:format("Logs in ~ts refreshed!~n~n", [LogDir1]), timer:sleep(500), % time to flush io before quitting @@ -756,8 +756,8 @@ script_start4(#opts{label = Label, profile = Profile, {ct_hooks, CTHooks}, {enable_builtin_hooks,EnableBuiltinHooks}]) of ok -> - ct_util:start(interactive, LogDir, - add_verbosity_defaults(Verbosity)), + _ = ct_util:start(interactive, LogDir, + add_verbosity_defaults(Verbosity)), ct_util:set_testdata({logopts, LogOpts}), log_ts_names(Specs), io:nl(), @@ -901,9 +901,8 @@ install(Opts, LogDir) -> VarFile = variables_file_name(LogDir), case file:open(VarFile, [write]) of {ok,Fd} -> - [io:format(Fd, "~p.\n", [Opt]) || Opt <- ConfOpts ], - file:close(Fd), - ok; + _ = [io:format(Fd, "~p.\n", [Opt]) || Opt <- ConfOpts], + ok = file:close(Fd); {error,Reason} -> io:format("CT failed to install configuration data. Please " "verify that the log directory exists and that " @@ -960,7 +959,7 @@ run_test1(StartOpts) when is_list(StartOpts) -> false -> case catch run_test2(StartOpts) of {'EXIT',Reason} -> - file:set_cwd(Cwd), + ok = file:set_cwd(Cwd), {error,Reason}; Result -> Result @@ -971,7 +970,7 @@ run_test1(StartOpts) when is_list(StartOpts) -> stop_trace(Tracing), exit(Res); RefreshDir -> - refresh_logs(?abs(RefreshDir)), + ok = refresh_logs(?abs(RefreshDir)), exit(done) end. @@ -1209,7 +1208,6 @@ run_all_specs([], _, _, TotResult) -> end; run_all_specs([{Specs,TS} | TSs], Opts, StartOpts, TotResult) -> - log_ts_names(Specs), Combined = #opts{config = TSConfig} = combine_test_opts(TS, Specs, Opts), AllConfig = merge_vals([Opts#opts.config, TSConfig]), try run_one_spec(TS, @@ -1430,7 +1428,7 @@ run_testspec1(TestSpec) -> io:format("~nCommon Test starting (cwd is ~ts)~n~n", [Cwd]), case catch run_testspec2(TestSpec) of {'EXIT',Reason} -> - file:set_cwd(Cwd), + ok = file:set_cwd(Cwd), exit({error,Reason}); Result -> exit(Result) @@ -1562,15 +1560,15 @@ refresh_logs(LogDir) -> _ -> case catch ct_logs:make_all_suites_index(refresh) of {'EXIT',ASReason} -> - file:set_cwd(Cwd), + ok = file:set_cwd(Cwd), {error,{all_suites_index,ASReason}}; _ -> case catch ct_logs:make_all_runs_index(refresh) of {'EXIT',ARReason} -> - file:set_cwd(Cwd), + ok = file:set_cwd(Cwd), {error,{all_runs_index,ARReason}}; _ -> - file:set_cwd(Cwd), + ok = file:set_cwd(Cwd), io:format("Logs in ~ts refreshed!~n",[LogDir]), ok end @@ -1610,22 +1608,34 @@ delistify(E) -> E. %%% @hidden %%% @equiv ct:run/3 run(TestDir, Suite, Cases) -> - install([]), - reformat_result(catch do_run(tests(TestDir, Suite, Cases), [])). + case install([]) of + ok -> + reformat_result(catch do_run(tests(TestDir, Suite, Cases), [])); + Error -> + Error + end. %%%----------------------------------------------------------------- %%% @hidden %%% @equiv ct:run/2 run(TestDir, Suite) when is_list(TestDir), is_integer(hd(TestDir)) -> - install([]), - reformat_result(catch do_run(tests(TestDir, Suite), [])). + case install([]) of + ok -> + reformat_result(catch do_run(tests(TestDir, Suite), [])); + Error -> + Error + end. %%%----------------------------------------------------------------- %%% @hidden %%% @equiv ct:run/1 run(TestDirs) -> - install([]), - reformat_result(catch do_run(tests(TestDirs), [])). + case install([]) of + ok -> + reformat_result(catch do_run(tests(TestDirs), [])); + Error -> + Error + end. reformat_result({'EXIT',{user_error,Reason}}) -> {error,Reason}; @@ -2017,7 +2027,7 @@ save_make_errors(Errors) -> "Error compiling or locating the " "following suites: ~n~p",[Suites]), %% save the info for logger - file:write_file(?missing_suites_info,term_to_binary(Errors)), + ok = file:write_file(?missing_suites_info,term_to_binary(Errors)), Errors. get_bad_suites([{{_TestDir,_Suite},Failed}|Errors], BadSuites) -> @@ -2234,7 +2244,7 @@ do_run_test(Tests, Skip, Opts0) -> end, application:set_env(test_server, esc_chars, EscChars), - test_server_ctrl:start_link(local), + {ok, _} = test_server_ctrl:start_link(local), %% let test_server expand the test tuples and count no of cases {Suites,NoOfCases} = count_test_cases(Tests, Skip), @@ -2295,7 +2305,7 @@ do_run_test(Tests, Skip, Opts0) -> lists:foreach(fun(Suite) -> maybe_cleanup_interpret(Suite, Opts#opts.step) end, CleanUp), - [code:del_path(Dir) || Dir <- AddedToPath], + _ = [code:del_path(Dir) || Dir <- AddedToPath], %% If a severe error has occurred in the test_server, %% we will generate an exception here. @@ -2422,7 +2432,7 @@ count_test_cases(Tests, Skip) -> SendResult = fun(Me, Result) -> Me ! {no_of_cases,Result} end, TSPid = test_server_ctrl:start_get_totals(SendResult), Ref = erlang:monitor(process, TSPid), - add_jobs(Tests, Skip, #opts{}, []), + _ = add_jobs(Tests, Skip, #opts{}, []), Counted = (catch count_test_cases1(length(Tests), 0, [], Ref)), erlang:demonitor(Ref, [flush]), case Counted of @@ -2776,14 +2786,14 @@ maybe_interpret1(Suite, Cases, StepOpts) when is_list(Cases) -> maybe_interpret2(Suite, Cases, StepOpts) -> set_break_on_config(Suite, StepOpts), - [begin try i:ib(Suite, Case, 1) of + _ = [begin try i:ib(Suite, Case, 1) of _ -> ok catch _:_Error -> io:format(user, "Invalid breakpoint: ~w:~w/1~n", [Suite,Case]) end - end || Case <- Cases, is_atom(Case)], + end || Case <- Cases, is_atom(Case)], test_server_ctrl:multiply_timetraps(infinity), WinOp = case lists:member(keep_inactive, ensure_atom(StepOpts)) of true -> no_kill; @@ -2802,12 +2812,12 @@ set_break_on_config(Suite, StepOpts) -> false -> ok end end, - SetBPIfExists(init_per_suite, 1), - SetBPIfExists(init_per_group, 2), - SetBPIfExists(init_per_testcase, 2), - SetBPIfExists(end_per_testcase, 2), - SetBPIfExists(end_per_group, 2), - SetBPIfExists(end_per_suite, 1); + ok = SetBPIfExists(init_per_suite, 1), + ok = SetBPIfExists(init_per_group, 2), + ok = SetBPIfExists(init_per_testcase, 2), + ok = SetBPIfExists(end_per_testcase, 2), + ok = SetBPIfExists(end_per_group, 2), + ok = SetBPIfExists(end_per_suite, 1); false -> ok end. @@ -2984,31 +2994,31 @@ add_verbosity_defaults(VLvls) -> %% relative dirs "post run_test erl_args" is not kept! rel_to_abs(CtArgs) -> {PA,PZ} = get_pa_pz(CtArgs, [], []), - [begin + _ = [begin Dir = rm_trailing_slash(D), Abs = make_abs(Dir), - if Dir /= Abs -> - code:del_path(Dir), - code:del_path(Abs), + _ = if Dir /= Abs -> + _ = code:del_path(Dir), + _ = code:del_path(Abs), io:format(user, "Converting ~p to ~p and re-inserting " "with add_pathz/1~n", [Dir, Abs]); true -> - code:del_path(Dir) + _ = code:del_path(Dir) end, code:add_pathz(Abs) end || D <- PZ], - [begin + _ = [begin Dir = rm_trailing_slash(D), Abs = make_abs(Dir), - if Dir /= Abs -> - code:del_path(Dir), - code:del_path(Abs), + _ = if Dir /= Abs -> + _ = code:del_path(Dir), + _ = code:del_path(Abs), io:format(user, "Converting ~p to ~p and re-inserting " "with add_patha/1~n", [Dir, Abs]); true -> - code:del_path(Dir) + _ = code:del_path(Dir) end, code:add_patha(Abs) end || D <- PA], diff --git a/lib/common_test/src/ct_slave.erl b/lib/common_test/src/ct_slave.erl index 3ad3937548..571958ca03 100644 --- a/lib/common_test/src/ct_slave.erl +++ b/lib/common_test/src/ct_slave.erl @@ -325,7 +325,7 @@ do_start(Host, Node, Options) -> Functions end, MasterHost = gethostname(), - if + _ = if MasterHost == Host -> spawn_local_node(Node, Options); true-> @@ -359,7 +359,7 @@ do_start(Host, Node, Options) -> pang-> {error, boot_timeout, ENode} end, - case Result of + _ = case Result of {ok, ENode}-> ok; {error, Timeout, ENode} @@ -422,7 +422,7 @@ spawn_remote_node(Host, Node, Options) -> {_, _}-> [{user, Username}, {password, Password}] end ++ [{silently_accept_hosts, true}] ++ SSHOpts, - application:ensure_all_started(ssh), + {ok, _} = application:ensure_all_started(ssh), {ok, SSHConnRef} = ssh:connect(atom_to_list(Host), SSHPort, SSHOptions), {ok, SSHChannelId} = ssh_connection:session_channel(SSHConnRef, infinity), ssh_setenv(SSHConnRef, SSHChannelId, Env), diff --git a/lib/common_test/src/ct_snmp.erl b/lib/common_test/src/ct_snmp.erl index bb0167eb22..2c59b19196 100644 --- a/lib/common_test/src/ct_snmp.erl +++ b/lib/common_test/src/ct_snmp.erl @@ -221,9 +221,17 @@ start(Config, MgrAgentConfName, SnmpAppConfName) -> Config, SysName, AgentManagerIP, IP), setup_manager(StartManager, MgrAgentConfName, SnmpAppConfName, Config, AgentManagerIP), - application:start(snmp), + ok = start_application(snmp), manager_register(StartManager, MgrAgentConfName). + +start_application(App) -> + case application:start(App) of + {error, {already_started, App}} -> + ok; + Else -> + Else + end. %%% @spec stop(Config) -> ok %%% Config = [{Key, Value}] @@ -233,8 +241,8 @@ start(Config, MgrAgentConfName, SnmpAppConfName) -> %%% @doc Stops the snmp manager and/or agent removes all files created. stop(Config) -> PrivDir = ?config(priv_dir, Config), - application:stop(snmp), - application:stop(mnesia), + ok = application:stop(snmp), + ok = application:stop(mnesia), MgrDir = filename:join(PrivDir,"mgr"), ConfDir = filename:join(PrivDir, "conf"), DbDir = filename:join(PrivDir,"db"), @@ -311,7 +319,7 @@ set_info(Config) -> SetLogFile = filename:join(PrivDir, ?CT_SNMP_LOG_FILE), case file:consult(SetLogFile) of {ok, SetInfo} -> - file:delete(SetLogFile), + ok = delete_file(SetLogFile), lists:reverse(SetInfo); _ -> [] @@ -513,7 +521,7 @@ unload_mibs(Mibs) -> prepare_snmp_env() -> %% To make sure application:set_env is not overwritten by any %% app-file settings. - application:load(snmp), + _ = application:load(snmp), %% Fix for older versions of snmp where there are some %% inappropriate default values for alway starting an @@ -533,7 +541,7 @@ setup_manager(true, MgrConfName, SnmpConfName, Config, IP) -> Users = [], Agents = [], Usms = [], - file:make_dir(MgrDir), + ok = make_dir(MgrDir), snmp_config:write_manager_snmp_files(MgrDir, IP, Port, MaxMsgSize, EngineID, Users, Agents, Usms), @@ -549,7 +557,7 @@ setup_agent(false,_, _, _, _, _, _) -> ok; setup_agent(true, AgentConfName, SnmpConfName, Config, SysName, ManagerIP, AgentIP) -> - application:start(mnesia), + ok = start_application(mnesia), PrivDir = ?config(priv_dir, Config), Vsns = ct:get_config({AgentConfName, agent_vsns}, ?CONF_FILE_VER), TrapUdp = ct:get_config({AgentConfName, agent_trap_udp}, ?TRAP_UDP), @@ -565,8 +573,8 @@ setup_agent(true, AgentConfName, SnmpConfName, ConfDir = filename:join(PrivDir, "conf"), DbDir = filename:join(PrivDir,"db"), - file:make_dir(ConfDir), - file:make_dir(DbDir), + ok = make_dir(ConfDir), + ok = make_dir(DbDir), snmp_config:write_agent_snmp_files(ConfDir, Vsns, ManagerIP, TrapUdp, AgentIP, AgentUdp, SysName, NotifType, SecType, Passwd, @@ -684,7 +692,7 @@ log(PrivDir, Agent, {_, _, Varbinds}, NewVarsAndVals) -> File = filename:join(PrivDir, ?CT_SNMP_LOG_FILE), {ok, Fd} = file:open(File, [write, append]), io:format(Fd, "~p.~n", [{Agent, OldVarsAndVals, NewVarsAndVals}]), - file:close(Fd), + ok = file:close(Fd), ok. %%%--------------------------------------------------------------------------- del_dir(Dir) -> @@ -692,7 +700,7 @@ del_dir(Dir) -> FullPathFiles = lists:map(fun(File) -> filename:join(Dir, File) end, Files), lists:foreach(fun file:delete/1, FullPathFiles), - file:del_dir(Dir), + ok = delete_dir(Dir), ok. %%%--------------------------------------------------------------------------- agent_conf(Agent, MgrAgentConfName) -> @@ -738,8 +746,8 @@ override_contexts(Config, {data_dir_file, File}) -> override_contexts(Config, Contexts) -> Dir = filename:join(?config(priv_dir, Config),"conf"), File = filename:join(Dir,"context.conf"), - file:delete(File), - snmp_config:write_agent_context_config(Dir, "", Contexts). + ok = delete_file(File), + ok = snmp_config:write_agent_context_config(Dir, "", Contexts). %%%--------------------------------------------------------------------------- override_sysinfo(_, undefined) -> @@ -754,8 +762,8 @@ override_sysinfo(Config, {data_dir_file, File}) -> override_sysinfo(Config, SysInfo) -> Dir = filename:join(?config(priv_dir, Config),"conf"), File = filename:join(Dir,"standard.conf"), - file:delete(File), - snmp_config:write_agent_standard_config(Dir, "", SysInfo). + ok = delete_file(File), + ok = snmp_config:write_agent_standard_config(Dir, "", SysInfo). %%%--------------------------------------------------------------------------- override_target_address(_, undefined) -> @@ -769,8 +777,8 @@ override_target_address(Config, {data_dir_file, File}) -> override_target_address(Config, TargetAddressConf) -> Dir = filename:join(?config(priv_dir, Config),"conf"), File = filename:join(Dir,"target_addr.conf"), - file:delete(File), - snmp_config:write_agent_target_addr_config(Dir, "", TargetAddressConf). + ok = delete_file(File), + ok = snmp_config:write_agent_target_addr_config(Dir, "", TargetAddressConf). %%%--------------------------------------------------------------------------- @@ -785,8 +793,8 @@ override_target_params(Config, {data_dir_file, File}) -> override_target_params(Config, TargetParamsConf) -> Dir = filename:join(?config(priv_dir, Config),"conf"), File = filename:join(Dir,"target_params.conf"), - file:delete(File), - snmp_config:write_agent_target_params_config(Dir, "", TargetParamsConf). + ok = delete_file(File), + ok = snmp_config:write_agent_target_params_config(Dir, "", TargetParamsConf). %%%--------------------------------------------------------------------------- override_notify(_, undefined) -> @@ -800,8 +808,8 @@ override_notify(Config, {data_dir_file, File}) -> override_notify(Config, NotifyConf) -> Dir = filename:join(?config(priv_dir, Config),"conf"), File = filename:join(Dir,"notify.conf"), - file:delete(File), - snmp_config:write_agent_notify_config(Dir, "", NotifyConf). + ok = delete_file(File), + ok = snmp_config:write_agent_notify_config(Dir, "", NotifyConf). %%%--------------------------------------------------------------------------- override_usm(_, undefined) -> @@ -815,8 +823,8 @@ override_usm(Config, {data_dir_file, File}) -> override_usm(Config, UsmConf) -> Dir = filename:join(?config(priv_dir, Config),"conf"), File = filename:join(Dir,"usm.conf"), - file:delete(File), - snmp_config:write_agent_usm_config(Dir, "", UsmConf). + ok = delete_file(File), + ok = snmp_config:write_agent_usm_config(Dir, "", UsmConf). %%%-------------------------------------------------------------------------- override_community(_, undefined) -> @@ -830,8 +838,8 @@ override_community(Config, {data_dir_file, File}) -> override_community(Config, CommunityConf) -> Dir = filename:join(?config(priv_dir, Config),"conf"), File = filename:join(Dir,"community.conf"), - file:delete(File), - snmp_config:write_agent_community_config(Dir, "", CommunityConf). + ok = delete_file(File), + ok = snmp_config:write_agent_community_config(Dir, "", CommunityConf). %%%--------------------------------------------------------------------------- @@ -846,8 +854,8 @@ override_vacm(Config, {data_dir_file, File}) -> override_vacm(Config, VacmConf) -> Dir = filename:join(?config(priv_dir, Config),"conf"), File = filename:join(Dir,"vacm.conf"), - file:delete(File), - snmp_config:write_agent_vacm_config(Dir, "", VacmConf). + ok = delete_file(File), + ok = snmp_config:write_agent_vacm_config(Dir, "", VacmConf). %%%--------------------------------------------------------------------------- @@ -861,3 +869,21 @@ while_ok(Fun,[H|T]) -> end; while_ok(_Fun,[]) -> ok. + +delete_file(FileName) -> + case file:delete(FileName) of + {error, enoent} -> ok; + Else -> Else + end. + +make_dir(Dir) -> + case file:make_dir(Dir) of + {error, eexist} -> ok; + Else -> Else + end. + +delete_dir(Dir) -> + case file:del_dir(Dir) of + {error, enoent} -> ok; + Else -> Else + end. diff --git a/lib/common_test/src/ct_ssh.erl b/lib/common_test/src/ct_ssh.erl index 92d912052e..6ab3bf036c 100644 --- a/lib/common_test/src/ct_ssh.erl +++ b/lib/common_test/src/ct_ssh.erl @@ -962,8 +962,8 @@ init(KeyOrName, {ConnType,Addr,Port}, AllOpts) -> end, [], AllOpts1), FinalOptions = [{silently_accept_hosts,true}, {user_interaction,false} | Options], - crypto:start(), - ssh:start(), + _ = crypto:start(), + _ = ssh:start(), Result = case ConnType of ssh -> ssh:connect(Addr, Port, FinalOptions); diff --git a/lib/common_test/src/ct_telnet.erl b/lib/common_test/src/ct_telnet.erl index f5f4f648f4..8fb411ec4f 100644 --- a/lib/common_test/src/ct_telnet.erl +++ b/lib/common_test/src/ct_telnet.erl @@ -605,7 +605,7 @@ init(Name,{Ip,Port,Type},{TargetMod,KeepAlive,Extra}) -> set_telnet_defaults(Settings,#state{}) end, %% Handle old user versions of TargetMod - code:ensure_loaded(TargetMod), + _ = code:ensure_loaded(TargetMod), try case erlang:function_exported(TargetMod,connect,7) of true -> @@ -688,7 +688,7 @@ handle_msg({cmd,Cmd,Opts},State) -> debug_cont_gen_log("Throwing Buffer:",[]), debug_log_lines(State#state.buffer), - case {State#state.type,State#state.prompt} of + _ = case {State#state.type,State#state.prompt} of {ts,_} -> silent_teln_expect(State#state.name, State#state.teln_pid, @@ -735,7 +735,7 @@ handle_msg({send,Cmd,Opts},State) -> debug_cont_gen_log("Throwing Buffer:",[]), debug_log_lines(State#state.buffer), - case {State#state.type,State#state.prompt} of + _ = case {State#state.type,State#state.prompt} of {ts,_} -> silent_teln_expect(State#state.name, State#state.teln_pid, diff --git a/lib/common_test/src/ct_telnet_client.erl b/lib/common_test/src/ct_telnet_client.erl index 1f1311776f..5df7e279ac 100644 --- a/lib/common_test/src/ct_telnet_client.erl +++ b/lib/common_test/src/ct_telnet_client.erl @@ -272,7 +272,7 @@ send(Data, Sock, ConnName) -> _:_ -> ok end end, - gen_tcp:send(Sock, Data), + ok = gen_tcp:send(Sock, Data), ok. %% [IAC,IAC] = buffer data value 255 @@ -284,7 +284,7 @@ check_msg(Sock, [?IAC | Cs], Acc) -> case get_cmd(Cs) of {Cmd,Cs1} -> cmd_dbg("Got",Cmd), - respond_cmd(Cmd, Sock), + ok = respond_cmd(Cmd, Sock), check_msg(Sock, Cs1, Acc); error -> Acc diff --git a/lib/common_test/src/ct_util.erl b/lib/common_test/src/ct_util.erl index d5a8e3fbc0..82a8743cf0 100644 --- a/lib/common_test/src/ct_util.erl +++ b/lib/common_test/src/ct_util.erl @@ -131,14 +131,14 @@ do_start(Parent, Mode, LogDir, Verbosity) -> create_table(?suite_table,#suite_data.key), create_table(?verbosity_table,1), - [ets:insert(?verbosity_table,{Cat,Lvl}) || {Cat,Lvl} <- Verbosity], + _ = [ets:insert(?verbosity_table,{Cat,Lvl}) || {Cat,Lvl} <- Verbosity], {ok,StartDir} = file:get_cwd(), case file:set_cwd(LogDir) of ok -> ok; E -> exit(E) end, - DoExit = fun(Reason) -> file:set_cwd(StartDir), exit(Reason) end, + DoExit = fun(Reason) -> ok = file:set_cwd(StartDir), exit(Reason) end, Opts = case read_opts() of {ok,Opts1} -> Opts1; @@ -169,7 +169,7 @@ do_start(Parent, Mode, LogDir, Verbosity) -> end, %% add user event handlers - case lists:keysearch(event_handler,1,Opts) of + _ = case lists:keysearch(event_handler,1,Opts) of {value,{_,Handlers}} -> Add = fun({H,Args}) -> case catch gen_event:add_handler(?CT_EVMGR_REF,H,Args) of @@ -195,7 +195,7 @@ do_start(Parent, Mode, LogDir, Verbosity) -> data={StartTime, lists:flatten(TestLogDir)}}), %% Initialize ct_hooks - try ct_hooks:init(Opts) of + _ = try ct_hooks:init(Opts) of ok -> Parent ! {self(),started}; {fail,CTHReason} -> @@ -228,7 +228,8 @@ create_table(TableName,KeyPos) -> create_table(TableName,set,KeyPos). create_table(TableName,Type,KeyPos) -> catch ets:delete(TableName), - ets:new(TableName,[Type,named_table,public,{keypos,KeyPos}]). + _ = ets:new(TableName,[Type,named_table,public,{keypos,KeyPos}]), + ok. read_opts() -> case file:consult(ct_run:variables_file_name("./")) of @@ -473,7 +474,7 @@ loop(Mode,TestData,StartDir) -> ct_logs:close(Info, StartDir), ct_event:stop(), ct_config:stop(), - file:set_cwd(StartDir), + ok = file:set_cwd(StartDir), return(From, Info); {Ref, _Msg} when is_reference(Ref) -> %% This clause is used when doing cast operations. @@ -505,7 +506,7 @@ loop(Mode,TestData,StartDir) -> %% Let process crash in case of error, this shouldn't happen! io:format("\n\nct_util_server got EXIT " "from ~w: ~p\n\n", [Pid,Reason]), - file:set_cwd(StartDir), + ok = file:set_cwd(StartDir), exit(Reason) end end. @@ -1035,10 +1036,12 @@ call(Msg, Timeout) -> end. return({To,Ref},Result) -> - To ! {Ref, Result}. + To ! {Ref, Result}, + ok. cast(Msg) -> - ct_util_server ! {Msg, {ct_util_server, make_ref()}}. + ct_util_server ! {Msg, {ct_util_server, make_ref()}}, + ok. seconds(T) -> test_server:seconds(T). @@ -1074,7 +1077,7 @@ abs_name2([],Acc) -> open_url(iexplore, Args, URL) -> {ok,R} = win32reg:open([read]), ok = win32reg:change_key(R,"applications\\iexplore.exe\\shell\\open\\command"), - case win32reg:values(R) of + _ = case win32reg:values(R) of {ok, Paths} -> Path = proplists:get_value(default, Paths), [Cmd | _] = string:tokens(Path, "%"), diff --git a/lib/common_test/src/ct_webtool.erl b/lib/common_test/src/ct_webtool.erl index 6cbcd98cc3..87af442fd3 100644 --- a/lib/common_test/src/ct_webtool.erl +++ b/lib/common_test/src/ct_webtool.erl @@ -84,27 +84,27 @@ %% Function = {FunctionName,Arity} | FunctionName | %% {Module, FunctionName, Arity} | {Module,FunctionName} debug(F) -> - ttb:tracer(all,[{file,"webtool.trc"}]), % tracing all nodes - ttb:p(all,[call,timestamp]), + {ok, _} = ttb:tracer(all,[{file,"webtool.trc"}]), % tracing all nodes + {ok, _} = ttb:p(all,[call,timestamp]), MS = [{'_',[],[{return_trace},{message,{caller}}]}], - tp(F,MS), - ttb:ctp(?MODULE,stop_debug), % don't want tracing of the stop_debug func + _ = 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 - ttb:tpl(M,F,A,MS), + {ok, _} = ttb:tpl(M,F,A,MS), tp(T,MS); tp([{M,F}|T],MS) when is_atom(F) -> % Other module - ttb:tpl(M,F,MS), + {ok, _} = ttb:tpl(M,F,MS), tp(T,MS); tp([{F,A}|T],MS) -> % function/arity - ttb:tpl(?MODULE,F,A,MS), + {ok, _} = ttb:tpl(?MODULE,F,A,MS), tp(T,MS); tp([F|T],MS) -> % function - ttb:tpl(?MODULE,F,MS), + {ok, _} = ttb:tpl(?MODULE,F,MS), tp(T,MS); tp([],_MS) -> ok. @@ -112,10 +112,10 @@ stop_debug() -> ttb:stop([format]). debug_app(Mod) -> - ttb:tracer(all,[{file,"webtool_app.trc"},{handler,{fun out/4,true}}]), - ttb:p(all,[call,timestamp]), + {ok, _} = ttb:tracer(all,[{file,"webtool_app.trc"},{handler,{fun out/4,true}}]), + {ok, _} = ttb:p(all,[call,timestamp]), MS = [{'_',[],[{return_trace},{message,{caller}}]}], - ttb:tp(Mod,MS), + {ok, _} = ttb:tp(Mod,MS), ok. out(_,{trace_ts,Pid,call,MFA={M,F,A},{W,_,_},TS},_,S) @@ -145,7 +145,7 @@ script_start([App]) -> script_start([App,DefaultBrowser]); script_start([App,Browser]) -> io:format("Starting webtool...\n"), - start(), + {ok, _} = start(), AvailableApps = get_applications(), {OSType,_} = os:type(), case lists:keysearch(App,1,AvailableApps) of @@ -159,14 +159,14 @@ script_start([App,Browser]) -> _ -> "http://localhost:" ++ PortStr ++ "/" ++ StartPage end, - case Browser of + _ = 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", - win32reg:change_key(R,Key), + ok = win32reg:change_key(R,Key), {ok,Val} = win32reg:value(R,"Path"), IExplore=filename:join(win32reg:expand(Val),"iexplore.exe"), os:cmd("\"" ++ IExplore ++ "\" " ++ Url); @@ -186,7 +186,7 @@ script_start([App,Browser]) -> {Port,{exit_status,_Error}} -> io:format(" not running, starting ~w...\n", [Browser]), - os:cmd(BStr ++ " " ++ Url), + _ = os:cmd(BStr ++ " " ++ Url), ok after ?SEND_URL_TIMEOUT -> io:format(" failed, starting ~w...\n",[Browser]), @@ -206,7 +206,7 @@ script_start([App,Browser]) -> usage() -> io:format("Starting webtool...\n"), - start(), + {ok, _} = start(), Apps = lists:map(fun({A,_}) -> A end,get_applications()), io:format( "\nUsage: start_webtool application [ browser ]\n" @@ -254,7 +254,12 @@ start(Path,Port) when is_integer(Port)-> start(Path,Data0)-> Data = Data0 ++ rest_of_standard_data(), - gen_server:start({local,ct_web_tool},ct_webtool,{Path,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). diff --git a/lib/common_test/src/cth_conn_log.erl b/lib/common_test/src/cth_conn_log.erl index 0501c6ed1c..883da0da0a 100644 --- a/lib/common_test/src/cth_conn_log.erl +++ b/lib/common_test/src/cth_conn_log.erl @@ -170,7 +170,7 @@ post_end_per_testcase(TestCase,_Config,Return,CthState) -> end, case ct_util:update_testdata(?MODULE, Update) of deleted -> - [ct_util:delete_testdata({?MODULE,ConnMod}) || + _ = [ct_util:delete_testdata({?MODULE,ConnMod}) || {ConnMod,_} <- CthState], error_logger:delete_report_handler(ct_conn_log_h); {error,no_response} -> diff --git a/lib/common_test/src/erl2html2.erl b/lib/common_test/src/erl2html2.erl index a1e0bf3879..e819f345de 100644 --- a/lib/common_test/src/erl2html2.erl +++ b/lib/common_test/src/erl2html2.erl @@ -62,15 +62,15 @@ convert(File, Dest, InclPath, Header) -> {ok,SFd} -> case file:open(Dest,[write,raw]) of {ok,DFd} -> - file:write(DFd,[Header,"<pre>\n"]), + ok = file:write(DFd,[Header,"<pre>\n"]), _Lines = build_html(SFd,DFd,encoding(File),Functions), - file:write(DFd,["</pre>\n",footer(), + ok = file:write(DFd,["</pre>\n",footer(), "</body>\n</html>\n"]), %% {_, Time2} = statistics(runtime), %% io:format("Converted ~p lines in ~.2f Seconds.~n", %% [_Lines, Time2/1000]), - file:close(SFd), - file:close(DFd), + ok = file:close(SFd), + ok = file:close(DFd), ok; Error -> Error @@ -138,7 +138,7 @@ parse_non_preprocessed_file(File) -> case file:open(File, []) of {ok,Epp} -> Forms = parse_non_preprocessed_file(Epp, File, 1), - file:close(Epp), + ok = file:close(Epp), {ok,Forms}; Error = {error,_E} -> Error @@ -205,14 +205,14 @@ build_html(SFd,DFd,Encoding,FuncsAndCs) -> %% line of last expression in function found build_html(SFd,DFd,Enc,{ok,Str},LastL,FuncsAndCs,_IsFuncDef,{F,LastL}) -> LastLineLink = test_server_ctrl:uri_encode(F++"-last_expr",utf8), - file:write(DFd,["<a name=\"", - to_raw_list(LastLineLink,Enc),"\"/>"]), + ok = file:write(DFd,["<a name=\"", + to_raw_list(LastLineLink,Enc),"\"/>"]), build_html(SFd,DFd,Enc,{ok,Str},LastL,FuncsAndCs,true,undefined); %% function start line found build_html(SFd,DFd,Enc,{ok,Str},L0,[{F,A,L0,LastL}|FuncsAndCs], _IsFuncDef,_FAndLastL) -> FALink = test_server_ctrl:uri_encode(F++"-"++integer_to_list(A),utf8), - file:write(DFd,["<a name=\"",to_raw_list(FALink,Enc),"\"/>"]), + ok = file:write(DFd,["<a name=\"",to_raw_list(FALink,Enc),"\"/>"]), build_html(SFd,DFd,Enc,{ok,Str},L0,FuncsAndCs,true,{F,LastL}); build_html(SFd,DFd,Enc,{ok,Str},L,[{clause,L}|FuncsAndCs], _IsFuncDef,FAndLastL) -> @@ -220,7 +220,7 @@ build_html(SFd,DFd,Enc,{ok,Str},L,[{clause,L}|FuncsAndCs], build_html(SFd,DFd,Enc,{ok,Str},L,FuncsAndCs,IsFuncDef,FAndLastL) -> LStr = line_number(L), Str1 = line(Str,IsFuncDef), - file:write(DFd,[LStr,Str1]), + ok = file:write(DFd,[LStr,Str1]), build_html(SFd,DFd,Enc,file:read_line(SFd),L+1,FuncsAndCs,false,FAndLastL); build_html(_SFd,_DFd,_Enc,eof,L,_FuncsAndCs,_IsFuncDef,_FAndLastL) -> L. diff --git a/lib/common_test/src/test_server.erl b/lib/common_test/src/test_server.erl index 919526c5d7..924086f2bd 100644 --- a/lib/common_test/src/test_server.erl +++ b/lib/common_test/src/test_server.erl @@ -21,7 +21,7 @@ -define(DEFAULT_TIMETRAP_SECS, 60). %%% TEST_SERVER_CTRL INTERFACE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --export([run_test_case_apply/1,init_target_info/0,init_purify/0]). +-export([run_test_case_apply/1,init_target_info/0]). -export([cover_compile/1,cover_analyse/2]). %%% TEST_SERVER_SUP INTERFACE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -49,10 +49,6 @@ -export([break/1,break/2,break/3,continue/0,continue/1]). -%%% DEBUGGER INTERFACE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --export([purify_new_leaks/0, purify_format/2, purify_new_fds_inuse/0, - purify_is_running/0]). - %%% PRIVATE EXPORTED %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -export([]). @@ -73,10 +69,6 @@ init_target_info() -> username=test_server_sup:get_username(), cookie=atom_to_list(erlang:get_cookie())}. -init_purify() -> - purify_new_leaks(). - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% cover_compile(#cover{app=App,incl=Include,excl=Exclude,cross=Cross}) -> %% {ok,#cover{mods=AnalyseModules}} | {error,Reason} @@ -100,7 +92,7 @@ cover_compile(CoverInfo=#cover{app=none,incl=Include,cross=Cross}) -> case length(CompileMods) of 0 -> io:fwrite("WARNING: No modules to cover compile!\n\n",[]), - cover:start(), % start cover server anyway + {ok, _} = start_cover(), % start cover server anyway {ok,CoverInfo#cover{mods=[]}}; N -> io:fwrite("Cover compiling ~w modules - " @@ -115,7 +107,7 @@ cover_compile(CoverInfo=#cover{app=App,excl=all,incl=Include,cross=Cross}) -> case length(CompileMods) of 0 -> io:fwrite("WARNING: No modules to cover compile!\n\n",[]), - cover:start(), % start cover server anyway + {ok, _} = start_cover(), % start cover server anyway {ok,CoverInfo#cover{mods=[]}}; N -> io:fwrite("Cover compiling '~w' (~w files) - " @@ -158,7 +150,7 @@ cover_compile(CoverInfo=#cover{app=App,excl=Exclude, case length(CompileMods) of 0 -> io:fwrite("WARNING: No modules to cover compile!\n\n",[]), - cover:start(), % start cover server anyway + {ok, _} = start_cover(), % start cover server anyway {ok,CoverInfo#cover{mods=[]}}; N -> io:fwrite("Cover compiling '~w' (~w files) - " @@ -175,11 +167,11 @@ module_names(Beams) -> do_cover_compile(Modules) -> - cover:start(), + {ok, _} = start_cover(), Sticky = prepare_cover_compile(Modules,[]), R = cover:compile_beam(Modules), - [warn_compile(Error) || Error <- R,element(1,Error)=/=ok], - [code:stick_mod(M) || M <- Sticky], + _ = [warn_compile(Error) || Error <- R,element(1,Error)=/=ok], + _ = [code:stick_mod(M) || M <- Sticky], ok. warn_compile({error,{Reason,Module}}) -> @@ -366,9 +358,7 @@ stick_all_sticky(Node,Sticky) -> %% compensate timetraps for runtime delays introduced by e.g. tools like %% cover. -run_test_case_apply({CaseNum,Mod,Func,Args,Name, - RunInit,TimetrapData}) -> - purify_format("Test case #~w ~w:~w/1", [CaseNum, Mod, Func]), +run_test_case_apply({Mod,Func,Args,Name,RunInit,TimetrapData}) -> case os:getenv("TS_RUN_VALGRIND") of false -> ok; @@ -380,7 +370,6 @@ run_test_case_apply({CaseNum,Mod,Func,Args,Name, Result = run_test_case_apply(Mod, Func, Args, Name, RunInit, TimetrapData), ProcAft = erlang:system_info(process_count), - purify_new_leaks(), DetFail = get(test_server_detected_fail), {Result,DetFail,ProcBef,ProcAft}. @@ -585,7 +574,8 @@ run_test_case_msgloop(#st{ref=Ref,pid=Pid,end_conf_pid=EndConfPid0}=St0) -> {user_timetrap,Pid,_TrapTime,StartTime,E={user_timetrap_error,_},_} -> case update_user_timetraps(Pid, StartTime) of proceed -> - self() ! {abort_current_testcase,E,Pid}; + self() ! {abort_current_testcase,E,Pid}, + ok; ignore -> ok end, @@ -600,7 +590,8 @@ run_test_case_msgloop(#st{ref=Ref,pid=Pid,end_conf_pid=EndConfPid0}=St0) -> true -> TrapTime end, - timetrap(TrapTime, TotalTime, Pid, Scale); + _ = timetrap(TrapTime, TotalTime, Pid, Scale), + ok; ignore -> ok end, @@ -724,7 +715,7 @@ do_call_end_conf(Starter,Mod,Func,Data,TCExitReason,Conf,TVal) -> Supervisor = self(), EndConfApply = fun() -> - timetrap(TVal), + _ = timetrap(TVal), %% We can't handle fails or skips here %% (neither input nor output). The error can %% be read from Conf though (tc_status). @@ -775,7 +766,8 @@ print_end_conf_result(Mod,Func,Conf,Cause,Error) -> " ~s!\n\tReason: ~ts\n", [Mod,Func,Conf,Cause,ErrorStr]) end, - group_leader() ! {printout,12,Str2Print}. + group_leader() ! {printout,12,Str2Print}, + ok. spawn_fw_call(Mod,IPTC={init_per_testcase,Func},CurrConf,Pid, @@ -1287,7 +1279,9 @@ user_callback({CBMod,CBFunc}, Mod, Func, InitOrEnd, Args) -> init_per_testcase(Mod, Func, Args) -> case code:is_loaded(Mod) of - false -> code:load_file(Mod); + false -> + _ = code:load_file(Mod), + ok; _ -> ok end, case erlang:function_exported(Mod, init_per_testcase, 2) of @@ -1355,7 +1349,8 @@ print_init_conf_result(Line,Cause,Reason) -> "\tLocation: ~ts\n\tReason: ~ts\n", [Cause,FormattedLoc,ReasonStr]) end, - group_leader() ! {printout,12,Str2Print}. + group_leader() ! {printout,12,Str2Print}, + ok. end_per_testcase(Mod, Func, Conf) -> @@ -1426,7 +1421,8 @@ print_end_tc_warning(EndFunc,Reason,Cause,Loc) -> "Reason: ~ts\nLine: ~ts\n", [EndFunc,Cause,ReasonStr,FormattedLoc]) end, - group_leader() ! {printout,12,Str2Print}. + group_leader() ! {printout,12,Str2Print}, + ok. get_loc() -> get(test_server_loc). @@ -1657,7 +1653,8 @@ messages_get() -> %% %% Make sure proceeding IO from FromPid won't get rejected permit_io(GroupLeader, FromPid) -> - GroupLeader ! {permit_io,FromPid}. + GroupLeader ! {permit_io,FromPid}, + ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% sleep(Time) -> ok @@ -1829,7 +1826,6 @@ timetrap_scale_factor() -> timetrap_scale_factor([ { 2, fun() -> has_lock_checking() end}, { 3, fun() -> has_superfluous_schedulers() end}, - { 5, fun() -> purify_is_running() end}, { 6, fun() -> is_debug() end}, {10, fun() -> is_cover() end} ]). @@ -2129,7 +2125,8 @@ timetrap_cancel_all(TCPid, SendToServer) -> ok; Timers -> [timetrap_cancel_one(Handle, false) || - {Handle,Pid,_} <- Timers, Pid == TCPid] + {Handle,Pid,_} <- Timers, Pid == TCPid], + ok end, case get(test_server_user_timetrap) of undefined -> @@ -2139,13 +2136,15 @@ timetrap_cancel_all(TCPid, SendToServer) -> {UserTTSup,_StartTime} -> remove_user_timetrap(UserTTSup), put(test_server_user_timetrap, - proplists:delete(TCPid, UserTTs)); + proplists:delete(TCPid, UserTTs)), + ok; undefined -> ok end end, if SendToServer == true -> - group_leader() ! {timetrap_cancel_all,TCPid,self()}; + group_leader() ! {timetrap_cancel_all,TCPid,self()}, + ok; true -> ok end, @@ -2560,10 +2559,11 @@ run_on_shielded_node(Fun, CArgs) when is_function(Fun), is_list(CArgs) -> -spec start_job_proxy_fun(_, _) -> fun(() -> no_return()). start_job_proxy_fun(Master, Fun) -> fun () -> - start_job_proxy(), + _ = start_job_proxy(), receive Ref -> - Master ! {Ref, Fun()} + Master ! {Ref, Fun()}, + ok end, receive after infinity -> infinity end end. @@ -2729,64 +2729,25 @@ is_commercial() -> _ -> true end. -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% DEBUGGER INTERFACE %% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% purify_is_running() -> false|true -%% -%% Tests if Purify is currently running. - -purify_is_running() -> - case catch erlang:system_info({error_checker, running}) of - {'EXIT', _} -> false; - Res -> Res - end. - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% purify_new_leaks() -> false|BytesLeaked -%% BytesLeaked = integer() -%% -%% Checks for new memory leaks if Purify is active. -%% Returns the number of bytes leaked, or false if Purify -%% is not running. -purify_new_leaks() -> - case catch erlang:system_info({error_checker, memory}) of - {'EXIT', _} -> false; - Leaked when is_integer(Leaked) -> Leaked - end. - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% purify_new_fds_inuse() -> false|FdsInuse -%% FdsInuse = integer() -%% -%% Checks for new file descriptors in use. -%% Returns the number of new file descriptors in use, or false -%% if Purify is not running. -purify_new_fds_inuse() -> - case catch erlang:system_info({error_checker, fd}) of - {'EXIT', _} -> false; - Inuse when is_integer(Inuse) -> Inuse - end. - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% purify_format(Format, Args) -> ok -%% Format = string() -%% Args = lists() -%% -%% Outputs the formatted string to Purify's logfile,if Purify is active. -purify_format(Format, Args) -> - (catch erlang:system_info({error_checker, io_lib:format(Format, Args)})), - ok. - - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% Apply given function and reply to caller or proxy. %% do_sync_apply(Proxy, From, {M,F,A}) -> Result = apply(M, F, A), - if is_pid(Proxy) -> Proxy ! {sync_result_proxy,From,Result}; - true -> From ! {sync_result,Result} + if is_pid(Proxy) -> + Proxy ! {sync_result_proxy,From,Result}, + ok; + true -> + From ! {sync_result,Result}, + ok end. + +start_cover() -> + case cover:start() of + {error, {already_started, Pid}} -> + {ok, Pid}; + Else -> + Else + end. + diff --git a/lib/common_test/src/test_server_ctrl.erl b/lib/common_test/src/test_server_ctrl.erl index ff960c22a5..b52e4bef9b 100644 --- a/lib/common_test/src/test_server_ctrl.erl +++ b/lib/common_test/src/test_server_ctrl.erl @@ -294,7 +294,7 @@ start_link(_) -> start() -> case gen_server:start({local,?MODULE}, ?MODULE, [], []) of - {ok, Pid} -> + {error, {already_started, Pid}} -> {ok, Pid}; Other -> Other @@ -302,7 +302,7 @@ start() -> start_link() -> case gen_server:start_link({local,?MODULE}, ?MODULE, [], []) of - {ok, Pid} -> + {error, {already_started, Pid}} -> {ok, Pid}; Other -> Other @@ -512,7 +512,7 @@ init([]) -> TI = TI0#target_info{host=TargetHost, naming=naming(), master=TargetHost}, - ets:new(slave_tab, [named_table,set,public,{keypos,2}]), + _ = ets:new(slave_tab, [named_table,set,public,{keypos,2}]), set_hosts([TI#target_info.host]), {ok,State#state{target_info=TI}}. @@ -867,7 +867,7 @@ handle_call({create_priv_dir,Value}, _From, State) -> handle_call({testcase_callback,ModFunc}, _From, State) -> case ModFunc of {Mod,Func} -> - case code:is_loaded(Mod) of + _ = case code:is_loaded(Mod) of {file,_} -> ok; false -> @@ -1079,8 +1079,8 @@ terminate(_Reason, State) -> false -> ok; Sock -> test_server_node:stop_tracer_node(Sock) end, - kill_all_jobs(State#state.jobs), - test_server_node:kill_nodes(), + ok = kill_all_jobs(State#state.jobs), + _ = test_server_node:kill_nodes(), ok. kill_all_jobs([{_Name,JobPid}|Jobs]) -> @@ -1125,7 +1125,7 @@ spawn_tester(Mod, Func, Args, Dir, Name, Levels, RejectIoReqs, init_tester(Mod, Func, Args, Dir, Name, {_,_,MinLev}=Levels, RejectIoReqs, CreatePrivDir, TCCallback, ExtraTools) -> process_flag(trap_exit, true), - test_server_io:start_link(), + _ = test_server_io:start_link(), put(test_server_name, Name), put(test_server_dir, Dir), put(test_server_total_time, 0), @@ -1199,8 +1199,7 @@ init_tester(Mod, Func, Args, Dir, Name, {_,_,MinLev}=Levels, {UnexpectedIoName,UnexpectedIoFooter} = get(test_server_unexpected_footer), {ok,UnexpectedIoFd} = open_html_file(UnexpectedIoName, [append]), io:put_chars(UnexpectedIoFd, "\n</pre>\n"++UnexpectedIoFooter), - file:close(UnexpectedIoFd), - ok. + ok = file:close(UnexpectedIoFd). report_severe_error(Reason) -> test_server_sup:framework_call(report, [severe_error,Reason]). @@ -1927,7 +1926,7 @@ html_convert_modules([Mod|Mods]) -> Name = atom_to_list(Mod), DestFile = filename:join(DestDir, downcase(Name)++?src_listing_ext), - html_possibly_convert(SrcFile1, SrcFileInfo, DestFile), + _ = html_possibly_convert(SrcFile1, SrcFileInfo, DestFile), html_convert_modules(Mods) end; _Other -> @@ -2066,7 +2065,7 @@ add_init_and_end_per_suite([], LastMod, LastRef, FwMod) -> end. do_add_init_and_end_per_suite(LastMod, LastRef, Mod, FwMod) -> - case code:is_loaded(Mod) of + _ = case code:is_loaded(Mod) of false -> code:load_file(Mod); _ -> ok end, @@ -2140,7 +2139,6 @@ do_add_end_per_suite_and_skip(LastMod, LastRef, Mod, FwMod) -> %% Runs the specified tests, then displays/logs the summary. run_test_cases(TestSpec, Config, TimetrapData) -> - test_server:init_purify(), case lists:member(no_src, get(test_server_logopts)) of true -> ok; @@ -2323,7 +2321,7 @@ run_test_cases_loop([{SkipTag,{Type,Ref,Case,Comment},SkipMode}|Cases], Config, TimetrapData, Mode, Status) when ((SkipTag==auto_skip_case) or (SkipTag==skip_case)) and ((Type==conf) or (Type==make)) -> - file:set_cwd(filename:dirname(get(test_server_dir))), + ok = file:set_cwd(filename:dirname(get(test_server_dir))), CurrIOHandler = get(test_server_common_io_handler), ParentMode = tl(Mode), @@ -2339,7 +2337,7 @@ run_test_cases_loop([{SkipTag,{Type,Ref,Case,Comment},SkipMode}|Cases], false -> %% this is a skipped end conf for a top level parallel %% group, buffered io can be flushed - handle_test_case_io_and_status(), + _ = handle_test_case_io_and_status(), set_io_buffering(undefined), {Mod,Func} = skip_case(AutoOrUser, Ref, 0, Case, Comment, false, SkipMode), @@ -2351,7 +2349,7 @@ run_test_cases_loop([{SkipTag,{Type,Ref,Case,Comment},SkipMode}|Cases], _ -> %% this is a skipped end conf for a parallel group nested %% under a parallel group (io buffering is active) - wait_for_cases(Ref), + _ = wait_for_cases(Ref), {Mod,Func} = skip_case(AutoOrUser, Ref, 0, Case, Comment, true, SkipMode), ConfData = {Mod,{Func,get_name(SkipMode)},Comment}, @@ -2459,7 +2457,7 @@ run_test_cases_loop([{auto_skip_case,{Case,Comment},SkipMode}|Cases], run_test_cases_loop([{skip_case,{{Mod,all}=Case,Comment},SkipMode}|Cases], Config, TimetrapData, Mode, Status) -> - skip_case(user, undefined, 0, Case, Comment, false, SkipMode), + _ = skip_case(user, undefined, 0, Case, Comment, false, SkipMode), test_server_sup:framework_call(report, [tc_user_skip, {Mod,{all,get_name(SkipMode)}, Comment}]), @@ -2489,7 +2487,7 @@ run_test_cases_loop([{conf,Ref,Props,{Mod,Func}}|_Cases]=Cs0, %% collect results from the test case processes %% and calc total time OkSkipFail = handle_test_case_io_and_status(), - file:set_cwd(filename:dirname(get(test_server_dir))), + ok = file:set_cwd(filename:dirname(get(test_server_dir))), After = ?now, Before = get(test_server_parallel_start_time), Elapsed = timer:now_diff(After, Before)/1000000, @@ -3582,7 +3580,7 @@ handle_io_and_exit_loop([], [{undefined,CurrPid,CaseNum,Mod,Func}|Ps] = Cases, O handle_io_and_exit_loop(Refs, [{Ref,CurrPid,CaseNum,Mod,Func}|Ps] = Cases, Ok,Skip,Fail) -> receive {started,_,CurrPid,CaseNum,Mod,Func} -> - handle_io_and_exits(self(), CurrPid, CaseNum, Mod, Func, Cases), + _ = handle_io_and_exits(self(), CurrPid, CaseNum, Mod, Func, Cases), Refs1 = case Refs of [Ref|Rs] -> % must be end conf case for subgroup @@ -3658,7 +3656,7 @@ handle_io_and_exits(Main, CurrPid, CaseNum, Mod, Func, Cases) -> %% about the execution time and the return value of the test case function. run_test_case(Ref, Num, Mod, Func, Args, RunInit, TimetrapData) -> - file:set_cwd(filename:dirname(get(test_server_dir))), + ok = file:set_cwd(filename:dirname(get(test_server_dir))), run_test_case1(Ref, Num, Mod, Func, Args, RunInit, TimetrapData, [], self()). @@ -3668,7 +3666,7 @@ run_test_case(Ref, Num, Mod, Func, Args, skip_init, TimetrapData, Mode) -> TimetrapData, Mode, self()); run_test_case(Ref, Num, Mod, Func, Args, RunInit, TimetrapData, Mode) -> - file:set_cwd(filename:dirname(get(test_server_dir))), + ok = file:set_cwd(filename:dirname(get(test_server_dir))), Main = self(), case check_prop(parallel, Mode) of false -> @@ -3682,7 +3680,7 @@ run_test_case(Ref, Num, Mod, Func, Args, RunInit, TimetrapData, Mode) -> spawn_link( fun() -> process_flag(trap_exit, true), - [put(Key, Val) || {Key,Val} <- Dictionary], + _ = [put(Key, Val) || {Key,Val} <- Dictionary], set_io_buffering({tc,Main}), run_test_case1(Ref, Num, Mod, Func, Args, RunInit, TimetrapData, Mode, Main) @@ -3699,7 +3697,8 @@ run_test_case1(Ref, Num, Mod, Func, Args, RunInit, false -> ok; true -> test_server_io:start_transaction(), - Main ! {started,Ref,self(),Num,Mod,Func} + Main ! {started,Ref,self(),Num,Mod,Func}, + ok end, TSDir = get(test_server_dir), @@ -3774,7 +3773,7 @@ run_test_case1(Ref, Num, Mod, Func, Args, RunInit, %% run the test case {Result,DetectedFail,ProcsBefore,ProcsAfter} = - run_test_case_apply(Num, Mod, Func, [UpdatedArgs], GrName, + run_test_case_apply(Mod, Func, [UpdatedArgs], GrName, RunInit, TimetrapData), {Time,RetVal,Loc,Opts,Comment} = case Result of @@ -3906,7 +3905,8 @@ run_test_case1(Ref, Num, Mod, Func, Args, RunInit, true -> test_server_io:end_transaction(), Main ! {finished,Ref,self(),Num,Mod,Func, - ?mod_result(Status),{Time,RetVal,Opts}} + ?mod_result(Status),{Time,RetVal,Opts}}, + ok end, {Time,RetVal,Opts}. @@ -4329,7 +4329,7 @@ do_format_exception(Reason={Error,Stack}) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% run_test_case_apply(CaseNum, Mod, Func, Args, Name, RunInit, +%% run_test_case_apply(Mod, Func, Args, Name, RunInit, %% TimetrapData) -> %% {{Time,RetVal,Loc,Opts,Comment},DetectedFail,ProcessesBefore,ProcessesAfter} | %% {{died,Reason,unknown,Comment},DetectedFail,ProcessesBefore,ProcessesAfter} @@ -4343,9 +4343,9 @@ do_format_exception(Reason={Error,Stack}) -> %% ProcessesBefore = ProcessesAfter = integer() %% -run_test_case_apply(CaseNum, Mod, Func, Args, Name, RunInit, +run_test_case_apply(Mod, Func, Args, Name, RunInit, TimetrapData) -> - test_server:run_test_case_apply({CaseNum,Mod,Func,Args,Name,RunInit, + test_server:run_test_case_apply({Mod,Func,Args,Name,RunInit, TimetrapData}). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -5276,7 +5276,8 @@ check_cross([]) -> %% This per application analysis writes the file cover.html in the %% application's run.<timestamp> directory. stop_cover(#cover{}=CoverInfo, TestDir) -> - cover_analyse(CoverInfo, TestDir); + cover_analyse(CoverInfo, TestDir), + ok; stop_cover(_CoverInfo, _TestDir) -> %% Cover is probably controlled by the framework ok. @@ -5315,7 +5316,7 @@ cover_analyse(CoverInfo, TestDir) -> [?cross_coverlog_name]), io:fwrite(CoverLog, "<p>CoverFile: <code>~tp</code>\n", [CoverFile]), - write_cross_cover_info(TestDir,Cross), + ok = write_cross_cover_info(TestDir,Cross), case length(cover:imported_modules()) of Imps when Imps > 0 -> @@ -5329,7 +5330,7 @@ cover_analyse(CoverInfo, TestDir) -> io:fwrite(CoverLog, "<p>Excluded module(s): <code>~tp</code>\n", [Excluded]), Coverage = test_server:cover_analyse(TestDir, CoverInfo), - write_binary_file(filename:join(TestDir,?raw_coverlog_name), + ok = write_binary_file(filename:join(TestDir,?raw_coverlog_name), term_to_binary(Coverage)), case lists:filter(fun({_M,{_,_,_}}) -> false; @@ -5344,8 +5345,8 @@ cover_analyse(CoverInfo, TestDir) -> end, TotPercent = write_cover_result_table(CoverLog, Coverage), - write_binary_file(filename:join(TestDir, ?cover_total), - term_to_binary(TotPercent)). + ok = write_binary_file(filename:join(TestDir, ?cover_total), + term_to_binary(TotPercent)). %% Cover analysis - accumulated over multiple tests %% This can be executed on any node after all tests are finished. @@ -5395,7 +5396,7 @@ write_cross_cover_info(Dir,Cross) -> write_cross_cover_logs([{Tag,Coverage}|T],TagDirMods) -> case lists:keyfind(Tag,1,TagDirMods) of {_,Dir,Mods} when Mods=/=[] -> - write_binary_file(filename:join(Dir,?raw_cross_coverlog_name), + ok = write_binary_file(filename:join(Dir,?raw_cross_coverlog_name), term_to_binary(Coverage)), CoverLogName = filename:join(Dir,?cross_coverlog_name), {ok,CoverLog} = open_html_file(CoverLogName), diff --git a/lib/common_test/src/test_server_gl.erl b/lib/common_test/src/test_server_gl.erl index 333c8fc06e..7d6fe64b92 100644 --- a/lib/common_test/src/test_server_gl.erl +++ b/lib/common_test/src/test_server_gl.erl @@ -185,7 +185,7 @@ handle_info({capture,Cap0}, St) -> end, {noreply,St#st{capture=Cap}}; handle_info({io_request,From,ReplyAs,Req}=IoReq, St) -> - try io_req(Req, From, St) of + _ = try io_req(Req, From, St) of passthrough -> group_leader() ! IoReq; {EscapeHtml,Data} -> @@ -197,7 +197,8 @@ handle_info({io_request,From,ReplyAs,Req}=IoReq, St) -> #st{capture=none} -> ok; #st{capture=CapturePid} -> - CapturePid ! {captured,Data} + CapturePid ! {captured,Data}, + ok end, case EscapeHtml andalso St#st.escape_chars of true -> diff --git a/lib/common_test/src/test_server_io.erl b/lib/common_test/src/test_server_io.erl index 8c5c0aef35..3d5238052b 100644 --- a/lib/common_test/src/test_server_io.erl +++ b/lib/common_test/src/test_server_io.erl @@ -215,7 +215,7 @@ handle_call({set_fd,Tag,Fd}, _From, #st{fds=Fds0,tags=Tags0, true -> %% Fd ready, print anything buffered for associated Tag lists:filtermap(fun({T,From,Str}) when T == Tag -> - output(From, Tag, Str, St1), + _ = output(From, Tag, Str, St1), false; (_) -> true @@ -274,14 +274,15 @@ handle_call(reset_state, _From, #st{fds=Fds,tags=Tags,gls=Gls, end end, Tags), GlList = gb_sets:to_list(Gls), - [test_server_gl:stop(GL) || GL <- GlList], + _ = [test_server_gl:stop(GL) || GL <- GlList], timer:sleep(100), case lists:filter(fun(GlPid) -> is_process_alive(GlPid) end, GlList) of [] -> ok; _ -> timer:sleep(2000), - [exit(GL, kill) || GL <- GlList] + [exit(GL, kill) || GL <- GlList], + ok end, Empty = gb_trees:empty(), {ok,Shared} = test_server_gl:start_link(), @@ -304,7 +305,7 @@ handle_call({stop,FdTags}, From, #st{fds=Fds0,tags=Tags0, none -> {Fds,Tags}; {value,Fd} -> - file:close(Fd), + _ = file:close(Fd), {gb_trees:delete(Tag, Fds), lists:delete(Tag, Tags)} end @@ -333,7 +334,7 @@ handle_info({'EXIT',_Pid,Reason}, _St) -> handle_info(stop_group_leaders, #st{gls=Gls}=St) -> %% Stop the remaining group leaders. GlPids = gb_sets:to_list(Gls), - [test_server_gl:stop(GL) || GL <- GlPids], + _ = [test_server_gl:stop(GL) || GL <- GlPids], timer:sleep(100), Wait = case lists:filter(fun(GlPid) -> is_process_alive(GlPid) end, GlPids) of @@ -344,7 +345,7 @@ handle_info(stop_group_leaders, #st{gls=Gls}=St) -> {noreply,St}; handle_info(kill_group_leaders, #st{gls=Gls,stopping=From, pending_ops=Ops}=St) -> - [exit(GL, kill) || GL <- gb_sets:to_list(Gls)], + _ = [exit(GL, kill) || GL <- gb_sets:to_list(Gls)], if From /= undefined -> gen_server:reply(From, ok); true -> % reply has been sent already @@ -434,7 +435,7 @@ do_print_buffered(Q0, St) -> eot -> Q; {Tag,Str} -> - do_output(Tag, Str, undefined, St), + _ = do_output(Tag, Str, undefined, St), do_print_buffered(Q, St) end. @@ -448,5 +449,5 @@ gc(#st{gls=Gls0}) -> InUse = ordsets:from_list(InUse0), Gls = gb_sets:to_list(Gls0), NotUsed = ordsets:subtract(Gls, InUse), - [test_server_gl:stop(Pid) || Pid <- NotUsed], + _ = [test_server_gl:stop(Pid) || Pid <- NotUsed], ok. diff --git a/lib/common_test/src/test_server_node.erl b/lib/common_test/src/test_server_node.erl index c64399e485..0b406c54cc 100644 --- a/lib/common_test/src/test_server_node.erl +++ b/lib/common_test/src/test_server_node.erl @@ -198,9 +198,9 @@ trc_loop(Sock,Patterns,Type) -> gen_tcp:close(Sock) end. add_nodes(Nodes,Patterns,_Type) -> - ttb:tracer(Nodes,[{file,{local, test_server}}, - {handler, {{?MODULE,handle_debug},initial}}]), - ttb:p(all,[call,timestamp]), + {ok, _} = ttb:tracer(Nodes,[{file,{local, test_server}}, + {handler, {{?MODULE,handle_debug},initial}}]), + {ok, _} = ttb:p(all,[call,timestamp]), lists:foreach(fun({TP,M,F,A,Pat}) -> ttb:TP(M,F,A,Pat); ({CTP,M,F,A}) -> ttb:CTP(M,F,A) end, @@ -360,8 +360,8 @@ start_node_peer(SlaveName, OptList, From, TI) -> -spec wait_for_node_started_fun(_, _, _, _, _) -> fun(() -> no_return()). wait_for_node_started_fun(LSock, Tmo, Cleanup, TI, Self) -> fun() -> - wait_for_node_started(LSock,Tmo,undefined, - Cleanup,TI,Self), + {{ok, _}, _} = wait_for_node_started(LSock,Tmo,undefined, + Cleanup,TI,Self), receive after infinity -> ok end end. @@ -432,7 +432,7 @@ wait_for_node_started(LSock,Timeout,Client,Cleanup,TI,CtrlPid) -> client=Client}); false -> ok end, - gen_tcp:controlling_process(Sock,CtrlPid), + ok = gen_tcp:controlling_process(Sock,CtrlPid), test_server_ctrl:node_started(Nodename), {{ok,Nodename},W} end; diff --git a/lib/common_test/src/test_server_sup.erl b/lib/common_test/src/test_server_sup.erl index fa2bb33c2d..6922e01fcc 100644 --- a/lib/common_test/src/test_server_sup.erl +++ b/lib/common_test/src/test_server_sup.erl @@ -755,7 +755,7 @@ framework_call(FW,_Func,_Args,DefaultReturn) DefaultReturn; framework_call(Callback,Func,Args,DefaultReturn) -> Mod = list_to_atom(Callback), - case code:is_loaded(Mod) of + _ = case code:is_loaded(Mod) of false -> code:load_file(Mod); _ -> ok end, @@ -851,7 +851,8 @@ util_start() -> spawn_link(fun() -> register(?MODULE, self()), util_loop(#util_state{starter=Starter}) - end); + end), + ok; _Pid -> ok end. diff --git a/lib/common_test/src/vts.erl b/lib/common_test/src/vts.erl index e1c16fbda4..f1c5051164 100644 --- a/lib/common_test/src/vts.erl +++ b/lib/common_test/src/vts.erl @@ -64,7 +64,7 @@ %%%----------------------------------------------------------------- %%% User API start() -> - ct_webtool:start(), + {ok, _} = ct_webtool:start(), ct_webtool:start_tools([],"app=vts"). init_data(ConfigFiles,EvHandlers,LogDir,LogOpts,Tests) -> @@ -169,7 +169,7 @@ loop(State) -> NewState = State#state{config=Config,event_handler=EvHandlers, current_log_dir=LogDir, logopts=LogOpts,tests=Tests}, - ct_install(NewState), + _ = ct_install(NewState), return(From,ok), loop(NewState); {start_page,From} -> @@ -192,12 +192,12 @@ loop(State) -> loop(State); {{add_config_file,Input},From} -> {Return,State1} = add_config_file1(Input,State), - ct_install(State1), + _ = ct_install(State1), return(From,Return), loop(State1); {{remove_config_file,Input},From} -> {Return,State1} = remove_config_file1(Input,State), - ct_install(State1), + _ = ct_install(State1), return(From,Return), loop(State1); {run_frame,From} -> @@ -233,7 +233,7 @@ loop(State) -> return(From,result_summary_frame1(State)), loop(State); stop_reload_results -> - file:set_cwd(State#state.start_dir), + 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()), @@ -277,8 +277,8 @@ call(Msg) -> end. return({To,Ref},Result) -> - To ! {Ref, Result}. - + To ! {Ref, Result}, + ok. run_test1(State=#state{tests=Tests,current_log_dir=LogDir, logopts=LogOpts}) -> @@ -311,7 +311,6 @@ run_test1(State=#state{tests=Tests,current_log_dir=LogDir, ct_install(#state{config=Config,event_handler=EvHandlers, current_log_dir=LogDir}) -> ct_run:install([{config,Config},{event_handler,EvHandlers}],LogDir). - %%%----------------------------------------------------------------- %%% HTML start_page1() -> @@ -549,7 +548,7 @@ case_select(Dir,Suite,Case,N) -> end, case MakeResult of ok -> - code:add_pathz(Dir), + true = code:add_pathz(Dir), case catch apply(Suite,all,[]) of {'EXIT',Reason} -> io:format("\n~p\n",[Reason]), @@ -755,7 +754,7 @@ report1(tests_start,{TestName,_N},State) -> end, State#state{testruns=TestRuns}; report1(tests_done,{_Ok,_Fail,_Skip},State) -> - timer:send_after(5000, self(),stop_reload_results), + {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; diff --git a/lib/common_test/test/Makefile b/lib/common_test/test/Makefile index 1532b6c1f7..b1eddfedd7 100644 --- a/lib/common_test/test/Makefile +++ b/lib/common_test/test/Makefile @@ -69,7 +69,8 @@ MODULES= \ erl2html2_SUITE \ test_server_SUITE \ test_server_test_lib \ - ct_release_test_SUITE + ct_release_test_SUITE \ + ct_log_SUITE ERL_FILES= $(MODULES:%=%.erl) HRL_FILES= test_server_test_lib.hrl diff --git a/lib/common_test/test/ct_log_SUITE.erl b/lib/common_test/test/ct_log_SUITE.erl new file mode 100644 index 0000000000..9bdd44cbdf --- /dev/null +++ b/lib/common_test/test/ct_log_SUITE.erl @@ -0,0 +1,328 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2009-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. +%% 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% +%% + +%%%------------------------------------------------------------------- +%%% File: ct_log_SUITE +%%% +%%% Description: Test that ct:log, ct:pal and io:format print to +%%% the test case log file as expected, with or without special HTML +%%% characters being escaped. +%%% +%%%------------------------------------------------------------------- +-module(ct_log_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("common_test/include/ct_event.hrl"). + +-define(eh, ct_test_support_eh). + +%%-------------------------------------------------------------------- +%% TEST SERVER CALLBACK FUNCTIONS +%%-------------------------------------------------------------------- +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_testcase(_TestCase, Config) -> + Config. + +end_per_testcase(_TestCase, _Config) -> + ok. + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [{group,print_and_verify}]. + +groups() -> + [{print_and_verify,[sequence],[print,verify]}]. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + +%%-------------------------------------------------------------------- +%% TEST CASES +%%-------------------------------------------------------------------- + +%%%----------------------------------------------------------------- +%%% +print(Config) -> + TcLogFile = proplists:get_value(tc_logfile, Config), + Pid = self(), + String = atom_to_list(?MODULE), + + %% START mark + io:format("LOGGING START~n"), + + %% io:format + io:format("1. Printing nothing~n", []), + io:format("2. Printing a string: ~s~n", [String]), + io:format("3. Printing a string: ~p~n", [String]), + io:format("4. Printing a tuple: ~w~n", [{module,?MODULE}]), + io:format("5. Printing a pid: ~w~n", [Pid]), + io:format("6. Printing HTML: <pre>~s</pre>~n", [String]), + + %% --- API --- + %% pal(Format) -> + %% = ct:pal(default, 50, Format, []). + %% pal(X1, X2) -> ok + %% X1 = Category | Importance | Format + %% X2 = Format | FormatArgs + %% pal(X1, X2, X3) -> ok + %% X1 = Category | Importance + %% X2 = Importance | Format + %% X3 = Format | FormatArgs + %% pal(Category, Importance, Format, FormatArgs) -> ok + %% ------ + ct:pal("1. Printing nothing"), + ct:pal("2. Printing nothing", []), + ct:pal("3. Printing a string: ~s", [String]), + ct:pal("4. Printing a string: ~p", [String]), + ct:pal("5. Printing a tuple: ~w", [{module,?MODULE}]), + ct:pal("6. Printing a pid: ~w", [Pid]), + ct:pal("7. Printing HTML: <pre>~s</pre>", [String]), + ct:pal(ct_internal, "8. Printing with category"), + ct:pal(ct_internal, "9. Printing with ~s", ["category"]), + ct:pal(50, "10. Printing with importance"), + ct:pal(50, "11. Printing with ~s", ["importance"]), + ct:pal(ct_internal, 50, "12. Printing with ~s", ["category and importance"]), + + %% --- API --- + %% log(Format) -> ok + %% = ct:log(default, 50, Format, [], []). + %% log(X1, X2) -> ok + %% X1 = Category | Importance | Format + %% X2 = Format | FormatArgs + %% log(X1, X2, X3) -> ok + %% X1 = Category | Importance + %% X2 = Importance | Format + %% X3 = Format | FormatArgs | Opts + %% log(X1, X2, X3, X4) -> ok + %% X1 = Category | Importance + %% X2 = Importance | Format + %% X3 = Format | FormatArgs + %% X4 = FormatArgs | Opts + %% log(Category, Importance, Format, FormatArgs, Opts) -> ok + %% ------ + ct:log("1. Printing nothing"), + ct:log("2. Printing nothing", []), + ct:log("3. Printing a string: ~s", [String]), + ct:log("4. Printing a string: ~p", [String]), + ct:log("5. Printing a tuple: ~w", [{module,?MODULE}]), + ct:log("6. Printing a pid: ~w", [Pid]), + ct:log("7. Printing HTML: <pre>~s</pre>", [String]), + ct:log("8. Printing a pid escaped: ~w", [Pid], [esc_chars]), + ct:log("9. Printing a string escaped: ~p", [String], [esc_chars]), + ct:log("10. Printing HTML escaped: <pre>~s</pre>", [String], [esc_chars]), + ct:log("11. Printing a string, no css: ~s", [String], [no_css]), + ct:log("12. Printing a pid escaped, no css: ~w", [Pid], + [esc_chars, no_css]), + ct:log(ct_internal, "13. Printing with category"), + ct:log(ct_internal, "14. Printing with ~s", ["category"]), + ct:log(ct_internal, "15. Printing with ~s, no_css", ["category"], + [no_css]), + ct:log(50, "16. Printing with importance"), + ct:log(50, "17. Printing with ~s", ["importance"]), + ct:log(50, "18. Printing with ~s, no_css", ["importance"], [no_css]), + ct:log(ct_internal, 50, "19. Printing with category and importance"), + ct:log(ct_internal, 50, "20. Printing with ~s", ["category and importance"]), + ct:log(ct_internal, 50, "21. Printing a pid escaped with ~s, no_css: ~w", + ["category and importance",Pid], [esc_chars,no_css]), + + %% END mark + ct:log("LOGGING END", [], [no_css]), + {save_config,[{the_logfile,TcLogFile},{the_pid,Pid},{the_string,String}]}. + + +verify(Config) -> + {print,SavedCfg} = proplists:get_value(saved_config, Config), + TcLogFile = proplists:get_value(the_logfile, SavedCfg), + Pid = proplists:get_value(the_pid, SavedCfg), + StrPid = lists:flatten(io_lib:format("~p",[Pid])), + EscPid = "<" ++ string:substr(StrPid, 2, length(StrPid)-2) ++ ">", + String = proplists:get_value(the_string, SavedCfg), + ct:log("Read from prev testcase: ~p & ~p", [TcLogFile,Pid]), + {ok,Dev} = file:open(TcLogFile, [read]), + ok = read_until(Dev, "LOGGING START\n"), + + %% io:format + match_line(Dev, "1. Printing nothing", []), + read_nl(Dev), + match_line(Dev, "2. Printing a string: ~s", [String]), + read_nl(Dev), + match_line(Dev, "3. Printing a string: ~p", [String]), + read_nl(Dev), + match_line(Dev, "4. Printing a tuple: ~w", [{module,?MODULE}]), + read_nl(Dev), + match_line(Dev, "5. Printing a pid: ~s", [EscPid]), + read_nl(Dev), + match_line(Dev, "6. Printing HTML: <pre>~s</pre>", [String]), + read_nl(Dev), + %% ct:pal + read_header(Dev), + match_line(Dev, "1. Printing nothing", []), + read_footer(Dev), + read_header(Dev), + match_line(Dev, "2. Printing nothing", []), + read_footer(Dev), + read_header(Dev), + match_line(Dev, "3. Printing a string: ~s", [String]), + read_footer(Dev), + read_header(Dev), + match_line(Dev, "4. Printing a string: ~p", [String]), + read_footer(Dev), + read_header(Dev), + match_line(Dev, "5. Printing a tuple: ~w", [{module,?MODULE}]), + read_footer(Dev), + read_header(Dev), + match_line(Dev, "6. Printing a pid: ~s", [EscPid]), + read_footer(Dev), + read_header(Dev), + match_line(Dev, "7. Printing HTML: <pre>~s</pre>", [String]), + read_footer(Dev), + read_header(Dev, "\"ct_internal\""), + match_line(Dev, "8. Printing with category", []), + read_footer(Dev), + read_header(Dev, "\"ct_internal\""), + match_line(Dev, "9. Printing with ~s", ["category"]), + read_footer(Dev), + read_header(Dev), + match_line(Dev, "10. Printing with importance", []), + read_footer(Dev), + read_header(Dev), + match_line(Dev, "11. Printing with ~s", ["importance"]), + read_footer(Dev), + read_header(Dev, "\"ct_internal\""), + match_line(Dev, "12. Printing with ~s", ["category and importance"]), + read_footer(Dev), + %% ct:log + read_header(Dev), + match_line(Dev, "1. Printing nothing", []), + read_footer(Dev), + read_header(Dev), + match_line(Dev, "2. Printing nothing", []), + read_footer(Dev), + read_header(Dev), + match_line(Dev, "3. Printing a string: ~s", [String]), + read_footer(Dev), + read_header(Dev), + match_line(Dev, "4. Printing a string: ~p", [String]), + read_footer(Dev), + read_header(Dev), + match_line(Dev, "5. Printing a tuple: ~w", [{module,?MODULE}]), + read_footer(Dev), + read_header(Dev), + match_line(Dev, "6. Printing a pid: ~w", [Pid]), + read_footer(Dev), + read_header(Dev), + match_line(Dev, "7. Printing HTML: <pre>~s</pre>", [String]), + read_footer(Dev), + read_header(Dev), + match_line(Dev, "8. Printing a pid escaped: ~s", [EscPid]), + read_footer(Dev), + read_header(Dev), + match_line(Dev, "9. Printing a string escaped: ~p", [String]), + read_footer(Dev), + read_header(Dev), + match_line(Dev, "10. Printing HTML escaped: <pre>~s</pre>", + [String]), + read_footer(Dev), + match_line(Dev, "11. Printing a string, no css: ~s", [String]), + match_line(Dev, "12. Printing a pid escaped, no css: ~s", [EscPid]), + read_header(Dev, "\"ct_internal\""), + match_line(Dev, "13. Printing with category", []), + read_footer(Dev), + read_header(Dev, "\"ct_internal\""), + match_line(Dev, "14. Printing with ~s", ["category"]), + read_footer(Dev), + match_line(Dev, "15. Printing with ~s, no_css", ["category"]), + read_header(Dev), + match_line(Dev, "16. Printing with importance", []), + read_footer(Dev), + read_header(Dev), + match_line(Dev, "17. Printing with ~s", ["importance"]), + read_footer(Dev), + match_line(Dev, "18. Printing with ~s, no_css", ["importance"]), + read_header(Dev, "\"ct_internal\""), + match_line(Dev, "19. Printing with category and importance", []), + read_footer(Dev), + read_header(Dev, "\"ct_internal\""), + match_line(Dev, "20. Printing with ~s", ["category and importance"]), + read_footer(Dev), + match_line(Dev, "21. Printing a pid escaped with ~s, no_css: ~s", + ["category and importance",EscPid]), + + file:close(Dev), + ok. + +%%%----------------------------------------------------------------- +%%% HELP FUNCTIONS +%%%----------------------------------------------------------------- + +read_until(Dev, Pat) -> + case file:read_line(Dev) of + {ok,Pat} -> + file:read_line(Dev), % \n + ok; + eof -> + file:close(Dev), + {error,{not_found,Pat}}; + _ -> + read_until(Dev, Pat) + end. + +match_line(Dev, Format, Args) -> + Pat = lists:flatten(io_lib:format(Format, Args)), + Line = element(2, file:read_line(Dev)), + case re:run(Line, Pat) of + {match,_} -> + ok; + nomatch -> + ct:pal("ERROR! No match for ~p.\nLine = ~p", [Pat,Line]), + file:close(Dev), + ct:fail({mismatch,Pat,Line}) + end. + +read_header(Dev) -> + read_header(Dev, "\"default\""). + +read_header(Dev, Cat) -> + file:read_line(Dev), % \n + "</pre>\n" = element(2, file:read_line(Dev)), + {match,_} = + re:run(element(2, file:read_line(Dev)), "<div class="++Cat++"><pre><b>" + "\\*\\*\\* User \\d{4}-\\d{2}-\\d{2} " + "\\d{2}:\\d{2}:\\d{2}.\\d{1,} \\*\\*\\*</b>"). + +read_footer(Dev) -> + "</pre></div>\n" = element(2, file:read_line(Dev)), + "<pre>\n" = element(2, file:read_line(Dev)). + +read_nl(Dev) -> + file:read_line(Dev). + + diff --git a/lib/common_test/test/ct_repeat_testrun_SUITE.erl b/lib/common_test/test/ct_repeat_testrun_SUITE.erl index 632597c214..f8b6a379f6 100644 --- a/lib/common_test/test/ct_repeat_testrun_SUITE.erl +++ b/lib/common_test/test/ct_repeat_testrun_SUITE.erl @@ -66,25 +66,47 @@ %% there will be clashes with logging processes etc). %%-------------------------------------------------------------------- init_per_suite(Config0) -> - Config = ct_test_support:init_per_suite(Config0), - DataDir = ?config(data_dir, Config), - Suite1 = filename:join([DataDir,"a_test","r1_SUITE"]), - Suite2 = filename:join([DataDir,"b_test","r2_SUITE"]), - Opts0 = ct_test_support:get_opts(Config), - Opts1 = Opts0 ++ [{suite,Suite1},{testcase,tc2},{label,timing1}], - Opts2 = Opts0 ++ [{suite,Suite2},{testcase,tc2},{label,timing2}], - - %% Make sure both suites are compiled - {1,0,{0,0}} = ct_test_support:run(ct,run_test,[Opts1],Config), - {1,0,{0,0}} = ct_test_support:run(ct,run_test,[Opts2],Config), - - %% Time the shortest testcase to use for offset - {_T0,{1,0,{0,0}}} = timer:tc(ct_test_support,run,[ct,run_test,[Opts1],Config]), - - %% -2 is to ensure we hit inside the target test case and not after -% T = round(T0/1000000)-2, - T=0, - [{offset,T}|Config]. + TTInfo = {_T,{_Scaled,ScaleVal}} = ct:get_timetrap_info(), + ct:pal("Timetrap info = ~w", [TTInfo]), + if ScaleVal > 1 -> + {skip,"Skip on systems running e.g. cover or debug!"}; + ScaleVal =< 1 -> + Config = ct_test_support:init_per_suite(Config0), + DataDir = ?config(data_dir, Config), + Suite1 = filename:join([DataDir,"a_test","r1_SUITE"]), + Suite2 = filename:join([DataDir,"b_test","r2_SUITE"]), + Opts0 = ct_test_support:get_opts(Config), + Opts1 = Opts0 ++ [{suite,Suite1},{testcase,tc2},{label,timing1}], + Opts2 = Opts0 ++ [{suite,Suite2},{testcase,tc2},{label,timing2}], + + %% Make sure both suites are compiled + {1,0,{0,0}} = ct_test_support:run(ct,run_test,[Opts1],Config), + {1,0,{0,0}} = ct_test_support:run(ct,run_test,[Opts2],Config), + + %% Check if file i/o is too slow for correct measurements + Opts3 = Opts0 ++ [{suite,Suite1},{testcase,tc1},{label,timing3}], + {T,_} = + timer:tc( + fun() -> + {1,0,{0,0}} = ct_test_support:run(ct,run_test, + [Opts3],Config), + {1,0,{0,0}} = ct_test_support:run(ct,run_test, + [Opts3],Config) + end), + %% The time to compare with here must match the timeout value + %% in the test suite. Accept 30% logging overhead (26 sec total). + if T > 26000000 -> + ct:pal("Timing test took ~w sec (< 27 sec expected). " + "Skipping the suite!", + [trunc(T/1000000)]), + ct_test_support:end_per_suite(Config), + {skip,"File I/O too slow for this suite"}; + true -> + ct:pal("Timing test took ~w sec. Proceeding...", + [trunc(T/1000000)]), + [{offset,0}|Config] + end + end. end_per_suite(Config) -> ct_test_support:end_per_suite(Config). diff --git a/lib/compiler/doc/src/compile.xml b/lib/compiler/doc/src/compile.xml index 954750fcdd..61e214294e 100644 --- a/lib/compiler/doc/src/compile.xml +++ b/lib/compiler/doc/src/compile.xml @@ -40,6 +40,19 @@ <funcs> <func> + <name>env_compiler_options()</name> + <fsummary> + Compiler options defined via the environment variable + <c>ERL_COMPILER_OPTIONS</c> + </fsummary> + <desc> + <p>Return compiler options given via the environment variable + <c>ERL_COMPILER_OPTIONS</c>. If the value is a list, it is + returned as is. If it is not a list, it is put into a list. + </p> + </desc> + </func> + <func> <name>file(File)</name> <fsummary>Compiles a file.</fsummary> <desc> @@ -768,6 +781,9 @@ module.beam: module.erl \ if you do not want the environment variable to be consulted, for example, if you are calling the compiler recursively from inside a parse transform.</p> + + <p>The list can be retrieved with + <seealso marker="#env_compiler_options/0">env_compiler_options/0</seealso>.</p> </section> <section> diff --git a/lib/compiler/src/beam_block.erl b/lib/compiler/src/beam_block.erl index a8cfdffdf3..85d332c56e 100644 --- a/lib/compiler/src/beam_block.erl +++ b/lib/compiler/src/beam_block.erl @@ -262,12 +262,17 @@ opt_move_1(R, [{set,[D],[R],move}|Is0], Acc) -> {yes,Is} -> opt_move_rev(D, Acc, Is); no -> not_possible end; -opt_move_1({x,_}, [{set,_,_,{alloc,_,_}}|_], _) -> - %% The optimization is not possible. If the X register is not - %% killed by allocation, the optimization would not be safe. - %% If the X register is killed, it means that there cannot - %% follow a 'move' instruction with this X register as the - %% source. +opt_move_1(_R, [{set,_,_,{alloc,_,_}}|_], _) -> + %% The optimization is either not possible or not safe. + %% + %% If R is an X register killed by allocation, the optimization is + %% not safe. On the other hand, if the X register is killed, there + %% will not follow a 'move' instruction with this X register as + %% the source. + %% + %% If R is a Y register, the optimization is still not safe + %% because the new target register is an X register that cannot + %% safely pass the alloc instruction. not_possible; opt_move_1(R, [{set,_,_,_}=I|Is], Acc) -> %% If the source register is either killed or used by this diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl index 13aa31b7c9..4c0cb6780a 100644 --- a/lib/compiler/src/beam_validator.erl +++ b/lib/compiler/src/beam_validator.erl @@ -658,8 +658,10 @@ valfun_4({test,is_map,{f,Lbl},[Src]}, Vst0) -> case Src of {Tag,_} when Tag =:= x; Tag =:= y -> set_type_reg(map, Src, Vst); + {literal,Map} when is_map(Map) -> + Vst; _ -> - Vst + kill_state(Vst) end; valfun_4({test,_Op,{f,Lbl},Src}, Vst) -> validate_src(Src, Vst), diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl index 149086152a..82ff8a95f3 100644 --- a/lib/compiler/src/compile.erl +++ b/lib/compiler/src/compile.erl @@ -26,6 +26,7 @@ -export([forms/1,forms/2,noenv_forms/2]). -export([output_generated/1,noenv_output_generated/1]). -export([options/0]). +-export([env_compiler_options/0]). %% Erlc interface. -export([compile/3,compile_beam/3,compile_asm/3,compile_core/3]). @@ -131,6 +132,14 @@ noenv_output_generated(Opts) -> end, Passes). %% +%% Retrieve ERL_COMPILER_OPTIONS as a list of terms +%% + +-spec env_compiler_options() -> [term()]. + +env_compiler_options() -> env_default_opts(). + +%% %% Local functions %% diff --git a/lib/compiler/src/rec_env.erl b/lib/compiler/src/rec_env.erl index 936c5f6106..cdc513e57c 100644 --- a/lib/compiler/src/rec_env.erl +++ b/lib/compiler/src/rec_env.erl @@ -22,8 +22,7 @@ %% @doc Abstract environments, supporting self-referential bindings and %% automatic new-key generation. -%% The current implementation is based on Erlang standard library -%% dictionaries. +%% The current implementation is based on Erlang standard library maps. %%% -define(DEBUG, true). @@ -62,7 +61,7 @@ test_0(Type, N) -> io:fwrite("\ncalls: ~w.\n", [get(new_key_calls)]), io:fwrite("\nretries: ~w.\n", [get(new_key_retries)]), io:fwrite("\nmax: ~w.\n", [get(new_key_max)]), - dict:to_list(element(1,Env)). + maps:to_list(element(1,Env)). test_1(integer = Type, N, Env) when is_integer(N), N > 0 -> Key = new_key(Env), @@ -80,14 +79,13 @@ test_1(_,0, Env) -> %% %% environment() = [Mapping] %% -%% Mapping = {map, Dict} | {rec, Dict, Dict} -%% Dict = dict:dictionary() +%% Mapping = {map, map()} | {rec, map(), map()} %% -%% An empty environment is a list containing a single `{map, Dict}' +%% An empty environment is a list containing a single `{map, map()}' %% element - empty lists are not valid environments. To find a key in an %% environment, it is searched for in each mapping in the list, in %% order, until it the key is found in some mapping, or the end of the -%% list is reached. In a 'rec' mapping, we keep the original dictionary +%% list is reached. In a 'rec' mapping, we keep the original map %% together with a version where entries may have been deleted - this %% makes it possible to garbage collect the entire 'rec' mapping when %% all its entries are unused (for example, by being shadowed by later @@ -97,7 +95,7 @@ test_1(_,0, Env) -> %% ===================================================================== %% @type environment(). An abstract environment. --type mapping() :: {'map', dict:dict()} | {'rec', dict:dict(), dict:dict()}. +-type mapping() :: {'map', map()} | {'rec', map(), map()}. -type environment() :: [mapping(),...]. %% ===================================================================== @@ -108,7 +106,7 @@ test_1(_,0, Env) -> -spec empty() -> environment(). empty() -> - [{map, dict:new()}]. + [{map, #{}}]. %% ===================================================================== @@ -119,14 +117,14 @@ empty() -> -spec is_empty(environment()) -> boolean(). -is_empty([{map, Dict} | Es]) -> - N = dict:size(Dict), +is_empty([{map, Map} | Es]) -> + N = map_size(Map), if N =/= 0 -> false; Es =:= [] -> true; true -> is_empty(Es) end; -is_empty([{rec, Dict, _} | Es]) -> - N = dict:size(Dict), +is_empty([{rec, Map, _} | Es]) -> + N = map_size(Map), if N =/= 0 -> false; Es =:= [] -> true; true -> is_empty(Es) @@ -146,12 +144,12 @@ is_empty([{rec, Dict, _} | Es]) -> size(Env) -> env_size(Env). -env_size([{map, Dict}]) -> - dict:size(Dict); -env_size([{map, Dict} | Env]) -> - dict:size(Dict) + env_size(Env); -env_size([{rec, Dict, _Dict0} | Env]) -> - dict:size(Dict) + env_size(Env). +env_size([{map, Map}]) -> + map_size(Map); +env_size([{map, Map} | Env]) -> + map_size(Map) + env_size(Env); +env_size([{rec, Map, _Map0} | Env]) -> + map_size(Map) + env_size(Env). %% ===================================================================== @@ -165,8 +163,8 @@ env_size([{rec, Dict, _Dict0} | Env]) -> -spec is_defined(term(), environment()) -> boolean(). -is_defined(Key, [{map, Dict} | Env]) -> - case dict:is_key(Key, Dict) of +is_defined(Key, [{map, Map} | Env]) -> + case maps:is_key(Key, Map) of true -> true; false when Env =:= [] -> @@ -174,8 +172,8 @@ is_defined(Key, [{map, Dict} | Env]) -> false -> is_defined(Key, Env) end; -is_defined(Key, [{rec, Dict, _Dict0} | Env]) -> - dict:is_key(Key, Dict) orelse is_defined(Key, Env). +is_defined(Key, [{rec, Map, _Map0} | Env]) -> + maps:is_key(Key, Map) orelse is_defined(Key, Env). %% ===================================================================== @@ -188,12 +186,12 @@ is_defined(Key, [{rec, Dict, _Dict0} | Env]) -> keys(Env) -> lists:sort(keys(Env, [])). -keys([{map, Dict}], S) -> - dict:fetch_keys(Dict) ++ S; -keys([{map, Dict} | Env], S) -> - keys(Env, dict:fetch_keys(Dict) ++ S); -keys([{rec, Dict, _Dict0} | Env], S) -> - keys(Env, dict:fetch_keys(Dict) ++ S). +keys([{map, Map}], S) -> + maps:keys(Map) ++ S; +keys([{map, Map} | Env], S) -> + keys(Env, maps:keys(Map) ++ S); +keys([{rec, Map, _Map0} | Env], S) -> + keys(Env, maps:keys(Map) ++ S). %% ===================================================================== @@ -212,12 +210,12 @@ keys([{rec, Dict, _Dict0} | Env], S) -> to_list(Env) -> lists:sort(to_list(Env, [])). -to_list([{map, Dict}], S) -> - dict:to_list(Dict) ++ S; -to_list([{map, Dict} | Env], S) -> - to_list(Env, dict:to_list(Dict) ++ S); -to_list([{rec, Dict, _Dict0} | Env], S) -> - to_list(Env, dict:to_list(Dict) ++ S). +to_list([{map, Map}], S) -> + maps:to_list(Map) ++ S; +to_list([{map, Map} | Env], S) -> + to_list(Env, maps:to_list(Map) ++ S); +to_list([{rec, Map, _Map0} | Env], S) -> + to_list(Env, maps:to_list(Map) ++ S). %% ===================================================================== @@ -236,12 +234,12 @@ to_list([{rec, Dict, _Dict0} | Env], S) -> -spec bind(term(), term(), environment()) -> environment(). -bind(Key, Value, [{map, Dict}]) -> - [{map, dict:store(Key, Value, Dict)}]; -bind(Key, Value, [{map, Dict} | Env]) -> - [{map, dict:store(Key, Value, Dict)} | delete_any(Key, Env)]; +bind(Key, Value, [{map, Map}]) -> + [{map, maps:put(Key, Value, Map)}]; +bind(Key, Value, [{map, Map} | Env]) -> + [{map, maps:put(Key, Value, Map)} | delete_any(Key, Env)]; bind(Key, Value, Env) -> - [{map, dict:store(Key, Value, dict:new())} | delete_any(Key, Env)]. + [{map, maps:put(Key, Value, #{})} | delete_any(Key, Env)]. %% ===================================================================== @@ -259,17 +257,17 @@ bind(Key, Value, Env) -> -spec bind_list([term()], [term()], environment()) -> environment(). -bind_list(Ks, Vs, [{map, Dict}]) -> - [{map, store_list(Ks, Vs, Dict)}]; -bind_list(Ks, Vs, [{map, Dict} | Env]) -> - [{map, store_list(Ks, Vs, Dict)} | delete_list(Ks, Env)]; +bind_list(Ks, Vs, [{map, Map}]) -> + [{map, store_list(Ks, Vs, Map)}]; +bind_list(Ks, Vs, [{map, Map} | Env]) -> + [{map, store_list(Ks, Vs, Map)} | delete_list(Ks, Env)]; bind_list(Ks, Vs, Env) -> - [{map, store_list(Ks, Vs, dict:new())} | delete_list(Ks, Env)]. + [{map, store_list(Ks, Vs, #{})} | delete_list(Ks, Env)]. -store_list([K | Ks], [V | Vs], Dict) -> - store_list(Ks, Vs, dict:store(K, V, Dict)); -store_list([], _, Dict) -> - Dict. +store_list([K | Ks], [V | Vs], Map) -> + store_list(Ks, Vs, maps:put(K, V, Map)); +store_list([], _, Map) -> + Map. delete_list([K | Ks], Env) -> delete_list(Ks, delete_any(K, Env)); @@ -298,48 +296,40 @@ delete_any(Key, Env) -> -spec delete(term(), environment()) -> environment(). -delete(Key, [{map, Dict} = E | Env]) -> - case dict:is_key(Key, Dict) of - true -> - [{map, dict:erase(Key, Dict)} | Env]; - false -> +delete(Key, [{map, Map} = E | Env]) -> + case maps:take(Key, Map) of + {_, Map1} -> + [{map, Map1} | Env]; + error -> delete_1(Key, Env, E) end; -delete(Key, [{rec, Dict, Dict0} = E | Env]) -> - case dict:is_key(Key, Dict) of - true -> - %% The Dict0 component must be preserved as it is until all - %% keys in Dict have been deleted. - Dict1 = dict:erase(Key, Dict), - case dict:size(Dict1) of - 0 -> - Env; % the whole {rec,...} is now garbage - _ -> - [{rec, Dict1, Dict0} | Env] - end; - false -> +delete(Key, [{rec, Map, Map0} = E | Env]) -> + case maps:take(Key, Map) of + {_, Map1} when map_size(Map1) =:= 0 -> + Env; % the whole {rec,...} is now garbage + %% The Map0 component must be preserved as it is until all + %% keys in Map have been deleted. + {_, Map1} -> + [{rec, Map1, Map0} | Env]; + error -> [E | delete(Key, Env)] end. %% This is just like above, except we pass on the preceding 'map' %% mapping in the list to enable merging when removing 'rec' mappings. -delete_1(Key, [{rec, Dict, Dict0} = E | Env], E1) -> - case dict:is_key(Key, Dict) of - true -> - Dict1 = dict:erase(Key, Dict), - case dict:size(Dict1) of - 0 -> - concat(E1, Env); - _ -> - [E1, {rec, Dict1, Dict0} | Env] - end; - false -> +delete_1(Key, [{rec, Map, Map0} = E | Env], E1) -> + case maps:take(Key, Map) of + {_, Map1} when map_size(Map1) =:= 0 -> + concat(E1, Env); + {_, Map1} -> + [E1, {rec, Map1, Map0} | Env]; + error -> [E1, E | delete(Key, Env)] end. -concat({map, D1}, [{map, D2} | Env]) -> - [dict:merge(fun (_K, V1, _V2) -> V1 end, D1, D2) | Env]; +concat({map, M1}, [{map, M2} | Env]) -> + [maps:merge(M2, M1) | Env]; concat(E1, Env) -> [E1 | Env]. @@ -392,15 +382,15 @@ bind_recursive([], [], _, Env) -> Env; bind_recursive(Ks, Vs, F, Env) -> F1 = fun (V) -> - fun (Dict) -> F(V, [{rec, Dict, Dict} | Env]) end + fun (Map) -> F(V, [{rec, Map, Map} | Env]) end end, - Dict = bind_recursive_1(Ks, Vs, F1, dict:new()), - [{rec, Dict, Dict} | Env]. + Map = bind_recursive_1(Ks, Vs, F1, #{}), + [{rec, Map, Map} | Env]. -bind_recursive_1([K | Ks], [V | Vs], F, Dict) -> - bind_recursive_1(Ks, Vs, F, dict:store(K, F(V), Dict)); -bind_recursive_1([], [], _, Dict) -> - Dict. +bind_recursive_1([K | Ks], [V | Vs], F, Map) -> + bind_recursive_1(Ks, Vs, F, maps:put(K, F(V), Map)); +bind_recursive_1([], [], _, Map) -> + Map. %% ===================================================================== @@ -416,8 +406,8 @@ bind_recursive_1([], [], _, Dict) -> -spec lookup(term(), environment()) -> 'error' | {'ok', term()}. -lookup(Key, [{map, Dict} | Env]) -> - case dict:find(Key, Dict) of +lookup(Key, [{map, Map} | Env]) -> + case maps:find(Key, Map) of {ok, _}=Value -> Value; error when Env =:= [] -> @@ -425,10 +415,10 @@ lookup(Key, [{map, Dict} | Env]) -> error -> lookup(Key, Env) end; -lookup(Key, [{rec, Dict, Dict0} | Env]) -> - case dict:find(Key, Dict) of +lookup(Key, [{rec, Map, Map0} | Env]) -> + case maps:find(Key, Map) of {ok, F} -> - {ok, F(Dict0)}; + {ok, F(Map0)}; error -> lookup(Key, Env) end. diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl index dbc27db377..e0de50f3ae 100644 --- a/lib/compiler/src/sys_core_fold.erl +++ b/lib/compiler/src/sys_core_fold.erl @@ -786,7 +786,7 @@ fold_lit_args(Call, Module, Name, Args0) -> Val -> case cerl:is_literal_term(Val) of true -> - cerl:abstract(Val); + cerl:ann_abstract(cerl:get_ann(Call), Val); false -> %% Successful evaluation, but it was not possible %% to express the computed value as a literal. @@ -2176,24 +2176,22 @@ opt_not_in_let_1(V, Call, Body) -> #c_call{module=#c_literal{val=erlang}, name=#c_literal{val='not'}, args=[#c_var{name=V}]} -> - opt_not_in_let_2(Body); + opt_not_in_let_2(Body, Call); _ -> no end. -opt_not_in_let_2(#c_case{clauses=Cs0}=Case) -> +opt_not_in_let_2(#c_case{clauses=Cs0}=Case, NotCall) -> Vars = make_vars([], 1), - Body = #c_call{module=#c_literal{val=erlang}, - name=#c_literal{val='not'}, - args=Vars}, + Body = NotCall#c_call{args=Vars}, Cs = [begin Let = #c_let{vars=Vars,arg=B,body=Body}, C#c_clause{body=opt_not_in_let(Let)} end || #c_clause{body=B}=C <- Cs0], {yes,Case#c_case{clauses=Cs}}; -opt_not_in_let_2(#c_call{}=Call0) -> +opt_not_in_let_2(#c_call{}=Call0, _NotCall) -> invert_call(Call0); -opt_not_in_let_2(_) -> no. +opt_not_in_let_2(_, _) -> no. invert_call(#c_call{module=#c_literal{val=erlang}, name=#c_literal{val=Name0}, diff --git a/lib/compiler/src/v3_codegen.erl b/lib/compiler/src/v3_codegen.erl index f5f3c73793..4df1aadd0a 100644 --- a/lib/compiler/src/v3_codegen.erl +++ b/lib/compiler/src/v3_codegen.erl @@ -1551,12 +1551,10 @@ set_cg([{var,R}], {binary,Segs}, Le, Vdb, Bef, #cg{bfail=Bfail}=St) -> %% Now generate the complete code for constructing the binary. Code = cg_binary(PutCode, Target, Temp, Fail, MaxRegs, Le#l.a), {Sis++Code,Aft,St}; -% Map single variable key -set_cg([{var,R}], {map,Op,Map,[{map_pair,{var,_}=K,V}]}, Le, Vdb, Bef, - #cg{bfail=Bfail}=St) -> - Fail = {f,Bfail}, - {Sis,Int0} = maybe_adjust_stack(Bef, Le#l.i, Le#l.i+1, Vdb, St), +%% Map: single variable key. +set_cg([{var,R}], {map,Op,Map,[{map_pair,{var,_}=K,V}]}, Le, Vdb, Bef, St0) -> + {Sis,Int0} = maybe_adjust_stack(Bef, Le#l.i, Le#l.i+1, Vdb, St0), SrcReg = cg_reg_arg_prefer_y(Map, Int0), Line = line(Le#l.a), @@ -1570,21 +1568,16 @@ set_cg([{var,R}], {map,Op,Map,[{map_pair,{var,_}=K,V}]}, Le, Vdb, Bef, Aft = Aft0#sr{reg=put_reg(R, Aft0#sr.reg)}, Target = fetch_reg(R, Aft#sr.reg), - I = case Op of - assoc -> put_map_assoc; - exact -> put_map_exact - end, - {Sis++[Line]++[{I,Fail,SrcReg,Target,Live,{list,List}}],Aft,St}; + {Is,St1} = set_cg_map(Line, Op, SrcReg, Target, Live, List, St0), + {Sis++Is,Aft,St1}; -% Map (possibly) multiple literal keys -set_cg([{var,R}], {map,Op,Map,Es}, Le, Vdb, Bef, - #cg{bfail=Bfail}=St) -> +%% Map: (possibly) multiple literal keys. +set_cg([{var,R}], {map,Op,Map,Es}, Le, Vdb, Bef, St0) -> %% assert key literals [] = [Var||{map_pair,{var,_}=Var,_} <- Es], - Fail = {f,Bfail}, - {Sis,Int0} = maybe_adjust_stack(Bef, Le#l.i, Le#l.i+1, Vdb, St), + {Sis,Int0} = maybe_adjust_stack(Bef, Le#l.i, Le#l.i+1, Vdb, St0), SrcReg = cg_reg_arg_prefer_y(Map, Int0), Line = line(Le#l.a), @@ -1599,11 +1592,10 @@ set_cg([{var,R}], {map,Op,Map,Es}, Le, Vdb, Bef, Aft = Aft0#sr{reg=put_reg(R, Aft0#sr.reg)}, Target = fetch_reg(R, Aft#sr.reg), - I = case Op of - assoc -> put_map_assoc; - exact -> put_map_exact - end, - {Sis++[Line]++[{I,Fail,SrcReg,Target,Live,{list,List}}],Aft,St}; + {Is,St1} = set_cg_map(Line, Op, SrcReg, Target, Live, List, St0), + {Sis++Is,Aft,St1}; + +%% Everything else. set_cg([{var,R}], Con, Le, Vdb, Bef, St) -> %% Find a place for the return register first. Int = Bef#sr{reg=put_reg(R, Bef#sr.reg)}, @@ -1616,6 +1608,34 @@ set_cg([{var,R}], Con, Le, Vdb, Bef, St) -> end, {Ais,clear_dead(Int, Le#l.i, Vdb),St}. + +set_cg_map(Line, Op0, SrcReg, Target, Live, List, St0) -> + Bfail = St0#cg.bfail, + Fail = {f,St0#cg.bfail}, + Op = case Op0 of + assoc -> put_map_assoc; + exact -> put_map_exact + end, + {OkLbl,St1} = new_label(St0), + {BadLbl,St2} = new_label(St1), + Is = if + Bfail =:= 0 orelse Op =:= put_map_assoc -> + [Line,{Op,{f,0},SrcReg,Target,Live,{list,List}}]; + true -> + %% Ensure that Target is always set, even if + %% the map update operation fails. That is necessary + %% because Target may be included in a test_heap + %% instruction. + [Line, + {Op,{f,BadLbl},SrcReg,Target,Live,{list,List}}, + {jump,{f,OkLbl}}, + {label,BadLbl}, + {move,{atom,ok},Target}, + {jump,Fail}, + {label,OkLbl}] + end, + {Is,St2}. + %%% %%% Code generation for constructing binaries. %%% diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl index a3b0236134..d71411de80 100644 --- a/lib/compiler/src/v3_core.erl +++ b/lib/compiler/src/v3_core.erl @@ -868,12 +868,16 @@ try_exception(Ecs0, St0) -> {Evs,St1} = new_vars(3, St0), % Tag, Value, Info {Ecs1,Ceps,St2} = clauses(Ecs0, St1), [_,Value,Info] = Evs, - Ec = #iclause{anno=#a{anno=[compiler_generated]}, + LA = case Ecs1 of + [] -> []; + [C|_] -> get_lineno_anno(C) + end, + Ec = #iclause{anno=#a{anno=[compiler_generated|LA]}, pats=[c_tuple(Evs)],guard=[#c_literal{val=true}], body=[#iprimop{anno=#a{}, %Must have an #a{} name=#c_literal{val=raise}, args=[Info,Value]}]}, - Hs = [#icase{anno=#a{},args=[c_tuple(Evs)],clauses=Ecs1,fc=Ec}], + Hs = [#icase{anno=#a{anno=LA},args=[c_tuple(Evs)],clauses=Ecs1,fc=Ec}], {Evs,Ceps++Hs,St2}. try_after(As, St0) -> @@ -2098,7 +2102,8 @@ upattern(#c_var{name=V}=Var, Ks, St0) -> true -> {N,St1} = new_var_name(St0), New = #c_var{name=N}, - Test = #icall{anno=#a{us=add_element(N, [V])}, + LA = get_lineno_anno(Var), + Test = #icall{anno=#a{anno=LA,us=add_element(N, [V])}, module=#c_literal{val=erlang}, name=#c_literal{val='=:='}, args=[New,Var]}, diff --git a/lib/compiler/test/beam_block_SUITE.erl b/lib/compiler/test/beam_block_SUITE.erl index d343e26737..4bcb252833 100644 --- a/lib/compiler/test/beam_block_SUITE.erl +++ b/lib/compiler/test/beam_block_SUITE.erl @@ -21,7 +21,7 @@ -export([all/0,suite/0,groups/0,init_per_suite/1,end_per_suite/1, init_per_group/2,end_per_group/2, - get_map_elements/1,otp_7345/1]). + get_map_elements/1,otp_7345/1,move_opt_across_gc_bif/1]). %% The only test for the following functions is that %% the code compiles and is accepted by beam_validator. @@ -36,7 +36,8 @@ all() -> groups() -> [{p,[parallel], [get_map_elements, - otp_7345 + otp_7345, + move_opt_across_gc_bif ]}]. init_per_suite(Config) -> @@ -118,6 +119,22 @@ otp_7345(ObjRef, _RdEnv, Args) -> 10}, id(LlUnitdataReq). + +%% Doing move optimizations across GC bifs are in general not safe. +move_opt_across_gc_bif(_Config) -> + [0,true,1] = positive(speaking), + ok. + +positive(speaking) -> + try + Positive = 0, + [+Positive, case Positive of _ -> true end, paris([], Positive)] + after + mailing + end. + +paris([], P) -> P + 1. + %%% %%% The only test of the following code is that it compiles. %%% diff --git a/lib/compiler/test/compile_SUITE.erl b/lib/compiler/test/compile_SUITE.erl index a15efc2a00..b0148f7103 100644 --- a/lib/compiler/test/compile_SUITE.erl +++ b/lib/compiler/test/compile_SUITE.erl @@ -34,7 +34,7 @@ cover/1, env/1, core/1, core_roundtrip/1, asm/1, sys_pre_attributes/1, dialyzer/1, - warnings/1, pre_load_check/1 + warnings/1, pre_load_check/1, env_compiler_options/1 ]). suite() -> [{ct_hooks,[ts_install_cth]}]. @@ -50,7 +50,8 @@ all() -> other_output, encrypted_abstr, strict_record, cover, env, core, core_roundtrip, asm, - sys_pre_attributes, dialyzer, warnings, pre_load_check]. + sys_pre_attributes, dialyzer, warnings, pre_load_check, + env_compiler_options]. groups() -> []. @@ -1092,6 +1093,23 @@ compiler_modules() -> FN = filename, [list_to_atom(FN:rootname(FN:basename(M), ".beam")) || M <- Ms]. +%% Test that ERL_COMPILER_OPTIONS are correctly retrieved +%% by env_compiler_options/0 + +env_compiler_options(_Config) -> + Cases = [ + {"bin_opt_info", [bin_opt_info]}, + {"'S'", ['S']}, + {"{source, \"test.erl\"}", [{source, "test.erl"}]}, + {"[{d,macro_one,1},{d,macro_two}]", [{d, macro_one, 1}, {d, macro_two}]}, + {"[warn_export_all, warn_export_vars]", [warn_export_all, warn_export_vars]} + ], + F = fun({Env, Expected}) -> + true = os:putenv("ERL_COMPILER_OPTIONS", Env), + Expected = compile:env_compiler_options() + end, + lists:foreach(F, Cases). + %%% %%% Utilities. %%% diff --git a/lib/compiler/test/core_fold_SUITE.erl b/lib/compiler/test/core_fold_SUITE.erl index 442b2d424c..376d2c8e9a 100644 --- a/lib/compiler/test/core_fold_SUITE.erl +++ b/lib/compiler/test/core_fold_SUITE.erl @@ -25,7 +25,8 @@ eq/1,nested_call_in_case/1,guard_try_catch/1,coverage/1, unused_multiple_values_error/1,unused_multiple_values/1, multiple_aliases/1,redundant_boolean_clauses/1, - mixed_matching_clauses/1,unnecessary_building/1]). + mixed_matching_clauses/1,unnecessary_building/1, + no_no_file/1]). -export([foo/0,foo/1,foo/2,foo/3]). @@ -43,7 +44,8 @@ groups() -> eq,nested_call_in_case,guard_try_catch,coverage, unused_multiple_values_error,unused_multiple_values, multiple_aliases,redundant_boolean_clauses, - mixed_matching_clauses,unnecessary_building]}]. + mixed_matching_clauses,unnecessary_building, + no_no_file]}]. init_per_suite(Config) -> @@ -454,4 +456,47 @@ do_unnecessary_building_2({a,_,_}=T) -> [_,_] = [T,none], x}. +%% This test tests that v3_core has provided annotations and that +%% sys_core_fold retains them, so that warnings produced by +%% sys_core_fold will have proper filenames and line numbers. Thus, no +%% "no_file" warnings. +no_no_file(_Config) -> + {'EXIT',{{case_clause,0},_}} = (catch source(true, any)), + surgery = (tim(#{reduction => any}))(), + + false = soul(#{[] => true}), + {'EXIT',{{case_clause,true},_}} = (catch soul(#{[] => false})), + + ok = experiment(), + ok. + +source(true, Activities) -> + case 0 of + Activities when [] -> + Activities + end. + +tim(#{reduction := Emergency}) -> + try + fun() -> surgery end + catch + _ when [] -> + planet + end. + +soul(#{[] := Properly}) -> + not case true of + Properly -> true; + Properly -> 0 + end. + +experiment() -> + case kingdom of + _ -> + +case "map" of + _ -> 0.0 + end + end, + ok. + id(I) -> I. diff --git a/lib/compiler/test/map_SUITE.erl b/lib/compiler/test/map_SUITE.erl index c3c4862794..36e82c1459 100644 --- a/lib/compiler/test/map_SUITE.erl +++ b/lib/compiler/test/map_SUITE.erl @@ -1287,6 +1287,7 @@ t_guard_update(Config) when is_list(Config) -> first = map_guard_update(#{}, #{x=>first}), second = map_guard_update(#{y=>old}, #{x=>second,y=>old}), third = map_guard_update(#{x=>old,y=>old}, #{x=>third,y=>old}), + bad_map_guard_update(), ok. t_guard_update_large(Config) when is_list(Config) -> @@ -1353,6 +1354,29 @@ map_guard_update(M1, M2) when M1#{x=>second} =:= M2 -> second; map_guard_update(M1, M2) when M1#{x:=third} =:= M2 -> third; map_guard_update(_, _) -> error. +bad_map_guard_update() -> + do_bad_map_guard_update(fun burns/1), + do_bad_map_guard_update(fun turns/1), + ok. + +do_bad_map_guard_update(Fun) -> + do_bad_map_guard_update_1(Fun, #{}), + do_bad_map_guard_update_1(Fun, #{true=>1}), + ok. + +do_bad_map_guard_update_1(Fun, Value) -> + %% Note: The business with the seemingly redundant fun + %% disables inlining, which would otherwise change the + %% EXIT reason. + {'EXIT',{function_clause,_}} = (catch Fun(Value)), + ok. + +burns(Richmond) when not (Richmond#{true := 0}); [Richmond] -> + specification. + +turns(Richmond) when not (Richmond#{true => 0}); [Richmond] -> + specification. + t_guard_receive(Config) when is_list(Config) -> M0 = #{ id => 0 }, Pid = spawn_link(fun() -> guard_receive_loop() end), diff --git a/lib/compiler/test/misc_SUITE.erl b/lib/compiler/test/misc_SUITE.erl index f05fe6c943..f543f0d4de 100644 --- a/lib/compiler/test/misc_SUITE.erl +++ b/lib/compiler/test/misc_SUITE.erl @@ -256,12 +256,15 @@ silly_coverage(Config) when is_list(Config) -> {jump,{f,42}}]}],99}, expect_error(fun() -> beam_clean:module(CleanInput, []) end), - %% beam_peep + %% beam_peep. This is tricky. Use a select instruction with + %% an odd number of elements in the list to crash + %% prune_redundant_values/2 but not beam_clean:clean_labels/1. PeepInput = {?MODULE,[{foo,0}],[], [{function,foo,0,2, [{label,1}, {func_info,{atom,?MODULE},{atom,foo},0}, - {label,2}|non_proper_list]}],99}, + {label,2},{select,op,r,{f,2},[{f,2}]}]}], + 2}, expect_error(fun() -> beam_peep:module(PeepInput, []) end), %% beam_bsm. This is tricky. Our function must be sane enough to not crash diff --git a/lib/debugger/src/dbg_debugged.erl b/lib/debugger/src/dbg_debugged.erl index 5b1469a10e..e142af4ae0 100644 --- a/lib/debugger/src/dbg_debugged.erl +++ b/lib/debugger/src/dbg_debugged.erl @@ -70,7 +70,10 @@ msg_loop(Meta, Mref, SaveStacktrace) -> %% Meta is evaluating a receive, must be done within context %% of real (=this) process {sys, Meta, {'receive',Msg}} -> - receive Msg -> Meta ! {self(), rec_acked} end, + receive Msg -> + Meta ! {self(), rec_acked}, + ok + end, msg_loop(Meta, Mref, SaveStacktrace); %% Meta needs something evaluated within context of real process diff --git a/lib/debugger/src/dbg_icmd.erl b/lib/debugger/src/dbg_icmd.erl index 1b274e20ae..57a3719a50 100644 --- a/lib/debugger/src/dbg_icmd.erl +++ b/lib/debugger/src/dbg_icmd.erl @@ -171,10 +171,10 @@ handle_cmd(Bs, Status, Ieval) -> %% User control of process execution and settings %%==================================================================== -step(Meta) -> Meta ! {user, {cmd, step}}. -next(Meta) -> Meta ! {user, {cmd, next}}. -continue(Meta) -> Meta ! {user, {cmd, continue}}. -finish(Meta) -> Meta ! {user, {cmd, finish}}. +step(Meta) -> Meta ! {user, {cmd, step}}, ok. +next(Meta) -> Meta ! {user, {cmd, next}}, ok. +continue(Meta) -> Meta ! {user, {cmd, continue}}, ok. +finish(Meta) -> Meta ! {user, {cmd, finish}}, ok. skip(Meta) -> Meta ! {user, {cmd, skip}}. timeout(Meta) -> Meta ! {user, timeout}. diff --git a/lib/debugger/src/dbg_iserver.erl b/lib/debugger/src/dbg_iserver.erl index 0ad303d8d9..3561454685 100644 --- a/lib/debugger/src/dbg_iserver.erl +++ b/lib/debugger/src/dbg_iserver.erl @@ -72,17 +72,17 @@ cast(Int, Request) -> gen_server:cast(Int, Request). safe_call(Request) -> - ensure_started(), + {ok, _} = ensure_started(), call(Request). safe_cast(Request) -> - ensure_started(), + {ok, _} = ensure_started(), cast(Request). ensure_started() -> case whereis(?MODULE) of undefined -> start(); - _Pid -> ignore + Pid -> {ok, Pid} end. %%--Module database--------------------------------------------------- @@ -402,8 +402,10 @@ handle_cast({set_status, Meta, Status, Info}, State) -> send_all(subscriber, {new_status, Proc#proc.pid, Status, Info}, State), if Status =:= break -> - auto_attach(break, State#state.auto, Proc); - true -> ignore + _ = auto_attach(break, State#state.auto, Proc), + ok; + true -> + ok end, Proc2 = Proc#proc{status=Status, info=Info}, {noreply, State#state{procs=lists:keyreplace(Meta, #proc.meta, @@ -470,7 +472,7 @@ handle_info({'EXIT',Who,Why}, State) -> [self(),AttPid,Pid,Why,ExitInfo]); undefined -> %% Otherwise, auto attach if necessary - auto_attach(exit, State#state.auto, Pid), + _ = auto_attach(exit, State#state.auto, Pid), Who end, send_all(subscriber, {new_status,Pid,exit,Why}, State), @@ -583,7 +585,8 @@ send_all(Pids, Msg) -> lists:foreach(fun(Pid) -> send(Pid, Msg) end, Pids). send(Pid, Msg) -> - Pid ! {int, Msg}. + Pid ! {int, Msg}, + ok. get_proc({Type, Pid}, Procs) -> Index = case Type of diff --git a/lib/debugger/src/dbg_wx_break_win.erl b/lib/debugger/src/dbg_wx_break_win.erl index cd1e81456f..770681510d 100644 --- a/lib/debugger/src/dbg_wx_break_win.erl +++ b/lib/debugger/src/dbg_wx_break_win.erl @@ -65,18 +65,18 @@ create_win(Parent, Pos, function, Mod, _Line) -> {choices, IntStrs}]), Expand = [{border, 5}, {flag,?wxLEFT bor ?wxRIGHT bor ?wxEXPAND}], - wxSizer:add(MainS, Label, [{border,5}, + _ = wxSizer:add(MainS, Label, [{border,5}, {flag,?wxTOP bor ?wxLEFT bor ?wxRIGHT}]), - wxSizer:add(MainS, Text, Expand), + _ = wxSizer:add(MainS, Text, Expand), FunLabel = wxStaticText:new(Win, ?wxID_ANY, "Function:"), LB = wxListBox:new(Win, ?wxID_ANY, [{size,{-1, 100}},{style,?wxLB_MULTIPLE}]), - wxSizer:add(MainS, FunLabel, Expand), - wxSizer:add(MainS, LB, [{proportion,1}|Expand]), + _ = wxSizer:add(MainS, FunLabel, Expand), + _ = wxSizer:add(MainS, LB, [{proportion,1}|Expand]), wxSizer:setMinSize(MainS, 300, 400), OK = wxDialog:createStdDialogButtonSizer(Win, ?wxOK bor ?wxCANCEL), - wxSizer:add(MainS, OK, [{border,5},{flag,?wxALL}]), + _ = wxSizer:add(MainS, OK, [{border,5},{flag,?wxALL}]), wxDialog:setSizer(Win,MainS), - wxSizer:fit(MainS, Win), + _ = wxSizer:fit(MainS, Win), wxSizer:setSizeHints(MainS,Win), wxComboBox:setFocus(Text), wxDialog:connect(Win, command_button_clicked), @@ -110,11 +110,11 @@ create_win(Parent, Pos, Type, Mod, Line) -> IntStrs = [atom_to_list(M) || M <- Int], ModT = wxComboBox:new(Win, ?wxID_ANY, [{choices,IntStrs}]), ModSz = create_label_of_control(Win, "Module:", ModT, Mod), - wxSizer:add(MainS,ModSz,[{flag, ?wxEXPAND}]), + _ = wxSizer:add(MainS,ModSz,[{flag, ?wxEXPAND}]), %% Create rest of text input fields Add = fun({IType, Label, Def}) -> {Sz, Text} = create_sizer_with_text(Win, Label, Def), - wxSizer:add(MainS, Sz, [{flag, ?wxEXPAND}]), + _ = wxSizer:add(MainS, Sz, [{flag, ?wxEXPAND}]), {Text, IType} end, Inputs = case Type of @@ -129,15 +129,15 @@ create_win(Parent, Pos, Type, Mod, Line) -> Entries = wx:map(Add, Inputs), %% Create and add radio box {TriggerBox,Trigger} = create_trigger_box(Win), - wxSizer:add(MainS, TriggerBox, [{border,5},{flag,?wxALL bor ?wxEXPAND}]), + _ = wxSizer:add(MainS, TriggerBox, [{border,5},{flag,?wxALL bor ?wxEXPAND}]), - wxSizer:addStretchSpacer(MainS), + _ = wxSizer:addStretchSpacer(MainS), %% Put it together OK = wxDialog:createStdDialogButtonSizer(Win, ?wxOK bor ?wxCANCEL), - wxSizer:add(MainS, OK, [{border,5},{flag,?wxALL}]), + _ = wxSizer:add(MainS, OK, [{border,5},{flag,?wxALL}]), wxSizer:setMinSize(MainS, 300, -1), wxDialog:setSizer(Win,MainS), - wxSizer:fit(MainS, Win), + _ = wxSizer:fit(MainS, Win), wxSizer:setSizeHints(MainS,Win), wxComboBox:setFocus(ModT), wxDialog:connect(Win, command_button_clicked), @@ -243,8 +243,8 @@ create_label_of_control(Parent, Label, Control, Def) -> Text = wxStaticText:new(Parent, ?wxID_ANY, Label), Border = {border, 5}, Flag = ?wxRIGHT bor ?wxLEFT bor ?wxALIGN_CENTRE_VERTICAL, - wxSizer:add(Sizer, Text, [{proportion,1}, {flag,Flag}, Border]), - wxSizer:add(Sizer, Control, [{proportion,3}, {flag,Flag bor ?wxEXPAND}, Border]), + _ = wxSizer:add(Sizer, Text, [{proportion,1}, {flag,Flag}, Border]), + _ = wxSizer:add(Sizer, Control, [{proportion,3}, {flag,Flag bor ?wxEXPAND}, Border]), wxControl:setLabel(Control, dbg_wx_win:to_string(Def)), Sizer. @@ -252,11 +252,11 @@ create_trigger_box(Win) -> SBox = wxStaticBox:new(Win, ?wxID_ANY, "Trigger Action:"), SBS = wxStaticBoxSizer:new(SBox, ?wxVERTICAL), Ebtn = wxRadioButton:new(Win, ?wxID_ANY, "Enable"), - wxSizer:add(SBS,Ebtn), + _ = wxSizer:add(SBS,Ebtn), Dibtn = wxRadioButton:new(Win, ?wxID_ANY, "Disable"), - wxSizer:add(SBS,Dibtn), + _ = wxSizer:add(SBS,Dibtn), Debtn = wxRadioButton:new(Win, ?wxID_ANY, "Delete"), - wxSizer:add(SBS,Debtn), + _ = wxSizer:add(SBS,Debtn), wxRadioButton:setValue(Ebtn, true), {SBS, [{Ebtn,enable},{Dibtn,disable},{Debtn,delete}]}. diff --git a/lib/debugger/src/dbg_wx_code.erl b/lib/debugger/src/dbg_wx_code.erl index f8fc331a81..473963500a 100644 --- a/lib/debugger/src/dbg_wx_code.erl +++ b/lib/debugger/src/dbg_wx_code.erl @@ -127,20 +127,22 @@ load_code(Ed, Code) -> %%io:format("~p ~p ~p~n", [Lines, Sz, LW]), ?stc:setMarginWidth(Ed, 0, LW+5), ?stc:setReadOnly(Ed, true), - Ed. + ok. unload_code(Ed) -> ?stc:setReadOnly(Ed, false), ?stc:setTextRaw(Ed, <<0:8>>), ?stc:setReadOnly(Ed, true), - Ed. + ok. add_break_to_code(Ed, Line, active) -> ?stc:markerDelete(Ed, Line-1, 1), - ?stc:markerAdd(Ed, Line-1, 0); + ?stc:markerAdd(Ed, Line-1, 0), + ok; add_break_to_code(Ed, Line, inactive) -> ?stc:markerDelete(Ed, Line-1, 0), - ?stc:markerAdd(Ed, Line-1, 1). + ?stc:markerAdd(Ed, Line-1, 1), + ok. del_break_from_code(Ed,Line) -> ?stc:markerDelete(Ed, Line-1, 0), diff --git a/lib/debugger/src/dbg_wx_filedialog_win.erl b/lib/debugger/src/dbg_wx_filedialog_win.erl index 2103536a04..f7b031dc28 100644 --- a/lib/debugger/src/dbg_wx_filedialog_win.erl +++ b/lib/debugger/src/dbg_wx_filedialog_win.erl @@ -118,9 +118,9 @@ init([Parent, Id, Options0]) -> wxTextCtrl:connect(Dir, char, [{callback, IsTab}]), Top = wxBoxSizer:new(?wxHORIZONTAL), - wxSizer:add(Top, Back, [{border, 2},{flag,?wxALL bor ?wxEXPAND}]), - wxSizer:add(Top, Forw, [{border, 2},{flag,?wxALL bor ?wxEXPAND}]), - wxSizer:add(Top, Up, [{border, 2},{flag,?wxALL bor ?wxEXPAND}]), + _ = wxSizer:add(Top, Back, [{border, 2},{flag,?wxALL bor ?wxEXPAND}]), + _ = wxSizer:add(Top, Forw, [{border, 2},{flag,?wxALL bor ?wxEXPAND}]), + _ = wxSizer:add(Top, Up, [{border, 2},{flag,?wxALL bor ?wxEXPAND}]), %% List Ctrl {Art, IconMap} = create_icons(ExtraIcons), @@ -154,13 +154,13 @@ init([Parent, Id, Options0]) -> %% OK done Box = wxBoxSizer:new(?wxVERTICAL), - wxSizer:add(Box, Top, [{border, 2}, {flag,?wxALL bor ?wxEXPAND}]), - wxSizer:add(Box, Dir, [{border, 2}, {flag,?wxALL bor ?wxEXPAND}]), - wxSizer:add(Box, LC, [{border, 2}, {flag,?wxALL bor ?wxEXPAND}, {proportion, 1}]), - wxSizer:add(Box, Bott, [{border, 2}, {flag,?wxALL bor ?wxEXPAND}]), + _ = wxSizer:add(Box, Top, [{border, 2}, {flag,?wxALL bor ?wxEXPAND}]), + _ = wxSizer:add(Box, Dir, [{border, 2}, {flag,?wxALL bor ?wxEXPAND}]), + _ = wxSizer:add(Box, LC, [{border, 2}, {flag,?wxALL bor ?wxEXPAND}, {proportion, 1}]), + _ = wxSizer:add(Box, Bott, [{border, 2}, {flag,?wxALL bor ?wxEXPAND}]), wxWindow:setSizer(Dlg, Box), - wxSizer:fit(Box, Dlg), + _ = wxSizer:fit(Box, Dlg), wxSizer:setSizeHints(Box,Dlg), State = #state{win=Dlg, back=Back, forward=Forw, up=Up, diff --git a/lib/debugger/src/dbg_wx_mon.erl b/lib/debugger/src/dbg_wx_mon.erl index 345367a911..a32a6894b8 100644 --- a/lib/debugger/src/dbg_wx_mon.erl +++ b/lib/debugger/src/dbg_wx_mon.erl @@ -135,7 +135,7 @@ init2(CallingPid, Mode, SFile, GS) -> %% Start other necessary stuff dbg_wx_win:init(), - dbg_wx_winman:start(), % Debugger window manager + _ = dbg_wx_winman:start(), % Debugger window manager %% Create monitor window Title = "Monitor", @@ -339,7 +339,7 @@ gui_cmd('Delete All Modules', State) -> lists:foreach(fun(Mod) -> int:nn(Mod) end, int:interpreted()), State; gui_cmd({module, Mod, What}, State) -> - case What of + _ = case What of delete -> int:nn(Mod); view -> Window = dbg_wx_mon_win:get_window(State#state.win), diff --git a/lib/debugger/src/dbg_wx_mon_win.erl b/lib/debugger/src/dbg_wx_mon_win.erl index 2e48210f55..9737c9e67f 100644 --- a/lib/debugger/src/dbg_wx_mon_win.erl +++ b/lib/debugger/src/dbg_wx_mon_win.erl @@ -107,31 +107,31 @@ create_win_batch(Title, Menus) -> Hlb = 200, Listbox = wxListBox:new(Panel, ?wxID_ANY, [{size,{?Wf,Hlb}}, {style,?wxLB_SINGLE}]), - wxSizer:add(LeftSz,Listbox,[{proportion,1},{border,3},{flag,?wxEXPAND}]), + _ = wxSizer:add(LeftSz,Listbox,[{proportion,1},{border,3},{flag,?wxEXPAND}]), wxListBox:connect(Listbox, command_listbox_doubleclicked), wxListBox:connect(Listbox, right_down), SBox = wxStaticBox:new(Panel, ?wxID_ANY, "Auto Attach:"), SBS = wxStaticBoxSizer:new(SBox, ?wxVERTICAL), Fbtn = wxCheckBox:new(Panel, ?autoId, "First Call"), - wxSizer:add(SBS,Fbtn), + _ = wxSizer:add(SBS,Fbtn), Bbtn = wxCheckBox:new(Panel, ?autoId, "On Break"), - wxSizer:add(SBS,Bbtn), + _ = wxSizer:add(SBS,Bbtn), Ebtn = wxCheckBox:new(Panel, ?autoId, "On Exit"), - wxSizer:add(SBS,Ebtn), + _ = wxSizer:add(SBS,Ebtn), wxFrame:connect(Panel, command_checkbox_clicked), - wxSizer:add(LeftSz,SBS, [{flag,?wxEXPAND}]), + _ = wxSizer:add(LeftSz,SBS, [{flag,?wxEXPAND}]), SLabel = wxStaticText:new(Panel, ?wxID_ANY, "Stack Trace:\n On (with tail)"), - wxSizer:add(LeftSz,SLabel), + _ = wxSizer:add(LeftSz,SLabel), BLabel = wxStaticText:new(Panel, ?wxID_ANY, "Back Trace Size:\n 50000"), - wxSizer:add(LeftSz,BLabel), + _ = wxSizer:add(LeftSz,BLabel), StringsBox = wxStaticBox:new(Panel, ?wxID_ANY, "Strings:"), StringsBS = wxStaticBoxSizer:new(StringsBox, ?wxVERTICAL), Stringsbtn = wxCheckBox:new(Panel, ?stringsId, ?STRTEXT), - wxSizer:add(StringsBS,Stringsbtn), - wxSizer:add(LeftSz,StringsBS, [{flag,?wxEXPAND}]), + _ = wxSizer:add(StringsBS,Stringsbtn), + _ = wxSizer:add(LeftSz,StringsBS, [{flag,?wxEXPAND}]), %% Create list_crtl / grid Grid = wxListCtrl:new(Panel, [{winid, ?GRID}, @@ -169,12 +169,12 @@ create_win_batch(Title, Menus) -> wxWindow:setFocus(Grid), %% Put it in the window - wxSizer:add(MainSz, LeftSz, [{border, 3}, {flag,?wxALL bor ?wxEXPAND}]), - wxSizer:add(MainSz, Grid, [{border, 3}, {flag,?wxALL bor ?wxEXPAND}, + _ = wxSizer:add(MainSz, LeftSz, [{border, 3}, {flag,?wxALL bor ?wxEXPAND}]), + _ = wxSizer:add(MainSz, Grid, [{border, 3}, {flag,?wxALL bor ?wxEXPAND}, {proportion, 1}]), wxWindow:setSizer(Panel,MainSz), - wxSizer:fit(MainSz, Win), + _ = wxSizer:fit(MainSz, Win), wxSizer:setSizeHints(MainSz,Win), IconFile = dbg_wx_win:find_icon("erlang_bug.png"), diff --git a/lib/debugger/src/dbg_wx_src_view.erl b/lib/debugger/src/dbg_wx_src_view.erl index 571c6b01bb..207c407fbc 100644 --- a/lib/debugger/src/dbg_wx_src_view.erl +++ b/lib/debugger/src/dbg_wx_src_view.erl @@ -56,7 +56,7 @@ code_area(Parent, Sizer) -> end, [SetStyle(Style) || Style <- Styles], ?stc:setKeyWords(Ed, 0, keyWords()), - wxSizer:add(Sizer, Ed, [{proportion,1}, {flag, ?wxEXPAND}]), + _ = wxSizer:add(Sizer, Ed, [{proportion,1}, {flag, ?wxEXPAND}]), Ed. diff --git a/lib/debugger/src/dbg_wx_trace.erl b/lib/debugger/src/dbg_wx_trace.erl index f9c60f9b72..6af19af33b 100644 --- a/lib/debugger/src/dbg_wx_trace.erl +++ b/lib/debugger/src/dbg_wx_trace.erl @@ -321,7 +321,7 @@ gui_cmd('Kill', State) -> exit(State#state.pid, kill), State; gui_cmd('Messages', State) -> - case int:meta(State#state.meta, messages) of + _ = case int:meta(State#state.meta, messages) of [] -> dbg_wx_trace_win:eval_output(State#state.win,"< No Messages!\n", bold); Messages -> diff --git a/lib/debugger/src/dbg_wx_trace_win.erl b/lib/debugger/src/dbg_wx_trace_win.erl index 51687ad4e9..972a917728 100644 --- a/lib/debugger/src/dbg_wx_trace_win.erl +++ b/lib/debugger/src/dbg_wx_trace_win.erl @@ -123,7 +123,8 @@ %% GS = term() %%-------------------------------------------------------------------- init() -> - dbg_wx_win:init(). + _ = dbg_wx_win:init(), + ok. stop(#winInfo{window=Win}) -> (catch wxFrame:destroy(Win)), @@ -149,29 +150,29 @@ create_win(Parent, Title, Windows, Menus) -> Sizer = wxBoxSizer:new(?wxVERTICAL), Code = code_area(Panel), - wxSizer:add(Sizer, Code#sub.win, + _ = wxSizer:add(Sizer, Code#sub.win, [{proportion,1}, {border, 2}, {flag, ?wxEXPAND bor ?wxDOWN}]), wxSizer:setVirtualSizeHints(Sizer, Code#sub.win), ExpandWithBorder = [{border, 3},{flag,?wxEXPAND bor ?wxALL}], Search = search_area(Panel), - wxSizer:add(Sizer, Search#sub.win, ExpandWithBorder), + _ = wxSizer:add(Sizer, Search#sub.win, ExpandWithBorder), Bs = button_area(Panel), - wxSizer:add(Sizer, Bs#sub.win, ExpandWithBorder), + _ = wxSizer:add(Sizer, Bs#sub.win, ExpandWithBorder), InfoArea = wxBoxSizer:new(?wxHORIZONTAL), wxSizer:setMinSize(InfoArea, {100, ?EVAL_H}), Eval = eval_area(Panel), - wxSizer:add(InfoArea, Eval#sub.win, [{proportion,1},{flag,?wxEXPAND}]), + _ = wxSizer:add(InfoArea, Eval#sub.win, [{proportion,1},{flag,?wxEXPAND}]), Bind = bind_area(Panel), - wxSizer:add(InfoArea, Bind#sub.win, + _ = wxSizer:add(InfoArea, Bind#sub.win, [{proportion,1},{border, 2}, {flag,?wxEXPAND bor ?wxLEFT}]), - wxSizer:add(Sizer, InfoArea, ExpandWithBorder), + _ = wxSizer:add(Sizer, InfoArea, ExpandWithBorder), Trace = trace_area(Panel), - wxSizer:add(Sizer, Trace#sub.win, ExpandWithBorder), + _ = wxSizer:add(Sizer, Trace#sub.win, ExpandWithBorder), SB = wxFrame:createStatusBar(Win,[]), %% Note id and lastId to get the event when it dragged is complete @@ -192,7 +193,7 @@ create_win(Parent, Title, Windows, Menus) -> Wi = show_windows(enable_windows(Wi0,Windows)), wxWindow:setSizer(Panel, Sizer), - wxSizer:fit(Sizer, Win), + _ = wxSizer:fit(Sizer, Win), wxSizer:setSizeHints(Sizer,Win), IconFile = dbg_wx_win:find_icon("erlang_bug.png"), @@ -230,11 +231,11 @@ get_window(WinInfo) -> %%-------------------------------------------------------------------- configure(Wi=#winInfo{window=Win,m_szr={Panel,Sizer}}) -> wx:batch(fun() -> - show_windows(Wi), + _ = show_windows(Wi), wxSizer:layout(Sizer), %%wxWindow:setSizerAndFit(Panel,Sizer), wxWindow:setSizer(Panel, Sizer), - wxSizer:fit(Sizer, Win), + _ = wxSizer:fit(Sizer, Win), wxSizer:setSizeHints(Sizer,Win), Wi end). @@ -242,10 +243,10 @@ configure(Wi=#winInfo{window=Win,m_szr={Panel,Sizer}}) -> configure(Wi0=#winInfo{window=Win,m_szr={Panel,Sizer}}, Windows) -> wx:batch(fun() -> Wi = enable_windows(Wi0, Windows), - show_windows(Wi), + _ = show_windows(Wi), wxSizer:layout(Sizer), wxWindow:setSizer(Panel, Sizer), - wxSizer:fit(Sizer, Win), + _ = wxSizer:fit(Sizer, Win), wxSizer:setSizeHints(Sizer,Win), Wi end). @@ -348,7 +349,7 @@ add_break(WinInfo, Menu, {{Mod,Line},[Status|_Options]}=Break) -> case WinInfo#winInfo.editor of {Mod, Editor} -> dbg_wx_code:add_break_to_code(Editor, Line, Status); - _ -> ignore + _ -> ok end, add_break_to_menu(WinInfo, Menu, Break). @@ -372,7 +373,7 @@ update_break(WinInfo, {{Mod,Line},[Status|_Options]}=Break) -> case WinInfo#winInfo.editor of {Mod, Editor} -> dbg_wx_code:add_break_to_code(Editor, Line, Status); - _ -> ignore + _ -> ok end, update_break_in_menu(WinInfo, Break). @@ -929,7 +930,7 @@ button_area(Parent) -> B=wxButton:new(Parent, Button, [{label,dbg_wx_win:to_string(Name)}]), Id = wxWindow:getId(B), - wxSizer:add(Sz,B, []), + _ = wxSizer:add(Sz,B, []), wxButton:connect(B, command_button_clicked, [{id,Id}]) end, buttons()), #sub{name='Button Area', win=Sz}. @@ -938,22 +939,22 @@ button_area(Parent) -> search_area(Parent) -> HSz = wxBoxSizer:new(?wxHORIZONTAL), - wxSizer:add(HSz, wxStaticText:new(Parent, ?wxID_ANY, "Find:"), + _ = wxSizer:add(HSz, wxStaticText:new(Parent, ?wxID_ANY, "Find:"), [{flag,?wxALIGN_CENTER_VERTICAL}]), TC1 = wxTextCtrl:new(Parent, ?SEARCH_ENTRY, [{style, ?wxTE_PROCESS_ENTER}]), - wxSizer:add(HSz, TC1, [{proportion,3}, {flag, ?wxEXPAND}]), + _ = wxSizer:add(HSz, TC1, [{proportion,3}, {flag, ?wxEXPAND}]), Nbtn = wxRadioButton:new(Parent, ?wxID_ANY, "Next"), wxRadioButton:setValue(Nbtn, true), - wxSizer:add(HSz,Nbtn,[{flag,?wxALIGN_CENTER_VERTICAL}]), + _ = wxSizer:add(HSz,Nbtn,[{flag,?wxALIGN_CENTER_VERTICAL}]), Pbtn = wxRadioButton:new(Parent, ?wxID_ANY, "Previous"), - wxSizer:add(HSz,Pbtn,[{flag,?wxALIGN_CENTER_VERTICAL}]), + _ = wxSizer:add(HSz,Pbtn,[{flag,?wxALIGN_CENTER_VERTICAL}]), Cbtn = wxCheckBox:new(Parent, ?wxID_ANY, "Match Case"), - wxSizer:add(HSz,Cbtn,[{flag,?wxALIGN_CENTER_VERTICAL}]), - wxSizer:add(HSz, 15,15, [{proportion,1}, {flag, ?wxEXPAND}]), - wxSizer:add(HSz, wxStaticText:new(Parent, ?wxID_ANY, "Goto Line:"), + _ = wxSizer:add(HSz,Cbtn,[{flag,?wxALIGN_CENTER_VERTICAL}]), + _ = wxSizer:add(HSz, 15,15, [{proportion,1}, {flag, ?wxEXPAND}]), + _ = wxSizer:add(HSz, wxStaticText:new(Parent, ?wxID_ANY, "Goto Line:"), [{flag,?wxALIGN_CENTER_VERTICAL}]), TC2 = wxTextCtrl:new(Parent, ?GOTO_ENTRY, [{style, ?wxTE_PROCESS_ENTER}]), - wxSizer:add(HSz, TC2, [{proportion,0}, {flag, ?wxEXPAND}]), + _ = wxSizer:add(HSz, TC2, [{proportion,0}, {flag, ?wxEXPAND}]), wxTextCtrl:connect(TC1, command_text_updated), wxTextCtrl:connect(TC1, command_text_enter), wxTextCtrl:connect(TC1, kill_focus), @@ -969,14 +970,14 @@ eval_area(Parent) -> VSz = wxBoxSizer:new(?wxVERTICAL), HSz = wxBoxSizer:new(?wxHORIZONTAL), - wxSizer:add(HSz, wxStaticText:new(Parent, ?wxID_ANY, "Evaluator:"), + _ = wxSizer:add(HSz, wxStaticText:new(Parent, ?wxID_ANY, "Evaluator:"), [{flag,?wxALIGN_CENTER_VERTICAL}]), TC = wxTextCtrl:new(Parent, ?EVAL_ENTRY, [{style, ?wxTE_PROCESS_ENTER}]), - wxSizer:add(HSz, TC, [{proportion,1}, {flag, ?wxEXPAND}]), - wxSizer:add(VSz, HSz, [{flag, ?wxEXPAND}]), + _ = wxSizer:add(HSz, TC, [{proportion,1}, {flag, ?wxEXPAND}]), + _ = wxSizer:add(VSz, HSz, [{flag, ?wxEXPAND}]), TL = wxTextCtrl:new(Parent, ?EVAL_LOG, [{style, ?wxTE_DONTWRAP bor ?wxTE_MULTILINE bor ?wxTE_READONLY}]), - wxSizer:add(VSz, TL, [{proportion,5}, {flag, ?wxEXPAND}]), + _ = wxSizer:add(VSz, TL, [{proportion,5}, {flag, ?wxEXPAND}]), wxTextCtrl:connect(TC, command_text_enter), #sub{name='Evaluator Area', win=VSz, in=TC, out=TL}. diff --git a/lib/debugger/src/dbg_wx_win.erl b/lib/debugger/src/dbg_wx_win.erl index 1ff8818bbe..25ffc5054c 100644 --- a/lib/debugger/src/dbg_wx_win.erl +++ b/lib/debugger/src/dbg_wx_win.erl @@ -43,7 +43,8 @@ %% GS = term() %%-------------------------------------------------------------------- init() -> - wx:new(). + _ = wx:new(), + ok. %%-------------------------------------------------------------------- %% create_menus(MenuBar, [Menu]) @@ -80,12 +81,12 @@ create_menus(_MB,[], _Win,Id) -> Id. create_menu_item(Menu, [separator|Is], Win, Id,Connect) -> - wxMenu:appendSeparator(Menu), + _ = wxMenu:appendSeparator(Menu), create_menu_item(Menu,Is,Win,Id+1,Connect); create_menu_item(Menu, [{Name, _N, cascade, Items}|Is], Win, Id0,Connect) -> Sub = wxMenu:new([]), Id = create_menu_item(Sub, Items, Win, Id0, false), - wxMenu:append(Menu, ?wxID_ANY, menu_name(Name,ignore), Sub), + _ = wxMenu:append(Menu, ?wxID_ANY, menu_name(Name,ignore), Sub), %% Simulate GS sub checkBox/RadioBox behaviour Self = self(), Butts = [{MI,get(MI)} || {MI,_,_} <- Items], @@ -99,8 +100,8 @@ create_menu_item(Menu, [{Name, _N, cascade, Items}|Is], Win, Id0,Connect) -> Enabled = lists:foldl(IsChecked, [], Butts), Self ! Ev#wx{userData={Name, Enabled}} end, - wxMenu:connect(Win, command_menu_selected, - [{id,Id0},{lastId, Id-1},{callback,Filter}]), + _ = wxMenu:connect(Win, command_menu_selected, + [{id,Id0},{lastId, Id-1},{callback,Filter}]), create_menu_item(Menu, Is, Win, Id, Connect); create_menu_item(Menu, [{Name,Pos}|Is], Win, Id, Connect) -> MenuId = case lists:member(Name, ['Debugger']) of @@ -168,7 +169,7 @@ add_break(Win, MenuName, Point) -> Delete = wxMenu:appendRadioItem(Trigger, ?wxID_ANY,"Delete"), Add(Delete, {break,Point,{trigger,delete}}), - wxMenu:append(Sub, ?wxID_ANY, "Trigger Action", Trigger), + _ = wxMenu:append(Sub, ?wxID_ANY, "Trigger Action", Trigger), MenuBtn = wxMenu:append(Menu,?wxID_ANY, Label, Sub), #break{mb={Menu,MenuBtn}, diff --git a/lib/debugger/src/dbg_wx_winman.erl b/lib/debugger/src/dbg_wx_winman.erl index efa58ae325..ca858fa4bb 100644 --- a/lib/debugger/src/dbg_wx_winman.erl +++ b/lib/debugger/src/dbg_wx_winman.erl @@ -100,11 +100,11 @@ update_windows_menu(Win, [MonInfo|Infos]) -> OldItems = wxMenu:getMenuItems(Menu), [wxMenu:delete(Menu, Item) || Item <- OldItems], menuitem(Win, Menu,MonInfo, 700), - wxMenu:appendSeparator(Menu), + _ = wxMenu:appendSeparator(Menu), wx:foldl(fun(Info,Acc) -> menuitem(Win,Menu,Info,Acc) end, 701, Infos). menuitem(Window, Menu, {Title, Win}, Id) -> - wxMenu:append(Menu, Id, Title), + _ = wxMenu:append(Menu, Id, Title), wxWindow:connect(Window, command_menu_selected, [{id,Id},{userData,{dbg_ui_winman,Win}}]), Id+1. diff --git a/lib/debugger/src/i.erl b/lib/debugger/src/i.erl index 4ed5265bdf..2da3e77618 100644 --- a/lib/debugger/src/i.erl +++ b/lib/debugger/src/i.erl @@ -108,7 +108,7 @@ ib(Module,Function,Arity) -> ib(Module,Function,Arity,Cond) -> Breaks1 = int:all_breaks(Module), - int:break_in(Module,Function,Arity), + ok = int:break_in(Module,Function,Arity), Breaks2 = int:all_breaks(Module), lists:foreach(fun({Mod,Line}) -> int:test_at_break(Mod,Line,Cond) end, Breaks2--Breaks1). diff --git a/lib/debugger/src/int.erl b/lib/debugger/src/int.erl index 3906c22afd..e5bade9abe 100644 --- a/lib/debugger/src/int.erl +++ b/lib/debugger/src/int.erl @@ -352,10 +352,10 @@ start() -> dbg_iserver:start(). stop() -> lists:foreach( fun(Mod) -> - everywhere(distributed, - fun() -> + _ = everywhere(distributed, + fun() -> erts_debug:breakpoint({Mod,'_','_'}, false) - end) + end) end, interpreted()), dbg_iserver:stop(). @@ -524,21 +524,21 @@ check(Mod) when is_atom(Mod) -> catch check_module(Mod); check(File) when is_list(File) -> catch check_file(File). load({Mod, Src, Beam, BeamBin, Exp, Abst}, Dist) -> - everywhere(Dist, - fun() -> + _ = everywhere(Dist, + fun() -> code:purge(Mod), erts_debug:breakpoint({Mod,'_','_'}, false), {module,Mod} = code:load_binary(Mod, Beam, BeamBin) - end), + end), case erl_prim_loader:get_file(filename:absname(Src)) of {ok, SrcBin, _} -> MD5 = code:module_md5(BeamBin), Bin = term_to_binary({interpreter_module,Exp,Abst,SrcBin,MD5}), {module, Mod} = dbg_iserver:safe_call({load, Mod, Src, Bin}), - everywhere(Dist, - fun() -> + _ = everywhere(Dist, + fun() -> true = erts_debug:breakpoint({Mod,'_','_'}, true) > 0 - end), + end), {module, Mod}; error -> error @@ -738,9 +738,9 @@ del_mod(AbsMod, Dist) -> list_to_atom(filename:basename(AbsMod,".erl")) end, dbg_iserver:safe_cast({delete, Mod}), - everywhere(Dist, - fun() -> + _ = everywhere(Dist, + fun() -> erts_debug:breakpoint({Mod,'_','_'}, false), erlang:yield() - end), + end), ok. diff --git a/lib/dialyzer/src/dialyzer_contracts.erl b/lib/dialyzer/src/dialyzer_contracts.erl index d1ffa07706..272ad10e90 100644 --- a/lib/dialyzer/src/dialyzer_contracts.erl +++ b/lib/dialyzer/src/dialyzer_contracts.erl @@ -591,10 +591,13 @@ remove_uses([{Var, Use}|ToRemove], Constrs0) -> remove_uses(_Var, _Use, []) -> []; remove_uses(Var, Use, [Constr|Constrs]) -> {V, Form} = Constr, - case erl_types:t_var_name(V) =:= Var of - true -> [{V, remove_use(Form, Use)}|Constrs]; - false -> [Constr|remove_uses(Var, Use, Constrs)] - end. + NewConstr = case erl_types:t_var_name(V) =:= Var of + true -> + {V, remove_use(Form, Use)}; + false -> + Constr + end, + [NewConstr|remove_uses(Var, Use, Constrs)]. remove_use({var, L, V}, V) -> {var, L, '_'}; remove_use(T, V) when is_tuple(T) -> diff --git a/lib/dialyzer/src/dialyzer_dataflow.erl b/lib/dialyzer/src/dialyzer_dataflow.erl index 5ab0c39c04..3349b12932 100644 --- a/lib/dialyzer/src/dialyzer_dataflow.erl +++ b/lib/dialyzer/src/dialyzer_dataflow.erl @@ -430,17 +430,35 @@ handle_apply(Tree, Map, State) -> handle_apply_or_call(FunInfoList, Args, ArgTypes, Map, Tree, State) -> None = t_none(), + %% Call-site analysis may be inaccurate and consider more funs than those that + %% are actually possible. If all of them are incorrect, then warnings can be + %% emitted. If at least one fun is ok, however, then no warning is emitted, + %% just in case the bad ones are not really possible. The last argument is + %% used for this, with the following encoding: + %% Initial value: {none, []} + %% First fun checked: {one, <List of warns>} + %% More funs checked: {many, <List of warns>} + %% A '{one, []}' can only become '{many, []}'. + %% If at any point an fun does not add warnings, then the list is also + %% replaced with an empty list. handle_apply_or_call(FunInfoList, Args, ArgTypes, Map, Tree, State, - [None || _ <- ArgTypes], None, false). + [None || _ <- ArgTypes], None, false, {none, []}). handle_apply_or_call([{local, external}|Left], Args, ArgTypes, Map, Tree, State, - _AccArgTypes, _AccRet, _HadExternal) -> + _AccArgTypes, _AccRet, _HadExternal, Warns) -> + {HowMany, _} = Warns, + NewHowMany = + case HowMany of + none -> one; + _ -> many + end, + NewWarns = {NewHowMany, []}, handle_apply_or_call(Left, Args, ArgTypes, Map, Tree, State, - ArgTypes, t_any(), true); + ArgTypes, t_any(), true, NewWarns); handle_apply_or_call([{TypeOfApply, {Fun, Sig, Contr, LocalRet}}|Left], Args, ArgTypes, Map, Tree, #state{opaques = Opaques} = State, - AccArgTypes, AccRet, HadExternal) -> + AccArgTypes, AccRet, HadExternal, Warns) -> Any = t_any(), AnyArgs = [Any || _ <- Args], GenSig = {AnyArgs, fun(_) -> t_any() end}, @@ -586,16 +604,32 @@ handle_apply_or_call([{TypeOfApply, {Fun, Sig, Contr, LocalRet}}|Left], end, NewAccRet = t_sup(AccRet, TotalRet), ?debug("NewAccRet: ~s\n", [t_to_string(NewAccRet)]), + {NewWarnings, State4} = state__remove_added_warnings(State, State3), + {HowMany, OldWarnings} = Warns, + NewWarns = + case HowMany of + none -> {one, NewWarnings}; + _ -> + case OldWarnings =:= [] of + true -> {many, []}; + false -> + case NewWarnings =:= [] of + true -> {many, []}; + false -> {many, NewWarnings ++ OldWarnings} + end + end + end, handle_apply_or_call(Left, Args, ArgTypes, Map, Tree, - State3, NewAccArgTypes, NewAccRet, HadExternal); + State4, NewAccArgTypes, NewAccRet, HadExternal, NewWarns); handle_apply_or_call([], Args, _ArgTypes, Map, _Tree, State, - AccArgTypes, AccRet, HadExternal) -> + AccArgTypes, AccRet, HadExternal, {_, Warnings}) -> + State1 = state__add_warnings(Warnings, State), case HadExternal of false -> NewMap = enter_type_lists(Args, AccArgTypes, Map), - {State, NewMap, AccRet}; + {State1, NewMap, AccRet}; true -> - {had_external, State} + {had_external, State1} end. apply_fail_reason(FailedSig, FailedBif, FailedContract) -> @@ -3038,6 +3072,14 @@ state__add_warning(#state{warnings = Warnings, warning_mode = true} = State, end end. +state__remove_added_warnings(OldState, NewState) -> + #state{warnings = OldWarnings} = OldState, + #state{warnings = NewWarnings} = NewState, + {NewWarnings -- OldWarnings, NewState#state{warnings = OldWarnings}}. + +state__add_warnings(Warns, #state{warnings = Warnings} = State) -> + State#state{warnings = Warns ++ Warnings}. + -spec state__set_curr_fun(curr_fun(), state()) -> state(). state__set_curr_fun(undefined, State) -> diff --git a/lib/dialyzer/test/small_SUITE_data/results/higher_order_discrepancy b/lib/dialyzer/test/small_SUITE_data/results/higher_order_discrepancy index 7ce440a60d..11b9ecade6 100644 --- a/lib/dialyzer/test/small_SUITE_data/results/higher_order_discrepancy +++ b/lib/dialyzer/test/small_SUITE_data/results/higher_order_discrepancy @@ -1,4 +1,3 @@ -higher_order_discrepancy.erl:11: The call higher_order_discrepancy:g('foo') will never return since it differs in the 1st argument from the success typing arguments: ('bar') -higher_order_discrepancy.erl:14: Function g/1 has no local return -higher_order_discrepancy.erl:14: The pattern 'bar' can never match the type 'foo' +higher_order_discrepancy.erl:19: Function g/1 has no local return +higher_order_discrepancy.erl:19: The pattern 'bar' can never match the type 'foo' diff --git a/lib/dialyzer/test/small_SUITE_data/src/higher_order_discrepancy.erl b/lib/dialyzer/test/small_SUITE_data/src/higher_order_discrepancy.erl index ff5ee6bac4..f9547d4929 100644 --- a/lib/dialyzer/test/small_SUITE_data/src/higher_order_discrepancy.erl +++ b/lib/dialyzer/test/small_SUITE_data/src/higher_order_discrepancy.erl @@ -1,3 +1,8 @@ +%% With the patch introduced to avoid false warnings in +%% user_SUITE_data/src/wpc_hlines.erl we can unfortunately no longer precisely +%% catch problems like this one... The refinement procedure is still enough to +%% keep some of the details, nevertheless. + -module(higher_order_discrepancy). -export([test/1]). diff --git a/lib/dialyzer/test/small_SUITE_data/src/loopy.erl b/lib/dialyzer/test/small_SUITE_data/src/loopy.erl new file mode 100644 index 0000000000..28125ec3d9 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/loopy.erl @@ -0,0 +1,17 @@ +%% ERL-157, OTP-13653. +%% Would cause Dialyzer to go into an infinite loop. + +-module(loopy). + +-export([loop/1]). + + +-spec loop(Args) -> ok when + Args :: [{Module, Args}], + Module :: module(), + Args :: any(). +loop([{Module, Args} | Rest]) -> + Module:init(Args), + loop(Rest); +loop([]) -> + ok. diff --git a/lib/dialyzer/test/user_SUITE_data/results/wpc_hlines b/lib/dialyzer/test/user_SUITE_data/results/wpc_hlines new file mode 100644 index 0000000000..d6e3f29ab9 --- /dev/null +++ b/lib/dialyzer/test/user_SUITE_data/results/wpc_hlines @@ -0,0 +1,3 @@ + +wpc_hlines.erl:22: Function bad/1 has no local return +wpc_hlines.erl:22: The pattern 'false' can never match the type 'true' diff --git a/lib/dialyzer/test/user_SUITE_data/src/wpc_hlines.erl b/lib/dialyzer/test/user_SUITE_data/src/wpc_hlines.erl new file mode 100644 index 0000000000..8c205a8247 --- /dev/null +++ b/lib/dialyzer/test/user_SUITE_data/src/wpc_hlines.erl @@ -0,0 +1,22 @@ +%% Bug reported by Dan Gudmundsson, test shrunk down by Magnus LÃ¥ng. + +%% The problem is that dialyzer_dep generates edges from the fun +%% application to both of the functions, and then during the warning pass +%% dialyzer_dataflow:handle_apply_or_call generates warnings for any such +%% edge that won't return. + +%% Since dialyzer_dep is currently supposed to overapproximate rather than +%% underapproximate, the fix was to modify handle_apply_or_call to not generate +%% warnings if some of the possible funs can succeed. + +-module(wpc_hlines). + +-export([do_export/0]). + +do_export() -> + {Proj, _} = % The culprit seems to be putting the funs in a tuple + {fun good/1, fun bad/1}, + Proj(true). + +good(_) -> ok. +bad(false) -> ok. diff --git a/lib/edoc/priv/edoc.dtd b/lib/edoc/priv/edoc.dtd index 4278a9e643..89058f5d85 100644 --- a/lib/edoc/priv/edoc.dtd +++ b/lib/edoc/priv/edoc.dtd @@ -103,7 +103,7 @@ <!ATTLIST type name CDATA #IMPLIED> <!ELEMENT union (typevar | atom | integer | float | nil | list | tuple | - fun | record | abstype)+> + fun | record | map | abstype)+> <!ELEMENT typevar EMPTY> <!ATTLIST typevar name CDATA #REQUIRED> @@ -129,6 +129,10 @@ <!ELEMENT field (atom, type)> +<!ELEMENT map (map_field)*> + +<!ELEMENT map_field (type, type)> + <!ELEMENT abstype (erlangName, type*)> <!ATTLIST abstype href CDATA #IMPLIED> diff --git a/lib/edoc/src/edoc_layout.erl b/lib/edoc/src/edoc_layout.erl index f723cd8373..e86d090b13 100644 --- a/lib/edoc/src/edoc_layout.erl +++ b/lib/edoc/src/edoc_layout.erl @@ -898,8 +898,20 @@ t_map(Es) -> Fs = get_elem(map_field, Es), ["#{"] ++ seq(fun t_map_field/1, Fs, ["}"]). -t_map_field(#xmlElement{content = [K,V]}) -> - t_utype_elem(K) ++ [" => "] ++ t_utype_elem(V). +t_map_field(#xmlElement{content = [K,V]}=E) -> + KElem = t_utype_elem(K), + VElem = t_utype_elem(V), + AT = get_attrval(assoc_type, E), + IsAny = fun(["any","()"]) -> true; (_) -> false end, + case AT =:= "assoc" andalso IsAny(KElem) andalso IsAny(VElem) of + true -> "..."; + false -> + AS = case AT of + "assoc" -> " => "; + "exact" -> " := " + end, + KElem ++ [AS] ++ VElem + end. t_record(E, Es) -> Name = ["#"] ++ t_type(get_elem(atom, Es)), @@ -1133,8 +1145,12 @@ ot_tuple(Es) -> ot_map(Es) -> {type,0,map,[ot_map_field(E) || E <- get_elem(map_field,Es)]}. -ot_map_field(#xmlElement{content=[K,V]}) -> - {type,0,map_field_assoc,ot_utype_elem(K), ot_utype_elem(V)}. +ot_map_field(#xmlElement{content=[K,V]}=E) -> + A = case get_attrval(assoc_type, E) of + "assoc" -> map_field_assoc; + "exact" -> map_field_exact + end, + {type,0,A,[ot_utype_elem(K), ot_utype_elem(V)]}. ot_fun(Es) -> Range = ot_utype(get_elem(type, Es)), diff --git a/lib/edoc/src/edoc_parser.yrl b/lib/edoc/src/edoc_parser.yrl index 835e7ccaa6..983e2f8496 100644 --- a/lib/edoc/src/edoc_parser.yrl +++ b/lib/edoc/src/edoc_parser.yrl @@ -36,8 +36,8 @@ Terminals atom float integer var an_var string start_spec start_typedef start_throws start_ref -'(' ')' ',' '.' '=>' '->' '{' '}' '[' ']' '|' '+' ':' '::' '=' '/' '//' '*' -'#' 'where' '<<' '>>' '..' '...'. +'(' ')' ',' '.' '=>' ':=' '->' '{' '}' '[' ']' '|' '+' ':' '::' '=' '/' '//' +'*' '#' 'where' '<<' '>>' '..' '...'. Rootsymbol start. @@ -76,7 +76,15 @@ utype_map_fields -> '$empty' : []. utype_map_fields -> utype_map_field : ['$1']. utype_map_fields -> utype_map_fields ',' utype_map_field : ['$3' | '$1']. -utype_map_field -> utype '=>' utype : #t_map_field{ k_type = '$1', v_type = '$3'}. +utype_map_field -> utype '=>' utype : #t_map_field{assoc_type = assoc, + k_type = '$1', + v_type = '$3'}. +utype_map_field -> utype ':=' utype : #t_map_field{assoc_type = exact, + k_type = '$1', + v_type = '$3'}. +utype_map_field -> '...' : #t_map_field{assoc_type = assoc, + k_type = any(), + v_type = any()}. utype_tuple -> '{' utypes '}' : lists:reverse('$2'). @@ -346,6 +354,9 @@ all_vars([#t_var{} | As]) -> all_vars(As) -> As =:= []. +any() -> + #t_type{name = #t_name{name = any}, args = []}. + %% --------------------------------------------------------------------- %% @doc EDoc type specification parsing. Parses the content of diff --git a/lib/edoc/src/edoc_run.erl b/lib/edoc/src/edoc_run.erl index 9a569d0879..261a649c70 100644 --- a/lib/edoc/src/edoc_run.erl +++ b/lib/edoc/src/edoc_run.erl @@ -44,6 +44,8 @@ -import(edoc_report, [report/2, error/1]). +-type args() :: [string()]. + %% @spec application([string()]) -> none() %% @@ -58,6 +60,7 @@ %% automatically terminated when the call has completed, signalling %% success or failure to the operating system. +-spec application(args()) -> no_return(). application(Args) -> F = fun () -> case parse_args(Args) of @@ -81,6 +84,7 @@ application(Args) -> %% automatically terminated when the call has completed, signalling %% success or failure to the operating system. +-spec files(args()) -> no_return(). files(Args) -> F = fun () -> case parse_args(Args) of @@ -93,6 +97,7 @@ files(Args) -> run(F). %% @hidden Not official yet +-spec toc(args()) -> no_return(). toc(Args) -> F = fun () -> case parse_args(Args) of @@ -126,6 +131,7 @@ toc(Args) -> %% automatically terminated when the call has completed, signalling %% success or failure to the operating system. +-spec file(args()) -> no_return(). file(Args) -> F = fun () -> case parse_args(Args) of @@ -137,8 +143,7 @@ file(Args) -> end, run(F). --spec invalid_args(string(), list()) -> no_return(). - +-spec invalid_args(string(), args()) -> no_return(). invalid_args(Where, Args) -> report("invalid arguments to ~ts: ~w.", [Where, Args]), shutdown_error(). @@ -169,10 +174,12 @@ wait_init() -> %% When and if a function init:stop/1 becomes generally available, we %% can use that instead of delay-and-pray when there is an error. +-spec shutdown_ok() -> no_return(). shutdown_ok() -> %% shut down emulator nicely, signalling "normal termination" init:stop(). +-spec shutdown_error() -> no_return(). shutdown_error() -> %% delay 1 second to allow I/O to finish receive after 1000 -> ok end, diff --git a/lib/edoc/src/edoc_scanner.erl b/lib/edoc/src/edoc_scanner.erl index 36423d63f8..f1d5e1d4b9 100644 --- a/lib/edoc/src/edoc_scanner.erl +++ b/lib/edoc/src/edoc_scanner.erl @@ -146,6 +146,8 @@ scan1([$>,$>|Cs], Toks, Pos) -> scan1(Cs, [{'>>',Pos}|Toks], Pos); scan1([$-,$>|Cs], Toks, Pos) -> scan1(Cs, [{'->',Pos}|Toks], Pos); +scan1([$:,$=|Cs], Toks, Pos) -> + scan1(Cs, [{':=',Pos}|Toks], Pos); scan1([$:,$:|Cs], Toks, Pos) -> scan1(Cs, [{'::',Pos}|Toks], Pos); scan1([$/,$/|Cs], Toks, Pos) -> diff --git a/lib/edoc/src/edoc_specs.erl b/lib/edoc/src/edoc_specs.erl index faee8adf7b..c15dfd328f 100644 --- a/lib/edoc/src/edoc_specs.erl +++ b/lib/edoc/src/edoc_specs.erl @@ -369,11 +369,11 @@ d2e({type,_,map,any}, _Prec) -> d2e({type,_,map,Es}, _Prec) -> #t_map{types = d2e(Es) }; d2e({type,_,map_field_assoc,[K,V]}, Prec) -> - T = #t_map_field{k_type = d2e(K), v_type=d2e(V) }, + T = #t_map_field{assoc_type = assoc, k_type = d2e(K), v_type=d2e(V) }, {P,_R} = erl_parse:type_preop_prec('#'), maybe_paren(P, Prec, T); -d2e({type,_,map_field_exact,K,V}, Prec) -> - T = #t_map_field{k_type = d2e(K), v_type=d2e(V) }, +d2e({type,_,map_field_exact,[K,V]}, Prec) -> + T = #t_map_field{assoc_type = exact, k_type = d2e(K), v_type=d2e(V) }, {P,_R} = erl_parse:type_preop_prec('#'), maybe_paren(P, Prec, T); d2e({type,_,tuple,Ts0}, _Prec) -> diff --git a/lib/edoc/src/edoc_types.erl b/lib/edoc/src/edoc_types.erl index 65fba61a72..5bb68e79fb 100644 --- a/lib/edoc/src/edoc_types.erl +++ b/lib/edoc/src/edoc_types.erl @@ -89,8 +89,8 @@ to_xml(#t_fun{args = As, range = T}, Env) -> wrap_utype(T, Env)]}; to_xml(#t_map{ types = Ts}, Env) -> {map, map(fun to_xml/2, Ts, Env)}; -to_xml(#t_map_field{ k_type=K, v_type=V}, Env) -> - {map_field, [wrap_utype(K,Env), wrap_utype(V, Env)]}; +to_xml(#t_map_field{assoc_type = AT, k_type=K, v_type=V}, Env) -> + {map_field, [{assoc_type, AT}], [wrap_utype(K,Env), wrap_utype(V, Env)]}; to_xml(#t_tuple{types = Ts}, Env) -> {tuple, map(fun wrap_utype/2, Ts, Env)}; to_xml(#t_list{type = T}, Env) -> diff --git a/lib/edoc/src/edoc_types.hrl b/lib/edoc/src/edoc_types.hrl index 7fec10d936..3e5e91484f 100644 --- a/lib/edoc/src/edoc_types.hrl +++ b/lib/edoc/src/edoc_types.hrl @@ -157,5 +157,5 @@ -record(t_paren, {a=[], type}). % parentheses -record(t_map, {a=[], types=[]}). --record(t_map_field, {a=[], k_type, v_type}). +-record(t_map_field, {a=[], assoc_type, k_type, v_type}). diff --git a/lib/eldap/test/eldap_basic_SUITE.erl b/lib/eldap/test/eldap_basic_SUITE.erl index d52a7c83f7..ac3447cfe6 100644 --- a/lib/eldap/test/eldap_basic_SUITE.erl +++ b/lib/eldap/test/eldap_basic_SUITE.erl @@ -32,7 +32,7 @@ -define(manageDsaIT, {control,"2.16.840.1.113730.3.4.2",false,asn1_NOVALUE}). suite() -> - [{timetrap,{seconds,40}}]. + [{timetrap,{seconds,360}}]. all() -> [app, diff --git a/lib/erl_docgen/src/docgen_otp_specs.erl b/lib/erl_docgen/src/docgen_otp_specs.erl index e154323f07..5bc3be7a8d 100644 --- a/lib/erl_docgen/src/docgen_otp_specs.erl +++ b/lib/erl_docgen/src/docgen_otp_specs.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2015. 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. @@ -389,10 +389,10 @@ t_type([#xmlElement{name = list, content = Es}]) -> t_list(Es); t_type([#xmlElement{name = nonempty_list, content = Es}]) -> t_nonempty_list(Es); -t_type([#xmlElement{name = tuple, content = Es}]) -> - t_tuple(Es); t_type([#xmlElement{name = map, content = Es}]) -> t_map(Es); +t_type([#xmlElement{name = tuple, content = Es}]) -> + t_tuple(Es); t_type([#xmlElement{name = 'fun', content = Es}]) -> ["fun("] ++ t_fun(Es) ++ [")"]; t_type([E = #xmlElement{name = record, content = Es}]) -> @@ -435,16 +435,28 @@ t_nonempty_list(Es) -> t_tuple(Es) -> ["{"] ++ seq(fun t_utype_elem/1, Es, ["}"]). +t_fun(Es) -> + ["("] ++ seq(fun t_utype_elem/1, get_content(argtypes, Es), + [") -> "] ++ t_utype(get_elem(type, Es))). + t_map(Es) -> Fs = get_elem(map_field, Es), ["#{"] ++ seq(fun t_map_field/1, Fs, ["}"]). -t_map_field(#xmlElement{content = [K,V]}) -> - [t_utype_elem(K) ++ " => " ++ t_utype_elem(V)]. - -t_fun(Es) -> - ["("] ++ seq(fun t_utype_elem/1, get_content(argtypes, Es), - [") -> "] ++ t_utype(get_elem(type, Es))). +t_map_field(#xmlElement{content = [K,V]}=E) -> + KElem = t_utype_elem(K), + VElem = t_utype_elem(V), + AT = get_attrval(assoc_type, E), + IsAny = fun(["any","()"]) -> true; (_) -> false end, + case AT =:= "assoc" andalso IsAny(KElem) andalso IsAny(VElem) of + true -> "..."; + false -> + AS = case AT of + "assoc" -> " => "; + "exact" -> " := " + end, + KElem ++ [AS] ++ VElem + end. t_record(E, Es) -> Name = ["#"] ++ t_type(get_elem(atom, Es)), @@ -618,8 +630,12 @@ ot_tuple(Es) -> ot_map(Es) -> {type,0,map,[ot_map_field(E) || E <- get_elem(map_field,Es)]}. -ot_map_field(#xmlElement{content=[K,V]}) -> - {type,0,map_field_assoc,[ot_utype_elem(K),ot_utype_elem(V)]}. +ot_map_field(#xmlElement{content=[K,V]}=E) -> + A = case get_attrval(assoc_type, E) of + "assoc" -> map_field_assoc; + "exact" -> map_field_exact + end, + {type,0,A,[ot_utype_elem(K), ot_utype_elem(V)]}. ot_fun(Es) -> Range = ot_utype(get_elem(type, Es)), diff --git a/lib/erl_interface/src/misc/ei_portio.h b/lib/erl_interface/src/misc/ei_portio.h index a14fdbd7d1..fbb61b0ccf 100644 --- a/lib/erl_interface/src/misc/ei_portio.h +++ b/lib/erl_interface/src/misc/ei_portio.h @@ -21,6 +21,12 @@ */ #ifndef _EI_PORTIO_H #define _EI_PORTIO_H +#if !defined(__WIN32__) || !defined(VXWORKS) +#ifdef HAVE_WRITEV +/* Declaration of struct iovec *iov should be visible in this scope. */ +#include <sys/uio.h> +#endif +#endif int ei_accept_t(int fd, void *addr, void *addrlen, unsigned ms); int ei_connect_t(int fd, void *sinp, int sin_siz, unsigned ms); @@ -29,8 +35,7 @@ int ei_write_fill(int fd, const char *buf, int len); int ei_read_fill_t(int fd, char* buf, int len, unsigned ms); int ei_write_fill_t(int fd, const char *buf, int len, unsigned ms); #ifdef HAVE_WRITEV -int ei_writev_fill_t(int fd, const struct iovec *iov, int iovcnt, - unsigned ms); +int ei_writev_fill_t(int fd, const struct iovec *iov, int iovcnt, unsigned ms); #endif #endif /* _EI_PORTIO_H */ diff --git a/lib/inets/src/ftp/ftp.erl b/lib/inets/src/ftp/ftp.erl index c2ca511795..bbf25f8e90 100644 --- a/lib/inets/src/ftp/ftp.erl +++ b/lib/inets/src/ftp/ftp.erl @@ -106,8 +106,8 @@ -type common_reason() :: 'econn' | 'eclosed' | term(). -type file_write_error_reason() :: term(). % See file:write for more info --define(DBG(F,A), 'n/a'). -%%-define(DBG(F,A), io:format(F,A)). +%%-define(DBG(F,A), 'n/a'). +-define(DBG(F,A), io:format(F,A)). %%%========================================================================= %%% API - CLIENT FUNCTIONS @@ -1383,12 +1383,18 @@ handle_call({_, {transfer_chunk, Bin}}, _, #state{chunk = true} = State) -> send_data_message(State, Bin), {reply, ok, State}; +handle_call({_, {transfer_chunk, _}}, _, #state{chunk = false} = State) -> + {reply, {error, echunk}, State}; + handle_call({_, chunk_end}, From, #state{chunk = true} = State) -> close_data_connection(State), activate_ctrl_connection(State), {noreply, State#state{client = From, dsock = undefined, caller = end_chunk_transfer, chunk = false}}; +handle_call({_, chunk_end}, _, #state{chunk = false} = State) -> + {reply, {error, echunk}, State}; + handle_call({_, {quote, Cmd}}, From, #state{chunk = false} = State) -> send_ctrl_message(State, mk_cmd(Cmd, [])), activate_ctrl_connection(State), @@ -1769,12 +1775,12 @@ handle_ctrl_result({pos_compl, _Lines}, {LSock, Caller}}} = State) -> handle_caller(State#state{caller = Caller, dsock = {lsock, LSock}}); -handle_ctrl_result({Status, Lines}, +handle_ctrl_result({Status, _Lines}, #state{mode = active, caller = {setup_data_connection, {LSock, _}}} = State) -> - close_connection(LSock), - ctrl_result_response(Status, State, {error, Lines}); + close_connection({tcp,LSock}), + ctrl_result_response(Status, State, {error, Status}); %% Data connection setup passive mode handle_ctrl_result({pos_compl, Lines}, @@ -1965,7 +1971,7 @@ handle_ctrl_result(_, #state{caller = {handle_dir_data_third_phase, DirData}, {noreply, State#state{client = undefined, caller = undefined}}; handle_ctrl_result({Status, _}, #state{caller = cd} = State) -> - ctrl_result_response(Status, State, {error, epath}); + ctrl_result_response(Status, State, {error, Status}); handle_ctrl_result(Status={epath, _}, #state{caller = {dir,_}} = State) -> ctrl_result_response(Status, State, {error, epath}); @@ -1980,11 +1986,11 @@ handle_ctrl_result({pos_interm, _}, #state{caller = {rename, NewFile}} handle_ctrl_result({Status, _}, #state{caller = {rename, _}} = State) -> - ctrl_result_response(Status, State, {error, epath}); + ctrl_result_response(Status, State, {error, Status}); handle_ctrl_result({Status, _}, #state{caller = rename_second_phase} = State) -> - ctrl_result_response(Status, State, {error, epath}); + ctrl_result_response(Status, State, {error, Status}); %%-------------------------------------------------------------------------- %% File handling - recv_bin @@ -2095,7 +2101,7 @@ handle_ctrl_result({pos_prel, _}, #state{caller = {transfer_data, Bin}} %% Default handle_ctrl_result({Status, Lines}, #state{client = From} = State) when From =/= undefined -> - ctrl_result_response(Status, State, {error, Lines}). + ctrl_result_response(Status, State, {error, Status}). %%-------------------------------------------------------------------------- %% Help functions to handle_ctrl_result @@ -2113,7 +2119,6 @@ ctrl_result_response(Status, #state{client = From} = State, _) (Status =:= epnospc) orelse (Status =:= efnamena) orelse (Status =:= econn) -> -%Status == etnospc; Status == epnospc; Status == econn -> gen_server:reply(From, {error, Status}), %% {stop, normal, {error, Status}, State#state{client = undefined}}; {stop, normal, State#state{client = undefined}}; @@ -2378,6 +2383,7 @@ close_ctrl_connection(#state{csock = Socket}) -> close_connection(Socket). close_data_connection(#state{dsock = undefined}) -> ok; close_data_connection(#state{dsock = Socket}) -> close_connection(Socket). +close_connection({lsock,Socket}) -> gen_tcp:close(Socket); close_connection({tcp, Socket}) -> gen_tcp:close(Socket); close_connection({ssl, Socket}) -> ssl:close(Socket). diff --git a/lib/inets/src/ftp/ftp_response.erl b/lib/inets/src/ftp/ftp_response.erl index 32db2dfe66..7533bc4550 100644 --- a/lib/inets/src/ftp/ftp_response.erl +++ b/lib/inets/src/ftp/ftp_response.erl @@ -194,5 +194,6 @@ interpret_status(?TRANS_NEG_COMPL,_,_) -> trans_neg_compl; interpret_status(?PERM_NEG_COMPL,?FILE_SYSTEM,0) -> epath; interpret_status(?PERM_NEG_COMPL,?FILE_SYSTEM,2) -> epnospc; interpret_status(?PERM_NEG_COMPL,?FILE_SYSTEM,3) -> efnamena; +interpret_status(?PERM_NEG_COMPL,?AUTH_ACC,0) -> elogin; interpret_status(?PERM_NEG_COMPL,_,_) -> perm_neg_compl. diff --git a/lib/inets/test/ftp_SUITE.erl b/lib/inets/test/ftp_SUITE.erl index 08295d4e3c..a8d39e3fe7 100644 --- a/lib/inets/test/ftp_SUITE.erl +++ b/lib/inets/test/ftp_SUITE.erl @@ -50,12 +50,17 @@ %%-------------------------------------------------------------------- %% Common Test interface functions ----------------------------------- %%-------------------------------------------------------------------- +suite() -> + [{timetrap,{seconds,20}}]. + all() -> [ {group, ftp_passive}, {group, ftp_active}, {group, ftps_passive}, - {group, ftps_active} + {group, ftps_active}, + error_ehost, + clean_shutdown ]. groups() -> @@ -92,14 +97,13 @@ ftp_tests()-> recv_chunk, type, quote, - ip_v6_disabled, + error_elogin, progress_report_send, progress_report_recv, not_owner, unexpected_call, unexpected_cast, - unexpected_bang, - clean_shutdown + unexpected_bang ]. %%-------------------------------------------------------------------- @@ -190,35 +194,31 @@ init_per_group(_Group, Config) -> Config. end_per_group(_Group, Config) -> Config. %%-------------------------------------------------------------------- - -init_per_testcase(Case, Config) when (Case =:= progress_report_send) orelse - (Case =:= progress_report_recv) -> - common_init_per_testcase(Case, [{progress, {?MODULE, progress, #progress{}}} | Config]); - -init_per_testcase(Case, Config) -> - common_init_per_testcase(Case, Config). - -common_init_per_testcase(Case, Config0) -> - Group = proplists:get_value(name,proplists:get_value(tc_group_properties,Config0)), - try ?MODULE:Case(doc) of - Msg -> ct:comment(Msg) - catch - _:_-> ok - end, +init_per_testcase(Case, Config0) -> + Group = proplists:get_value(name, proplists:get_value(tc_group_properties,Config0)), TLS = [{tls,[{reuse_sessions,true}]}], ACTIVE = [{mode,active}], PASSIVE = [{mode,passive}], - ExtraOpts = [verbose], + CaseOpts = case Case of + progress_report_send -> [{progress, {?MODULE,progress,#progress{}}}]; + progress_report_recv -> [{progress, {?MODULE,progress,#progress{}}}]; + _ -> [] + end, + ExtraOpts = [verbose | CaseOpts], Config = case Group of - ftp_active -> ftp__open(Config0, ACTIVE ++ExtraOpts); - ftps_active -> ftp__open(Config0, TLS++ ACTIVE ++ExtraOpts); - ftp_passive -> ftp__open(Config0, PASSIVE ++ExtraOpts); - ftps_passive -> ftp__open(Config0, TLS++PASSIVE ++ExtraOpts) + ftp_active -> ftp__open(Config0, ACTIVE ++ ExtraOpts); + ftps_active -> ftp__open(Config0, TLS++ ACTIVE ++ ExtraOpts); + ftp_passive -> ftp__open(Config0, PASSIVE ++ ExtraOpts); + ftps_passive -> ftp__open(Config0, TLS++PASSIVE ++ ExtraOpts); + undefined -> Config0 end, case Case of - user -> Config; - bad_user -> Config; + user -> Config; + bad_user -> Config; + error_elogin -> Config; + error_ehost -> Config; + clean_shutdown -> Config; _ -> Pid = proplists:get_value(ftp,Config), ok = ftp:user(Pid, ?FTP_USER, ?FTP_PASS(atom_to_list(Group)++"-"++atom_to_list(Case)) ), @@ -229,6 +229,9 @@ common_init_per_testcase(Case, Config0) -> end_per_testcase(user, _Config) -> ok; end_per_testcase(bad_user, _Config) -> ok; +end_per_testcase(error_elogin, _Config) -> ok; +end_per_testcase(error_ehost, _Config) -> ok; +end_per_testcase(clean_shutdown, _Config) -> ok; end_per_testcase(_Case, Config) -> case proplists:get_value(tc_status,Config) of ok -> ok; @@ -286,7 +289,8 @@ cd(Config0) -> {ok, PWD} = ftp:pwd(Pid), ExpectedPWD = id2ftp_result(Dir, Config), PWD = ExpectedPWD, - {error, epath} = ftp:cd(Pid, ?BAD_DIR). + {error, epath} = ftp:cd(Pid, ?BAD_DIR), + ok. %%------------------------------------------------------------------------- lcd() -> @@ -359,8 +363,11 @@ rename(Config0) -> id2ftp(NewFile,Config)), true = (chk_file(NewFile,Contents,Config) - and chk_no_file([OldFile],Config)). - + and chk_no_file([OldFile],Config)), + {error,epath} = ftp:rename(Pid, + id2ftp("non_existing_file",Config), + id2ftp(NewFile,Config)), + ok. %%------------------------------------------------------------------------- send() -> @@ -372,14 +379,16 @@ send(Config0) -> Config = set_state([reset,{mkfile,[SrcDir,File],Contents}], Config0), Pid = proplists:get_value(ftp, Config), -chk_no_file([File],Config), -chk_file([SrcDir,File],Contents,Config), + chk_no_file([File],Config), + chk_file([SrcDir,File],Contents,Config), ok = ftp:lcd(Pid, id2ftp(SrcDir,Config)), ok = ftp:cd(Pid, id2ftp("",Config)), ok = ftp:send(Pid, File), + chk_file(File, Contents, Config), - chk_file(File, Contents, Config). + {error,epath} = ftp:send(Pid, "non_existing_file"), + ok. %%------------------------------------------------------------------------- send_3() -> @@ -395,8 +404,10 @@ send_3(Config0) -> ok = ftp:cd(Pid, id2ftp(Dir,Config)), ok = ftp:lcd(Pid, id2ftp("",Config)), ok = ftp:send(Pid, File, RemoteFile), + chk_file([Dir,RemoteFile], Contents, Config), - chk_file([Dir,RemoteFile], Contents, Config). + {error,epath} = ftp:send(Pid, "non_existing_file", RemoteFile), + ok. %%------------------------------------------------------------------------- send_bin() -> @@ -408,24 +419,33 @@ send_bin(Config0) -> Pid = proplists:get_value(ftp, Config), {error, enotbinary} = ftp:send_bin(Pid, "some string", id2ftp(File,Config)), ok = ftp:send_bin(Pid, BinContents, id2ftp(File,Config)), - chk_file(File, BinContents, Config). + chk_file(File, BinContents, Config), + {error, efnamena} = ftp:send_bin(Pid, BinContents, "/nothere"), + ok. %%------------------------------------------------------------------------- send_chunk() -> [{doc, "Send a binary using chunks."}]. send_chunk(Config0) -> - Contents = <<"ftp_SUITE test ...">>, + Contents1 = <<"1: ftp_SUITE test ...">>, + Contents2 = <<"2: ftp_SUITE test ...">>, File = "file.txt", Config = set_state([reset,{mkdir,"incoming"}], Config0), Pid = proplists:get_value(ftp, Config), ok = ftp:send_chunk_start(Pid, id2ftp(File,Config)), + {error, echunk} = ftp:send_chunk_start(Pid, id2ftp(File,Config)), {error, echunk} = ftp:cd(Pid, "incoming"), {error, enotbinary} = ftp:send_chunk(Pid, "some string"), - ok = ftp:send_chunk(Pid, Contents), - ok = ftp:send_chunk(Pid, Contents), + ok = ftp:send_chunk(Pid, Contents1), + ok = ftp:send_chunk(Pid, Contents2), ok = ftp:send_chunk_end(Pid), - chk_file(File, <<Contents/binary,Contents/binary>>, Config). + chk_file(File, <<Contents1/binary,Contents2/binary>>, Config), + + {error, echunk} = ftp:send_chunk(Pid, Contents1), + {error, echunk} = ftp:send_chunk_end(Pid), + {error, efnamena} = ftp:send_chunk_start(Pid, "/"), + ok. %%------------------------------------------------------------------------- delete() -> @@ -436,7 +456,9 @@ delete(Config0) -> Config = set_state([reset,{mkfile,File,Contents}], Config0), Pid = proplists:get_value(ftp, Config), ok = ftp:delete(Pid, id2ftp(File,Config)), - chk_no_file([File], Config). + chk_no_file([File], Config), + {error,epath} = ftp:delete(Pid, id2ftp(File,Config)), + ok. %%------------------------------------------------------------------------- mkdir() -> @@ -446,7 +468,9 @@ mkdir(Config0) -> Config = set_state([reset], Config0), Pid = proplists:get_value(ftp, Config), ok = ftp:mkdir(Pid, id2ftp(NewDir,Config)), - chk_dir([NewDir], Config). + chk_dir([NewDir], Config), + {error,epath} = ftp:mkdir(Pid, id2ftp(NewDir,Config)), + ok. %%------------------------------------------------------------------------- rmdir() -> @@ -456,7 +480,9 @@ rmdir(Config0) -> Config = set_state([reset,{mkdir,Dir}], Config0), Pid = proplists:get_value(ftp, Config), ok = ftp:rmdir(Pid, id2ftp(Dir,Config)), - chk_no_dir([Dir], Config). + chk_no_dir([Dir], Config), + {error,epath} = ftp:rmdir(Pid, id2ftp(Dir,Config)), + ok. %%------------------------------------------------------------------------- append() -> @@ -469,7 +495,9 @@ append(Config0) -> Pid = proplists:get_value(ftp, Config), ok = ftp:append(Pid, id2ftp(SrcFile,Config), id2ftp(DstFile,Config)), ok = ftp:append(Pid, id2ftp(SrcFile,Config), id2ftp(DstFile,Config)), - chk_file(DstFile, <<Contents/binary,Contents/binary>>, Config). + chk_file(DstFile, <<Contents/binary,Contents/binary>>, Config), + {error,epath} = ftp:append(Pid, id2ftp("non_existing_file",Config), id2ftp(DstFile,Config)), + ok. %%------------------------------------------------------------------------- append_bin() -> @@ -511,7 +539,9 @@ recv(Config0) -> ok = ftp:cd(Pid, id2ftp(SrcDir,Config)), ok = ftp:lcd(Pid, id2ftp("",Config)), ok = ftp:recv(Pid, File), - chk_file(File, Contents, Config). + chk_file(File, Contents, Config), + {error,epath} = ftp:recv(Pid, "non_existing_file"), + ok. %%------------------------------------------------------------------------- recv_3() -> @@ -535,7 +565,9 @@ recv_bin(Config0) -> Config = set_state([reset, {mkfile,File,Contents}], Config0), Pid = proplists:get_value(ftp, Config), {ok,Received} = ftp:recv_bin(Pid, id2ftp(File,Config)), - find_diff(Received, Contents). + find_diff(Received, Contents), + {error,epath} = ftp:recv_bin(Pid, id2ftp("non_existing_file",Config)), + ok. %%------------------------------------------------------------------------- recv_chunk() -> @@ -581,6 +613,154 @@ quote(Config) -> %% = ftp:quote(Pid, "list"), ok. +%%------------------------------------------------------------------------- +progress_report_send() -> + [{doc, "Test the option progress for ftp:send/[2,3]"}]. +progress_report_send(Config) when is_list(Config) -> + ReportPid = + spawn_link(?MODULE, progress_report_receiver_init, [self(), 1]), + send(Config), + receive + {ReportPid, ok} -> + ok + end. + +%%------------------------------------------------------------------------- +progress_report_recv() -> + [{doc, "Test the option progress for ftp:recv/[2,3]"}]. +progress_report_recv(Config) when is_list(Config) -> + ReportPid = + spawn_link(?MODULE, progress_report_receiver_init, [self(), 3]), + recv(Config), + receive + {ReportPid, ok} -> + ok + end. + +%%------------------------------------------------------------------------- + +not_owner() -> + [{doc, "Test what happens if a process that not owns the connection tries " + "to use it"}]. +not_owner(Config) when is_list(Config) -> + Pid = proplists:get_value(ftp, Config), + + Parent = self(), + OtherPid = spawn_link( + fun() -> + {error, not_connection_owner} = ftp:pwd(Pid), + ftp:close(Pid), + Parent ! {self(), ok} + end), + receive + {OtherPid, ok} -> + {ok, _} = ftp:pwd(Pid) + end. + + +%%------------------------------------------------------------------------- + + +unexpected_call()-> + [{doc, "Test that behaviour of the ftp process if the api is abused"}]. +unexpected_call(Config) when is_list(Config) -> + Flag = process_flag(trap_exit, true), + Pid = proplists:get_value(ftp, Config), + + %% Serious programming fault, connetion will be shut down + case (catch gen_server:call(Pid, {self(), foobar, 10}, infinity)) of + {error, {connection_terminated, 'API_violation'}} -> + ok; + Unexpected1 -> + exit({unexpected_result, Unexpected1}) + end, + ct:sleep(500), + undefined = process_info(Pid, status), + process_flag(trap_exit, Flag). +%%------------------------------------------------------------------------- + +unexpected_cast()-> + [{doc, "Test that behaviour of the ftp process if the api is abused"}]. +unexpected_cast(Config) when is_list(Config) -> + Flag = process_flag(trap_exit, true), + Pid = proplists:get_value(ftp, Config), + %% Serious programming fault, connetion will be shut down + gen_server:cast(Pid, {self(), foobar, 10}), + ct:sleep(500), + undefined = process_info(Pid, status), + process_flag(trap_exit, Flag). +%%------------------------------------------------------------------------- + +unexpected_bang()-> + [{doc, "Test that connection ignores unexpected bang"}]. +unexpected_bang(Config) when is_list(Config) -> + Flag = process_flag(trap_exit, true), + Pid = proplists:get_value(ftp, Config), + %% Could be an innocent misstake the connection lives. + Pid ! foobar, + ct:sleep(500), + {status, _} = process_info(Pid, status), + process_flag(trap_exit, Flag). + +%%------------------------------------------------------------------------- + +clean_shutdown() -> + [{doc, "Test that owning process that exits with reason " + "'shutdown' does not cause an error message. OTP 6035"}]. + +clean_shutdown(Config) -> + Parent = self(), + HelperPid = spawn( + fun() -> + ftp__open(Config, [verbose]), + Parent ! ok, + receive + nothing -> ok + end + end), + receive + ok -> + PrivDir = proplists:get_value(priv_dir, Config), + LogFile = filename:join([PrivDir,"ticket_6035.log"]), + error_logger:logfile({open, LogFile}), + exit(HelperPid, shutdown), + timer:sleep(2000), + error_logger:logfile(close), + case is_error_report_6035(LogFile) of + true -> ok; + false -> {fail, "Bad logfile"} + end + end. + +%%%---------------------------------------------------------------- +%%% Error codes not tested elsewhere + +error_elogin(Config0) -> + Dir = "test", + OldFile = "old.txt", + NewFile = "new.txt", + SrcDir = "data", + File = "file.txt", + Config = set_state([reset, + {mkdir,Dir}, + {mkfile,OldFile,<<"Contents..">>}, + {mkfile,[SrcDir,File],<<"Contents..">>}], Config0), + + Pid = proplists:get_value(ftp, Config), + ok = ftp:lcd(Pid, id2ftp(SrcDir,Config)), + {error,elogin} = ftp:send(Pid, File), + ok = ftp:lcd(Pid, id2ftp("",Config)), + {error,elogin} = ftp:pwd(Pid), + {error,elogin} = ftp:cd(Pid, id2ftp(Dir,Config)), + {error,elogin} = ftp:rename(Pid, + id2ftp(OldFile,Config), + id2ftp(NewFile,Config)), + ok. + +error_ehost(_Config) -> + {error, ehost} = ftp:open("nohost.nodomain"), + ok. + %%-------------------------------------------------------------------- %% Internal functions ----------------------------------------------- %%-------------------------------------------------------------------- @@ -674,112 +854,7 @@ chk_no_dir(PathList, Config) -> ct:fail("Unexpected error for ~p: ~p",[Path,Error]) end. -%%------------------------------------------------------------------------- -progress_report_send() -> - [{doc, "Test the option progress for ftp:send/[2,3]"}]. -progress_report_send(Config) when is_list(Config) -> - ReportPid = - spawn_link(?MODULE, progress_report_receiver_init, [self(), 1]), - send(Config), - receive - {ReportPid, ok} -> - ok - end. -%%------------------------------------------------------------------------- -progress_report_recv() -> - [{doc, "Test the option progress for ftp:recv/[2,3]"}]. -progress_report_recv(Config) when is_list(Config) -> - ReportPid = - spawn_link(?MODULE, progress_report_receiver_init, [self(), 3]), - recv(Config), - receive - {ReportPid, ok} -> - ok - end. - -%%------------------------------------------------------------------------- - -not_owner() -> - [{doc, "Test what happens if a process that not owns the connection tries " - "to use it"}]. -not_owner(Config) when is_list(Config) -> - Pid = proplists:get_value(ftp, Config), - OtherPid = spawn_link(?MODULE, not_owner, [Pid, self()]), - - receive - {OtherPid, ok} -> - {ok, _} = ftp:pwd(Pid) - end. - - -%%------------------------------------------------------------------------- - - -unexpected_call()-> - [{doc, "Test that behaviour of the ftp process if the api is abused"}]. -unexpected_call(Config) when is_list(Config) -> - Flag = process_flag(trap_exit, true), - Pid = proplists:get_value(ftp, Config), - - %% Serious programming fault, connetion will be shut down - case (catch gen_server:call(Pid, {self(), foobar, 10}, infinity)) of - {error, {connection_terminated, 'API_violation'}} -> - ok; - Unexpected1 -> - exit({unexpected_result, Unexpected1}) - end, - ct:sleep(500), - undefined = process_info(Pid, status), - process_flag(trap_exit, Flag). -%%------------------------------------------------------------------------- - -unexpected_cast()-> - [{doc, "Test that behaviour of the ftp process if the api is abused"}]. -unexpected_cast(Config) when is_list(Config) -> - Flag = process_flag(trap_exit, true), - Pid = proplists:get_value(ftp, Config), - %% Serious programming fault, connetion will be shut down - gen_server:cast(Pid, {self(), foobar, 10}), - ct:sleep(500), - undefined = process_info(Pid, status), - process_flag(trap_exit, Flag). -%%------------------------------------------------------------------------- - -unexpected_bang()-> - [{doc, "Test that connection ignores unexpected bang"}]. -unexpected_bang(Config) when is_list(Config) -> - Flag = process_flag(trap_exit, true), - Pid = proplists:get_value(ftp, Config), - %% Could be an innocent misstake the connection lives. - Pid ! foobar, - ct:sleep(500), - {status, _} = process_info(Pid, status), - process_flag(trap_exit, Flag). - -%%------------------------------------------------------------------------- - -clean_shutdown() -> - [{doc, "Test that owning process that exits with reason " - "'shutdown' does not cause an error message. OTP 6035"}]. - -clean_shutdown(Config) -> - PrivDir = proplists:get_value(priv_dir, Config), - LogFile = filename:join([PrivDir,"ticket_6035.log"]), - Host = proplists:get_value(ftpd_host,Config), - try - Pid = spawn(?MODULE, open_wait_6035, [Host, self()]), - error_logger:logfile({open, LogFile}), - true = kill_ftp_proc_6035(Pid, LogFile), - error_logger:logfile(close) - catch - throw:{error, not_found} -> - {skip, "No available FTP servers"} - end. - %%-------------------------------------------------------------------- -%% Internal functions -%%-------------------------------------------------------------------- - find_executable(Config) -> FTPservers = case proplists:get_value(ftpservers,Config) of undefined -> ?default_ftp_servers; @@ -893,12 +968,6 @@ rm(F, Pfx) -> ok end. -not_owner(FtpPid, Pid) -> - {error, not_connection_owner} = ftp:pwd(FtpPid), - ftp:close(FtpPid), - ct:sleep(100), - Pid ! {self(), ok}. - id2abs(Id, Conf) -> filename:join(proplists:get_value(priv_dir,Conf),ids(Id)). id2ftp(Id, Conf) -> (proplists:get_value(id2ftp,Conf))(ids(Id)). id2ftp_result(Id, Conf) -> (proplists:get_value(id2ftp_result,Conf))(ids(Id)). @@ -912,96 +981,75 @@ is_expected_ftpInName(Id, File, Conf) -> File = (proplists:get_value(id2ftp,Conf is_expected_ftpOutName(Id, File, Conf) -> File = (proplists:get_value(id2ftp_result,Conf))(Id). -progress(#progress{} = Progress , _File, {file_size, Total}) -> +%%%---------------------------------------------------------------- +%%% Help functions for the option '{progress,Progress}' +%%% + +%%%---------------- +%%% Callback: + +progress(#progress{} = P, _File, {file_size, Total} = M) -> + ct:pal("Progress: ~p",[M]), progress_report_receiver ! start, - Progress#progress{total = Total}; + P#progress{total = Total}; -progress(#progress{total = Total, current = Current} - = Progress, _File, {transfer_size, 0}) -> +progress(#progress{current = Current} = P, _File, {transfer_size, 0} = M) -> + ct:pal("Progress: ~p",[M]), progress_report_receiver ! finish, - case Total of - unknown -> - ok; - Current -> - ok; - _ -> - ct:fail({error, {progress, {total, Total}, - {current, Current}}}) - end, - Progress; -progress(#progress{current = Current} = Progress, _File, - {transfer_size, Size}) -> + case P#progress.total of + unknown -> P; + Current -> P; + Total -> ct:fail({error, {progress, {total,Total}, {current,Current}}}), + P + end; + +progress(#progress{current = Current} = P, _File, {transfer_size, Size} = M) -> + ct:pal("Progress: ~p",[M]), progress_report_receiver ! update, - Progress#progress{current = Current + Size}. + P#progress{current = Current + Size}; + +progress(P, _File, M) -> + ct:pal("Progress **** Strange: ~p",[M]), + P. + + +%%%---------------- +%%% Help process that counts the files transferred: -progress_report_receiver_init(Pid, N) -> +progress_report_receiver_init(Parent, N) -> register(progress_report_receiver, self()), + progress_report_receiver_expect_N_files(Parent, N). + +progress_report_receiver_expect_N_files(_Parent, 0) -> + ct:pal("progress_report got all files!", []); +progress_report_receiver_expect_N_files(Parent, N) -> + ct:pal("progress_report expects ~p more files",[N]), receive - start -> - ok + start -> ok end, - progress_report_receiver_loop(Pid, N-1). - -progress_report_receiver_loop(Pid, N) -> - receive - update -> - progress_report_receiver_loop(Pid, N); - finish when N =:= 0 -> - Pid ! {self(), ok}; - finish -> - Pid ! {self(), ok}, - receive - start -> - ok - end, - progress_report_receiver_loop(Pid, N-1) - end. - -kill_ftp_proc_6035(Pid, LogFile) -> + progress_report_receiver_loop(Parent, N-1). + + +progress_report_receiver_loop(Parent, N) -> + ct:pal("progress_report expect update | finish. N = ~p",[N]), receive - open -> - exit(Pid, shutdown), - kill_ftp_proc_6035(Pid, LogFile); - {open_failed, Reason} -> - exit({skip, {failed_openening_server_connection, Reason}}) - after - 5000 -> - is_error_report_6035(LogFile) + update -> + ct:pal("progress_report got update",[]), + progress_report_receiver_loop(Parent, N); + finish -> + ct:pal("progress_report got finish, send ~p to ~p",[{self(),ok}, Parent]), + Parent ! {self(), ok}, + progress_report_receiver_expect_N_files(Parent, N) end. -open_wait_6035({_Tag, FtpServer}, From) -> - case ftp:open(FtpServer, [{timeout, timer:seconds(15)}]) of - {ok, Pid} -> - _LoginResult = ftp:user(Pid,"anonymous","kldjf"), - From ! open, - receive - dummy -> - ok - after - 10000 -> - ok - end, - ok; - {error, Reason} -> - From ! {open_failed, {Reason, FtpServer}}, - ok - end. +%%%---------------------------------------------------------------- +%%% Help functions for bug OTP-6035 is_error_report_6035(LogFile) -> - Res = - case file:read_file(LogFile) of - {ok, Bin} -> - Txt = binary_to_list(Bin), - read_log_6035(Txt); - _ -> - false - end, - %% file:delete(LogFile), - Res. - -read_log_6035("=ERROR REPORT===="++_Rest) -> - true; -read_log_6035([_|T]) -> - read_log_6035(T); -read_log_6035([]) -> - false. + case file:read_file(LogFile) of + {ok, Bin} -> + nomatch =/= binary:match(Bin, <<"=ERROR REPORT====">>); + _ -> + false + end. + diff --git a/lib/inets/test/ftp_format_SUITE.erl b/lib/inets/test/ftp_format_SUITE.erl index 2c17e2657c..a33b31f46f 100644 --- a/lib/inets/test/ftp_format_SUITE.erl +++ b/lib/inets/test/ftp_format_SUITE.erl @@ -253,7 +253,7 @@ ftp_other_status_codes(Config) when is_list(Config) -> {perm_neg_compl, _ } = ftp_response:interpret("501 Foobar\r\n"), {perm_neg_compl, _ } = ftp_response:interpret("503 Foobar\r\n"), {perm_neg_compl, _ } = ftp_response:interpret("504 Foobar\r\n"), - {perm_neg_compl, _ } = ftp_response:interpret("530 Foobar\r\n"), + {elogin, _ } = ftp_response:interpret("530 Foobar\r\n"), {perm_neg_compl, _ } = ftp_response:interpret("532 Foobar\r\n"), {epath, _ } = ftp_response:interpret("550 Foobar\r\n"), {epnospc, _ } = ftp_response:interpret("552 Foobar\r\n"), diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl index 42772923e4..932567ec55 100644 --- a/lib/inets/test/httpc_SUITE.erl +++ b/lib/inets/test/httpc_SUITE.erl @@ -2080,7 +2080,7 @@ run_clients(NumClients, ServerPort, SeqNumServer) -> end end), MRef = erlang:monitor(process, Pid), - timer:sleep(10 + random:uniform(1334)), + timer:sleep(10 + rand:uniform(1334)), {Id, Pid, MRef} end, lists:seq(1, NumClients)). @@ -2169,7 +2169,7 @@ slowly_send_response(CSock, Answer) -> [length(Answer), Answer])), lists:foreach( fun(Char) -> - timer:sleep(random:uniform(500)), + timer:sleep(rand:uniform(500)), gen_tcp:send(CSock, <<Char>>) end, Response). @@ -2189,9 +2189,8 @@ parse_connection_type(Request) -> set_random_seed() -> Unique = erlang:unique_integer(), - A = erlang:phash2([make_ref(), self(), Unique]), - random:seed(A, A, A). + rand:seed(exsplus, {A, A, A}). otp_8739(doc) -> diff --git a/lib/kernel/doc/src/gen_tcp.xml b/lib/kernel/doc/src/gen_tcp.xml index 8bd94892ad..88135ea43d 100644 --- a/lib/kernel/doc/src/gen_tcp.xml +++ b/lib/kernel/doc/src/gen_tcp.xml @@ -153,17 +153,17 @@ do_recv(Sock, Bs) -> <c><anno>Address</anno></c> can be a hostname or an IP address.</p> <p>The following options are available:</p> <taglist> - <tag><c>{ip, ip_address()}</c></tag> + <tag><c>{ip, Address}</c></tag> <item><p>If the host has many network interfaces, this option specifies which one to use.</p></item> - <tag><c>{ifaddr, ip_address()}</c></tag> - <item><p>Same as <c>{ip, ip_address()}</c>. If the host has many + <tag><c>{ifaddr, Address}</c></tag> + <item><p>Same as <c>{ip, Address}</c>. If the host has many network interfaces, this option specifies which one to use.</p> </item> <tag><c>{fd, integer() >= 0}</c></tag> <item><p>If a socket has somehow been connected without using <c>gen_tcp</c>, use this option to pass the file descriptor - for it. If <c>{ip, ip_address()}</c> and/or + for it. If <c>{ip, Address}</c> and/or <c>{port, port_number()}</c> is combined with this option, the <c>fd</c> is bound to the specified interface and port before connecting. If these options are not specified, it is assumed that @@ -172,6 +172,15 @@ do_recv(Sock, Bs) -> <item><p>Sets up the socket for IPv4.</p></item> <tag><c>inet6</c></tag> <item><p>Sets up the socket for IPv6.</p></item> + <tag><c>local</c></tag> + <item> + <p> + Sets up a Unix Domain Socket. See + <seealso marker="inet#type-local_address"> + <c>inet:local_address()</c> + </seealso> + </p> + </item> <tag><c>{port, Port}</c></tag> <item><p>Specifies which local port number to use.</p></item> <tag><c>{tcp_module, module()}</c></tag> @@ -246,7 +255,7 @@ do_recv(Sock, Bs) -> <item><p><c>B</c> is an integer >= <c>0</c>. The backlog value defines the maximum length that the queue of pending connections can grow to. Defaults to <c>5</c>.</p></item> - <tag><c>{ip, ip_address()}</c></tag> + <tag><c>{ip, Address}</c></tag> <item><p>If the host has many network interfaces, this option specifies which one to listen on.</p></item> <tag><c>{port, Port}</c></tag> @@ -255,8 +264,8 @@ do_recv(Sock, Bs) -> <item><p>If a socket has somehow been connected without using <c>gen_tcp</c>, use this option to pass the file descriptor for it.</p></item> - <tag><c>{ifaddr, ip_address()}</c></tag> - <item><p>Same as <c>{ip, ip_address()}</c>. If the host has many + <tag><c>{ifaddr, Address}</c></tag> + <item><p>Same as <c>{ip, Address}</c>. If the host has many network interfaces, this option specifies which one to use.</p> </item> <tag><c>inet6</c></tag> diff --git a/lib/kernel/doc/src/gen_udp.xml b/lib/kernel/doc/src/gen_udp.xml index 3db291600d..3f88a0272d 100644 --- a/lib/kernel/doc/src/gen_udp.xml +++ b/lib/kernel/doc/src/gen_udp.xml @@ -85,11 +85,11 @@ <item><p>Received <c>Packet</c> is delivered as a list.</p></item> <tag><c>binary</c></tag> <item><p>Received <c>Packet</c> is delivered as a binary.</p></item> - <tag><c>{ip, ip_address()}</c></tag> + <tag><c>{ip, Address}</c></tag> <item><p>If the host has many network interfaces, this option specifies which one to use.</p></item> - <tag><c>{ifaddr, ip_address()}</c></tag> - <item><p>Same as <c>{ip, ip_address()}</c>. If the host has many + <tag><c>{ifaddr, Address}</c></tag> + <item><p>Same as <c>{ip, Address}</c>. If the host has many network interfaces, this option specifies which one to use.</p></item> <tag><c>{fd, integer() >= 0}</c></tag> @@ -104,6 +104,15 @@ <item><p>Sets up the socket for IPv6.</p></item> <tag><c>inet</c></tag> <item><p>Sets up the socket for IPv4.</p></item> + <tag><c>local</c></tag> + <item> + <p> + Sets up a Unix Domain Socket. See + <seealso marker="inet#type-local_address"> + <c>inet:local_address()</c> + </seealso> + </p> + </item> <tag><c>{udp_module, module()}</c></tag> <item><p>Overrides which callback module is used. Defaults to <c>inet_udp</c> for IPv4 and <c>inet6_udp</c> for IPv6.</p></item> @@ -176,8 +185,10 @@ <name name="send" arity="4"/> <fsummary>Send a packet.</fsummary> <desc> - <p>Sends a packet to the specified address and port. Argument - <c><anno>Address</anno></c> can be a hostname or an IP address.</p> + <p> + Sends a packet to the specified address and port. Argument + <c><anno>Address</anno></c> can be a hostname or a socket address. + </p> </desc> </func> </funcs> diff --git a/lib/kernel/doc/src/heart.xml b/lib/kernel/doc/src/heart.xml index c587e39345..864f8facac 100644 --- a/lib/kernel/doc/src/heart.xml +++ b/lib/kernel/doc/src/heart.xml @@ -83,6 +83,17 @@ <c><![CDATA[SIGKILL]]></c>:</p> <pre> % <input>erl -heart -env HEART_KILL_SIGNAL SIGABRT ...</input></pre> + <p> If heart should <b>not</b> kill the Erlang runtime system, this can be indicated + using the environment variable <c><![CDATA[HEART_NO_KILL=TRUE]]></c>. + This can be useful if the command executed by heart takes care of this, + for example as part of a specific cleanup sequence. + If unset, or not set to <c><![CDATA[TRUE]]></c>, the default behaviour + will be to kill as described above. + </p> + + <pre> +% <input>erl -heart -env HEART_NO_KILL 1 ...</input></pre> + <p>Furthermore, <c><![CDATA[ERL_CRASH_DUMP_SECONDS]]></c> has the following behavior on <c>heart</c>:</p> <taglist> diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml index 5ff167bcb3..c0dce2f50c 100644 --- a/lib/kernel/doc/src/inet.xml +++ b/lib/kernel/doc/src/inet.xml @@ -118,6 +118,59 @@ fe80::204:acff:fe17:bf38 <name name="port_number"/> </datatype> <datatype> + <name name="local_address"/> + <desc> + <p> + This address family only works on Unix-like systems. + </p> + <p> + <c><anno>File</anno></c> is normally a file pathname + in a local filesystem. It is limited in length by the + operating system, traditionally to 108 bytes. + </p> + <p> + A <c>binary()</c> is passed as is to the operating system, + but a <c>string()</c> is encoded according to the + <seealso marker="file#native_name_encoding/0"> + system filename encoding mode. + </seealso> + </p> + <p> + Other addresses are possible, for example Linux implements + "Abstract Addresses". See the documentation for + Unix Domain Sockets on your system, + normally <c>unix</c> in manual section 7. + </p> + <p> + In most API functions where you can use + this address family the port number must be <c>0</c>. + </p> + </desc> + </datatype> + <datatype> + <name name="socket_address"/> + </datatype> + <datatype> + <name name="returned_non_ip_address"/> + <desc> + <p> + Addresses besides + <seealso marker="#type-ip_address"> + <c>ip_address()</c> + </seealso> + ones that are returned from socket API functions. + See in particular + <seealso marker="#type-local_address"> + <c>local_address()</c>. + </seealso> + The <c>unspec</c> family corresponds to AF_UNSPEC and can + occur if the other side has no socket address. + The <c>undefined</c> family can only occur in the unlikely + event of an address family that the VM does not recognize. + </p> + </desc> + </datatype> + <datatype> <name name="posix"/> <desc> <p>An atom that is named from the POSIX error codes used in Unix, diff --git a/lib/kernel/src/Makefile b/lib/kernel/src/Makefile index 8bf02afec9..2b72f78dcf 100644 --- a/lib/kernel/src/Makefile +++ b/lib/kernel/src/Makefile @@ -104,6 +104,8 @@ MODULES = \ inet_sctp \ kernel \ kernel_config \ + local_udp \ + local_tcp \ net \ net_adm \ net_kernel \ @@ -244,6 +246,8 @@ $(EBIN)/inet_tcp.beam: inet_int.hrl $(EBIN)/inet_udp_dist.beam: ../include/net_address.hrl ../include/dist.hrl ../include/dist_util.hrl $(EBIN)/inet_udp.beam: inet_int.hrl $(EBIN)/inet_sctp.beam: inet_int.hrl ../include/inet_sctp.hrl +$(EBIN)/local_udp.beam: inet_int.hrl +$(EBIN)/local_tcp.beam: inet_int.hrl $(EBIN)/net_kernel.beam: ../include/net_address.hrl $(EBIN)/os.beam: ../include/file.hrl $(EBIN)/ram_file.beam: ../include/file.hrl diff --git a/lib/kernel/src/gen_sctp.erl b/lib/kernel/src/gen_sctp.erl index d00ed828a7..b133e6fed4 100644 --- a/lib/kernel/src/gen_sctp.erl +++ b/lib/kernel/src/gen_sctp.erl @@ -124,8 +124,8 @@ open() -> SockType :: seqpacket | stream, Socket :: sctp_socket(). -open(Opts) when is_list(Opts) -> - Mod = mod(Opts, undefined), +open(Opts0) when is_list(Opts0) -> + {Mod, Opts} = inet:sctp_module(Opts0), case Mod:open(Opts) of {error,badarg} -> erlang:error(badarg, [Opts]); @@ -445,32 +445,3 @@ controlling_process(S, Pid) when is_port(S), is_pid(Pid) -> inet:udp_controlling_process(S, Pid); controlling_process(S, Pid) -> erlang:error(badarg, [S,Pid]). - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% Utilites -%% - -%% Get the SCTP module, but IPv6 address overrides default IPv4 -mod(Address) -> - case inet_db:sctp_module() of - inet_sctp when tuple_size(Address) =:= 8 -> - inet6_sctp; - Mod -> - Mod - end. - -%% Get the SCTP module, but option sctp_module|inet|inet6 overrides -mod([{sctp_module,Mod}|_], _Address) -> - Mod; -mod([inet|_], _Address) -> - inet_sctp; -mod([inet6|_], _Address) -> - inet6_sctp; -mod([{ip, Address}|Opts], _) -> - mod(Opts, Address); -mod([{ifaddr, Address}|Opts], _) -> - mod(Opts, Address); -mod([_|Opts], Address) -> - mod(Opts, Address); -mod([], Address) -> - mod(Address). diff --git a/lib/kernel/src/gen_tcp.erl b/lib/kernel/src/gen_tcp.erl index 1f0d44b8e1..1a21541b7c 100644 --- a/lib/kernel/src/gen_tcp.erl +++ b/lib/kernel/src/gen_tcp.erl @@ -96,17 +96,17 @@ tos | ipv6_v6only. -type connect_option() :: - {ip, inet:ip_address()} | + {ip, inet:socket_address()} | {fd, Fd :: non_neg_integer()} | - {ifaddr, inet:ip_address()} | + {ifaddr, inet:socket_address()} | inet:address_family() | {port, inet:port_number()} | {tcp_module, module()} | option(). -type listen_option() :: - {ip, inet:ip_address()} | + {ip, inet:socket_address()} | {fd, Fd :: non_neg_integer()} | - {ifaddr, inet:ip_address()} | + {ifaddr, inet:socket_address()} | inet:address_family() | {port, inet:port_number()} | {backlog, B :: non_neg_integer()} | @@ -122,7 +122,7 @@ %% -spec connect(Address, Port, Options) -> {ok, Socket} | {error, Reason} when - Address :: inet:ip_address() | inet:hostname(), + Address :: inet:socket_address() | inet:hostname(), Port :: inet:port_number(), Options :: [connect_option()], Socket :: socket(), @@ -133,7 +133,7 @@ connect(Address, Port, Opts) -> -spec connect(Address, Port, Options, Timeout) -> {ok, Socket} | {error, Reason} when - Address :: inet:ip_address() | inet:hostname(), + Address :: inet:socket_address() | inet:hostname(), Port :: inet:port_number(), Options :: [connect_option()], Timeout :: timeout(), @@ -151,8 +151,8 @@ connect(Address, Port, Opts, Time) -> Error -> Error end. -connect1(Address,Port,Opts,Timer) -> - Mod = mod(Opts, Address), +connect1(Address, Port, Opts0, Timer) -> + {Mod, Opts} = inet:tcp_module(Opts0, Address), case Mod:getaddrs(Address,Timer) of {ok,IPs} -> case Mod:getserv(Port) of @@ -185,8 +185,8 @@ try_connect([], _Port, _Opts, _Timer, _Mod, Err) -> ListenSocket :: socket(), Reason :: system_limit | inet:posix(). -listen(Port, Opts) -> - Mod = mod(Opts, undefined), +listen(Port, Opts0) -> + {Mod, Opts} = inet:tcp_module(Opts0), case Mod:getserv(Port) of {ok,TP} -> Mod:listen(TP, Opts); @@ -335,32 +335,6 @@ controlling_process(S, NewOwner) -> %% %% Create a port/socket from a file descriptor %% -fdopen(Fd, Opts) -> - Mod = mod(Opts, undefined), +fdopen(Fd, Opts0) -> + {Mod, Opts} = inet:tcp_module(Opts0), Mod:fdopen(Fd, Opts). - -%% Get the tcp_module, but IPv6 address overrides default IPv4 -mod(Address) -> - case inet_db:tcp_module() of - inet_tcp when tuple_size(Address) =:= 8 -> - inet6_tcp; - Mod -> - Mod - end. - -%% Get the tcp_module, but option tcp_module|inet|inet6 overrides -mod([{tcp_module,Mod}|_], _Address) -> - Mod; -mod([inet|_], _Address) -> - inet_tcp; -mod([inet6|_], _Address) -> - inet6_tcp; -mod([{ip, Address}|Opts], _) -> - mod(Opts, Address); -mod([{ifaddr, Address}|Opts], _) -> - mod(Opts, Address); -mod([_|Opts], Address) -> - mod(Opts, Address); -mod([], Address) -> - mod(Address). - diff --git a/lib/kernel/src/gen_udp.erl b/lib/kernel/src/gen_udp.erl index 5e85c61dd9..98d2f0bcfb 100644 --- a/lib/kernel/src/gen_udp.erl +++ b/lib/kernel/src/gen_udp.erl @@ -92,18 +92,18 @@ open(Port) -> -spec open(Port, Opts) -> {ok, Socket} | {error, Reason} when Port :: inet:port_number(), Opts :: [Option], - Option :: {ip, inet:ip_address()} + Option :: {ip, inet:socket_address()} | {fd, non_neg_integer()} - | {ifaddr, inet:ip_address()} + | {ifaddr, inet:socket_address()} | inet:address_family() | {port, inet:port_number()} | option(), Socket :: socket(), Reason :: inet:posix(). -open(Port, Opts) -> - Mod = mod(Opts, undefined), - {ok,UP} = Mod:getserv(Port), +open(Port, Opts0) -> + {Mod, Opts} = inet:udp_module(Opts0), + {ok, UP} = Mod:getserv(Port), Mod:open(UP, Opts). -spec close(Socket) -> ok when @@ -114,7 +114,7 @@ close(S) -> -spec send(Socket, Address, Port, Packet) -> ok | {error, Reason} when Socket :: socket(), - Address :: inet:ip_address() | inet:hostname(), + Address :: inet:socket_address() | inet:hostname(), Port :: inet:port_number(), Packet :: iodata(), Reason :: not_owner | inet:posix(). @@ -148,7 +148,7 @@ send(S, Packet) when is_port(S) -> {ok, {Address, Port, Packet}} | {error, Reason} when Socket :: socket(), Length :: non_neg_integer(), - Address :: inet:ip_address(), + Address :: inet:ip_address() | inet:returned_non_ip_address(), Port :: inet:port_number(), Packet :: string() | binary(), Reason :: not_owner | inet:posix(). @@ -166,7 +166,7 @@ recv(S,Len) when is_port(S), is_integer(Len) -> Socket :: socket(), Length :: non_neg_integer(), Timeout :: timeout(), - Address :: inet:ip_address(), + Address :: inet:ip_address() | inet:returned_non_ip_address(), Port :: inet:port_number(), Packet :: string() | binary(), Reason :: not_owner | inet:posix(). @@ -203,32 +203,6 @@ controlling_process(S, NewOwner) -> %% %% Create a port/socket from a file descriptor %% -fdopen(Fd, Opts) -> - Mod = mod(Opts, undefined), +fdopen(Fd, Opts0) -> + {Mod,Opts} = inet:udp_module(Opts0), Mod:fdopen(Fd, Opts). - - -%% Get the udp_module, but IPv6 address overrides default IPv4 -mod(Address) -> - case inet_db:udp_module() of - inet_udp when tuple_size(Address) =:= 8 -> - inet6_udp; - Mod -> - Mod - end. - -%% Get the udp_module, but option udp_module|inet|inet6 overrides -mod([{udp_module,Mod}|_], _Address) -> - Mod; -mod([inet|_], _Address) -> - inet_udp; -mod([inet6|_], _Address) -> - inet6_udp; -mod([{ip, Address}|Opts], _) -> - mod(Opts, Address); -mod([{ifaddr, Address}|Opts], _) -> - mod(Opts, Address); -mod([_|Opts], Address) -> - mod(Opts, Address); -mod([], Address) -> - mod(Address). diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl index 713a9cf725..9fc685e728 100644 --- a/lib/kernel/src/inet.erl +++ b/lib/kernel/src/inet.erl @@ -37,6 +37,7 @@ parse_ipv6strict_address/1, parse_address/1, parse_strict_address/1, ntoa/1]). -export([connect_options/2, listen_options/2, udp_options/2, sctp_options/2]). +-export([udp_module/1, tcp_module/1, tcp_module/2, sctp_module/1]). -export([i/0, i/1, i/2]). @@ -72,9 +73,9 @@ -export([start_timer/1, timeout/1, timeout/2, stop_timer/1]). -export_type([address_family/0, hostent/0, hostname/0, ip4_address/0, - ip6_address/0, ip_address/0, posix/0, socket/0, - port_number/0]). - + ip6_address/0, ip_address/0, port_number/0, + local_address/0, socket_address/0, returned_non_ip_address/0, + posix/0, socket/0, stat_option/0]). %% imports -import(lists, [append/1, duplicate/2, filter/2, foldl/3]). @@ -97,6 +98,11 @@ 0..65535,0..65535,0..65535,0..65535}. -type ip_address() :: ip4_address() | ip6_address(). -type port_number() :: 0..65535. +-type local_address() :: {local, File :: binary() | string()}. +-type returned_non_ip_address() :: + {local, binary()} | + {unspec, <<>>} | + {undefined, any()}. -type posix() :: exbadport | exbadseq | file:posix(). -type socket() :: port(). @@ -133,9 +139,11 @@ 'running' | 'multicast' | 'loopback']} | {'hwaddr', ether_address()}. --type address_family() :: 'inet' | 'inet6'. +-type address_family() :: 'inet' | 'inet6' | 'local'. -type socket_protocol() :: 'tcp' | 'udp' | 'sctp'. -type socket_type() :: 'stream' | 'dgram' | 'seqpacket'. +-type socket_address() :: + ip_address() | 'any' | 'loopback' | local_address(). -type stat_option() :: 'recv_cnt' | 'recv_max' | 'recv_avg' | 'recv_oct' | 'recv_dvi' | 'send_cnt' | 'send_max' | 'send_avg' | 'send_oct' | 'send_pend'. @@ -160,26 +168,33 @@ close(Socket) -> end. --spec peername(Socket) -> {ok, {Address, Port}} | {error, posix()} when - Socket :: socket(), - Address :: ip_address(), - Port :: non_neg_integer(). +-spec peername(Socket :: socket()) -> + {ok, + {ip_address(), port_number()} | + returned_non_ip_address()} | + {error, posix()}. peername(Socket) -> prim_inet:peername(Socket). --spec setpeername(Socket :: socket(), Address :: {ip_address(), port_number()}) -> - 'ok' | {'error', any()}. +-spec setpeername( + Socket :: socket(), + Address :: + {ip_address() | 'any' | 'loopback', + port_number()} | + socket_address()) -> + 'ok' | {'error', any()}. setpeername(Socket, {IP,Port}) -> prim_inet:setpeername(Socket, {IP,Port}); setpeername(Socket, undefined) -> prim_inet:setpeername(Socket, undefined). --spec peernames(Socket) -> {ok, [{Address, Port}]} | {error, posix()} when - Socket :: socket(), - Address :: ip_address(), - Port :: non_neg_integer(). +-spec peernames(Socket :: socket()) -> + {ok, + [{ip_address(), port_number()} | + returned_non_ip_address()]} | + {error, posix()}. peernames(Socket) -> prim_inet:peernames(Socket). @@ -195,15 +210,21 @@ peernames(Socket, Assoc) -> prim_inet:peernames(Socket, Assoc). --spec sockname(Socket) -> {ok, {Address, Port}} | {error, posix()} when - Socket :: socket(), - Address :: ip_address(), - Port :: non_neg_integer(). +-spec sockname(Socket :: socket()) -> + {ok, + {ip_address(), port_number()} | + returned_non_ip_address()} | + {error, posix()}. sockname(Socket) -> prim_inet:sockname(Socket). --spec setsockname(Socket :: socket(), Address :: {ip_address(), port_number()}) -> +-spec setsockname( + Socket :: socket(), + Address :: + {ip_address() | 'any' | 'loopback', + port_number()} | + socket_address()) -> 'ok' | {'error', any()}. setsockname(Socket, {IP,Port}) -> @@ -211,10 +232,11 @@ setsockname(Socket, {IP,Port}) -> setsockname(Socket, undefined) -> prim_inet:setsockname(Socket, undefined). --spec socknames(Socket) -> {ok, [{Address, Port}]} | {error, posix()} when - Socket :: socket(), - Address :: ip_address(), - Port :: non_neg_integer(). +-spec socknames(Socket :: socket()) -> + {ok, + [{ip_address(), port_number()} | + returned_non_ip_address()]} | + {error, posix()}. socknames(Socket) -> prim_inet:socknames(Socket). @@ -681,7 +703,7 @@ connect_options() -> low_msgq_watermark, send_timeout, send_timeout_close, delay_send, raw, show_econnreset]. -connect_options(Opts, Family) -> +connect_options(Opts, Mod) -> BaseOpts = case application:get_env(kernel, inet_default_connect_options) of {ok,List} when is_list(List) -> @@ -698,7 +720,7 @@ connect_options(Opts, Family) -> {ok, R} -> {ok, R#connect_opts { opts = lists:reverse(R#connect_opts.opts), - ifaddr = translate_ip(R#connect_opts.ifaddr, Family) + ifaddr = Mod:translate_ip(R#connect_opts.ifaddr) }}; Error -> Error end. @@ -713,9 +735,6 @@ con_opt([Opt | Opts], #connect_opts{} = R, As) -> {fd,Fd} -> con_opt(Opts, R#connect_opts { fd = Fd }, As); binary -> con_add(mode, binary, R, Opts, As); list -> con_add(mode, list, R, Opts, As); - {tcp_module,_} -> con_opt(Opts, R, As); - inet -> con_opt(Opts, R, As); - inet6 -> con_opt(Opts, R, As); {netns,NS} -> BinNS = filename2binary(NS), case prim_inet:is_sockopt_val(netns, BinNS) of @@ -752,7 +771,7 @@ listen_options() -> low_msgq_watermark, send_timeout, send_timeout_close, delay_send, packet_size, raw, show_econnreset]. -listen_options(Opts, Family) -> +listen_options(Opts, Mod) -> BaseOpts = case application:get_env(kernel, inet_default_listen_options) of {ok,List} when is_list(List) -> @@ -769,7 +788,7 @@ listen_options(Opts, Family) -> {ok, R} -> {ok, R#listen_opts { opts = lists:reverse(R#listen_opts.opts), - ifaddr = translate_ip(R#listen_opts.ifaddr, Family) + ifaddr = Mod:translate_ip(R#listen_opts.ifaddr) }}; Error -> Error end. @@ -785,9 +804,6 @@ list_opt([Opt | Opts], #listen_opts{} = R, As) -> {backlog,BL} -> list_opt(Opts, R#listen_opts { backlog = BL }, As); binary -> list_add(mode, binary, R, Opts, As); list -> list_add(mode, list, R, Opts, As); - {tcp_module,_} -> list_opt(Opts, R, As); - inet -> list_opt(Opts, R, As); - inet6 -> list_opt(Opts, R, As); {netns,NS} -> BinNS = filename2binary(NS), case prim_inet:is_sockopt_val(netns, BinNS) of @@ -812,6 +828,19 @@ list_add(Name, Val, #listen_opts{} = R, Opts, As) -> Error -> Error end. +tcp_module(Opts) -> + tcp_module_1(Opts, undefined). + +tcp_module(Opts, Addr) -> + Address = {undefined,Addr}, + %% Address has to be a 2-tuple but the first element is ignored + tcp_module_1(Opts, Address). + +tcp_module_1(Opts, Address) -> + mod( + Opts, tcp_module, Address, + #{inet => inet_tcp, inet6 => inet6_tcp, local => local_tcp}). + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Available options for udp:open %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -823,12 +852,12 @@ udp_options() -> high_msgq_watermark, low_msgq_watermark]. -udp_options(Opts, Family) -> +udp_options(Opts, Mod) -> case udp_opt(Opts, #udp_opts { }, udp_options()) of {ok, R} -> {ok, R#udp_opts { opts = lists:reverse(R#udp_opts.opts), - ifaddr = translate_ip(R#udp_opts.ifaddr, Family) + ifaddr = Mod:translate_ip(R#udp_opts.ifaddr) }}; Error -> Error end. @@ -843,9 +872,6 @@ udp_opt([Opt | Opts], #udp_opts{} = R, As) -> {fd,Fd} -> udp_opt(Opts, R#udp_opts { fd = Fd }, As); binary -> udp_add(mode, binary, R, Opts, As); list -> udp_add(mode, list, R, Opts, As); - {udp_module,_} -> udp_opt(Opts, R, As); - inet -> udp_opt(Opts, R, As); - inet6 -> udp_opt(Opts, R, As); {netns,NS} -> BinNS = filename2binary(NS), case prim_inet:is_sockopt_val(netns, BinNS) of @@ -870,6 +896,11 @@ udp_add(Name, Val, #udp_opts{} = R, Opts, As) -> Error -> Error end. +udp_module(Opts) -> + mod( + Opts, udp_module, undefined, + #{inet => inet_udp, inet6 => inet6_udp, local => local_udp}). + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Available options for sctp:open %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -926,9 +957,6 @@ sctp_opt([Opt|Opts], Mod, #sctp_opts{} = R, As) -> sctp_opt(Opts, Mod, R#sctp_opts{type=Type}, As); binary -> sctp_opt (Opts, Mod, R, As, mode, binary); list -> sctp_opt (Opts, Mod, R, As, mode, list); - {sctp_module,_} -> sctp_opt (Opts, Mod, R, As); % Done with - inet -> sctp_opt (Opts, Mod, R, As); % Done with - inet6 -> sctp_opt (Opts, Mod, R, As); % Done with {netns,NS} -> BinNS = filename2binary(NS), case prim_inet:is_sockopt_val(netns, BinNS) of @@ -970,6 +998,11 @@ sctp_opt_ifaddr(Opts, Mod, #sctp_opts{ifaddr=IfAddr}=R, As, Addr) -> _ -> [IP,IfAddr] end}, As). +sctp_module(Opts) -> + mod( + Opts, sctp_module, undefined, + #{inet => inet_sctp, inet6 => inet6_sctp}). + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Util to check and insert option in option list %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -1028,6 +1061,53 @@ translate_ip(any, inet6) -> {0,0,0,0,0,0,0,0}; translate_ip(loopback, inet6) -> {0,0,0,0,0,0,0,1}; translate_ip(IP, _) -> IP. +mod(Opts, Tag, Address, Map) -> + mod(Opts, Tag, Address, Map, undefined, []). +%% +mod([{Tag, M}|Opts], Tag, Address, Map, Mod, Acc) -> + mod(Opts, Tag, Address, Map, Mod, Acc, M); +mod([{T, _} = Opt|Opts], Tag, _Address, Map, Mod, Acc) + when T =:= ip; T =:= ifaddr-> + mod(Opts, Tag, Opt, Map, Mod, [Opt|Acc]); +mod([Family|Opts], Tag, Address, Map, Mod, Acc) when is_atom(Family) -> + case Map of + #{Family := M} -> + mod(Opts, Tag, Address, Map, Mod, Acc, M); + #{} -> + mod(Opts, Tag, Address, Map, Mod, [Family|Acc]) + end; +mod([Opt|Opts], Tag, Address, Map, Mod, Acc) -> + mod(Opts, Tag, Address, Map, Mod, [Opt|Acc]); +mod([], Tag, Address, Map, undefined, Acc) -> + {case Address of + {_, {local, _}} -> + case Map of + #{local := Mod} -> + Mod; + #{} -> + inet_db:Tag() + end; + {_, IP} when tuple_size(IP) =:= 8 -> + #{inet := IPv4Mod} = Map, + %% Get the mod, but IPv6 address overrides default IPv4 + case inet_db:Tag() of + IPv4Mod -> + #{inet6 := IPv6Mod} = Map, + IPv6Mod; + Mod -> + Mod + end; + _ -> + inet_db:Tag() + end, lists:reverse(Acc)}; +mod([], _Tag, _Address, _Map, Mod, Acc) -> + {Mod, lists:reverse(Acc)}. +%% +mod(Opts, Tag, Address, Map, undefined, Acc, M) -> + mod(Opts, Tag, Address, Map, M, Acc); +mod(Opts, Tag, Address, Map, Mod, Acc, _M) -> + mod(Opts, Tag, Address, Map, Mod, Acc). + getaddrs_tm({A,B,C,D} = IP, Fam, _) -> %% Only "syntactic" validation and check of family. @@ -1235,7 +1315,17 @@ gethostbyaddr_tm_native(Addr, Timer, Opts) -> end. -spec open(Fd_or_OpenOpts :: integer() | list(), - Addr :: ip_address(), + Addr :: + socket_address() | + {ip_address() | 'any' | 'loopback', % Unofficial + port_number()} | + {inet, % Unofficial + {ip4_address() | 'any' | 'loopback', + port_number()}} | + {inet6, % Unofficial + {ip6_address() | 'any' | 'loopback', + port_number()}} | + undefined, % Internal - no bind() Port :: port_number(), Opts :: [socket_setopt()], Protocol :: socket_protocol(), @@ -1255,11 +1345,16 @@ open(FdO, Addr, Port, Opts, Protocol, Family, Type, Module) {ok,S} -> case prim_inet:setopts(S, Opts) of ok -> - case if is_list(Addr) -> - bindx(S, Addr, Port); - true -> - prim_inet:bind(S, Addr, Port) - end of + case + case Addr of + undefined -> + {ok, undefined}; + _ when is_list(Addr) -> + bindx(S, Addr, Port); + _ -> + prim_inet:bind(S, Addr, Port) + end + of {ok, _} -> inet_db:register_socket(S, Module), {ok,S}; diff --git a/lib/kernel/src/inet6_sctp.erl b/lib/kernel/src/inet6_sctp.erl index cc98bc0ccb..a5503f6f54 100644 --- a/lib/kernel/src/inet6_sctp.erl +++ b/lib/kernel/src/inet6_sctp.erl @@ -1,8 +1,8 @@ %% %% %CopyrightBegin% -%% +%% %% Copyright Ericsson AB 2007-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. %% You may obtain a copy of the License at @@ -14,13 +14,12 @@ %% 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% %% %% SCTP protocol contribution by Leonid Timochouk and Serge Aleynikov. %% See also: $ERL_TOP/lib/kernel/AUTHORS %% -%% -module(inet6_sctp). %% This module provides functions for communicating with @@ -31,6 +30,7 @@ -include("inet_sctp.hrl"). -include("inet_int.hrl"). +-define(PROTO, sctp). -define(FAMILY, inet6). -export([getserv/1,getaddr/1,getaddr/2,translate_ip/1]). -export([open/1,close/1,listen/2,peeloff/2,connect/5]). @@ -39,25 +39,19 @@ getserv(Port) when is_integer(Port) -> {ok, Port}; -getserv(Name) when is_atom(Name) -> - inet:getservbyname(Name, sctp); -getserv(_) -> - {error,einval}. - -getaddr(Address) -> - inet:getaddr(Address, ?FAMILY). -getaddr(Address, Timer) -> - inet:getaddr_tm(Address, ?FAMILY, Timer). +getserv(Name) when is_atom(Name) -> inet:getservbyname(Name, ?PROTO); +getserv(_) -> {error,einval}. -translate_ip(IP) -> - inet:translate_ip(IP, ?FAMILY). +getaddr(Address) -> inet:getaddr(Address, ?FAMILY). +getaddr(Address, Timer) -> inet:getaddr_tm(Address, ?FAMILY, Timer). +translate_ip(IP) -> inet:translate_ip(IP, ?FAMILY). open(Opts) -> case inet:sctp_options(Opts, ?MODULE) of {ok,#sctp_opts{fd=Fd,ifaddr=Addr,port=Port,type=Type,opts=SOs}} -> - inet:open(Fd, Addr, Port, SOs, sctp, ?FAMILY, Type, ?MODULE); + inet:open(Fd, Addr, Port, SOs, ?PROTO, ?FAMILY, Type, ?MODULE); Error -> Error end. diff --git a/lib/kernel/src/inet6_tcp.erl b/lib/kernel/src/inet6_tcp.erl index 3dfe5dfc86..a0d5d3df70 100644 --- a/lib/kernel/src/inet6_tcp.erl +++ b/lib/kernel/src/inet6_tcp.erl @@ -25,13 +25,18 @@ -export([controlling_process/2]). -export([fdopen/2]). --export([family/0, mask/2, parse_address/1]). +-export([family/0, mask/2, parse_address/1]). % inet_tcp_dist -export([getserv/1, getaddr/1, getaddr/2, getaddrs/1, getaddrs/2]). +-export([translate_ip/1]). -include("inet_int.hrl"). +-define(FAMILY, inet6). +-define(PROTO, tcp). +-define(TYPE, stream). + %% my address family -family() -> inet6. +family() -> ?FAMILY. %% Apply netmask on address mask({M1,M2,M3,M4,M5,M6,M7,M8}, {IP1,IP2,IP3,IP4,IP5,IP6,IP7,IP8}) -> @@ -50,15 +55,18 @@ parse_address(Host) -> %% inet_tcp port lookup getserv(Port) when is_integer(Port) -> {ok, Port}; -getserv(Name) when is_atom(Name) -> inet:getservbyname(Name,tcp). +getserv(Name) when is_atom(Name) -> inet:getservbyname(Name, ?PROTO). %% inet_tcp address lookup -getaddr(Address) -> inet:getaddr(Address, inet6). -getaddr(Address,Timer) -> inet:getaddr_tm(Address, inet6, Timer). +getaddr(Address) -> inet:getaddr(Address, ?FAMILY). +getaddr(Address, Timer) -> inet:getaddr_tm(Address, ?FAMILY, Timer). %% inet_tcp address lookup -getaddrs(Address) -> inet:getaddrs(Address, inet6). -getaddrs(Address,Timer) -> inet:getaddrs_tm(Address,inet6,Timer). +getaddrs(Address) -> inet:getaddrs(Address, ?FAMILY). +getaddrs(Address, Timer) -> inet:getaddrs_tm(Address, ?FAMILY, Timer). + +%% inet_udp special this side addresses +translate_ip(IP) -> inet:translate_ip(IP, ?FAMILY). %% %% Send data on a socket @@ -73,11 +81,6 @@ recv(Socket, Length) -> prim_inet:recv(Socket, Length). recv(Socket, Length, Timeout) -> prim_inet:recv(Socket, Length, Timeout). unrecv(Socket, Data) -> prim_inet:unrecv(Socket, Data). -%% -%% Close a socket (async) -%% -close(Socket) -> - inet:tcp_close(Socket). %% %% Shutdown one end of a socket @@ -86,6 +89,12 @@ shutdown(Socket, How) -> prim_inet:shutdown(Socket, How). %% +%% Close a socket (async) +%% +close(Socket) -> + inet:tcp_close(Socket). + +%% %% Set controlling process %% FIXME: move messages to new owner!!! %% @@ -100,24 +109,28 @@ connect(Address, Port, Opts) -> connect(Address, Port, Opts, infinity) -> do_connect(Address, Port, Opts, infinity); -connect(Address, Port, Opts, Timeout) when is_integer(Timeout), - Timeout >= 0 -> +connect(Address, Port, Opts, Timeout) + when is_integer(Timeout), Timeout >= 0 -> do_connect(Address, Port, Opts, Timeout). -do_connect(Addr = {A,B,C,D,E,F,G,H}, Port, Opts, Time) when - ?ip6(A,B,C,D,E,F,G,H), ?port(Port) -> - case inet:connect_options(Opts, inet6) of +do_connect(Addr = {A,B,C,D,E,F,G,H}, Port, Opts, Time) + when ?ip6(A,B,C,D,E,F,G,H), ?port(Port) -> + case inet:connect_options(Opts, ?MODULE) of {error, Reason} -> exit(Reason); - {ok, #connect_opts{fd=Fd, - ifaddr=BAddr={Ab,Bb,Cb,Db,Eb,Fb,Gb,Hb}, - port=BPort, - opts=SockOpts}} + {ok, + #connect_opts{ + fd = Fd, + ifaddr = BAddr = {Ab,Bb,Cb,Db,Eb,Fb,Gb,Hb}, + port = BPort, + opts = SockOpts}} when ?ip6(Ab,Bb,Cb,Db,Eb,Fb,Gb,Hb), ?port(BPort) -> - case inet:open(Fd,BAddr,BPort,SockOpts,tcp,inet6,stream,?MODULE) of + case inet:open( + Fd, BAddr, BPort, SockOpts, + ?PROTO, ?FAMILY, ?TYPE, ?MODULE) of {ok, S} -> case prim_inet:connect(S, Addr, Port, Time) of - ok -> {ok,S}; - Error -> prim_inet:close(S), Error + ok -> {ok,S}; + Error -> prim_inet:close(S), Error end; Error -> Error end; @@ -128,14 +141,18 @@ do_connect(Addr = {A,B,C,D,E,F,G,H}, Port, Opts, Time) when %% Listen %% listen(Port, Opts) -> - case inet:listen_options([{port,Port} | Opts], inet6) of + case inet:listen_options([{port,Port} | Opts], ?MODULE) of {error, Reason} -> exit(Reason); - {ok, #listen_opts{fd=Fd, - ifaddr=BAddr={A,B,C,D,E,F,G,H}, - port=BPort, - opts=SockOpts}=R} + {ok, + #listen_opts{ + fd = Fd, + ifaddr = BAddr = {A,B,C,D,E,F,G,H}, + port = BPort, + opts = SockOpts} = R} when ?ip6(A,B,C,D,E,F,G,H), ?port(BPort) -> - case inet:open(Fd,BAddr,BPort,SockOpts,tcp,inet6,stream,?MODULE) of + case inet:open( + Fd, BAddr, BPort, SockOpts, + ?PROTO, ?FAMILY, ?TYPE, ?MODULE) of {ok, S} -> case prim_inet:listen(S, R#listen_opts.backlog) of ok -> {ok, S}; @@ -156,18 +173,17 @@ accept(L) -> {ok,S}; Error -> Error end. - -accept(L,Timeout) -> - case prim_inet:accept(L,Timeout) of + +accept(L, Timeout) -> + case prim_inet:accept(L, Timeout) of {ok, S} -> inet_db:register_socket(S, ?MODULE), {ok,S}; Error -> Error end. - + %% %% Create a port/socket from a file descriptor %% fdopen(Fd, Opts) -> - inet:fdopen(Fd, Opts, tcp, inet6, stream, ?MODULE). - + inet:fdopen(Fd, Opts, ?PROTO, ?FAMILY, ?TYPE, ?MODULE). diff --git a/lib/kernel/src/inet6_udp.erl b/lib/kernel/src/inet6_udp.erl index eb6945f60a..71db0357cd 100644 --- a/lib/kernel/src/inet6_udp.erl +++ b/lib/kernel/src/inet6_udp.erl @@ -24,29 +24,44 @@ -export([controlling_process/2]). -export([fdopen/2]). --export([getserv/1, getaddr/1, getaddr/2]). +-export([getserv/1, getaddr/1, getaddr/2, translate_ip/1]). -include("inet_int.hrl"). +-define(FAMILY, inet6). +-define(PROTO, udp). +-define(TYPE, dgram). + + %% inet_udp port lookup getserv(Port) when is_integer(Port) -> {ok, Port}; -getserv(Name) when is_atom(Name) -> inet:getservbyname(Name,udp). +getserv(Name) when is_atom(Name) -> inet:getservbyname(Name, ?PROTO). %% inet_udp address lookup -getaddr(Address) -> inet:getaddr(Address, inet6). -getaddr(Address,Timer) -> inet:getaddr(Address, inet6, Timer). +getaddr(Address) -> inet:getaddr(Address, ?FAMILY). +getaddr(Address, Timer) -> inet:getaddr(Address, ?FAMILY, Timer). + +%% inet_udp special this side addresses +translate_ip(IP) -> inet:translate_ip(IP, ?FAMILY). +-spec open(_) -> {ok, inet:socket()} | {error, atom()}. open(Port) -> open(Port, []). +-spec open(_, _) -> {ok, inet:socket()} | {error, atom()}. open(Port, Opts) -> - case inet:udp_options([{port,Port} | Opts], inet6) of + case inet:udp_options( + [{port,Port} | Opts], + ?MODULE) of {error, Reason} -> exit(Reason); - {ok, #udp_opts{fd=Fd, - ifaddr=BAddr={A,B,C,D,E,F,G,H}, - port=BPort, - opts=SockOpts}} + {ok, + #udp_opts{ + fd = Fd, + ifaddr = BAddr = {A,B,C,D,E,F,G,H}, + port = BPort, + opts = SockOpts}} when ?ip6(A,B,C,D,E,F,G,H), ?port(BPort) -> - inet:open(Fd,BAddr,BPort,SockOpts,udp,inet6,dgram,?MODULE); + inet:open( + Fd, BAddr, BPort, SockOpts, ?PROTO, ?FAMILY, ?TYPE, ?MODULE); {ok, _} -> exit(badarg) end. @@ -61,12 +76,13 @@ connect(S, Addr = {A,B,C,D,E,F,G,H}, P) when ?ip6(A,B,C,D,E,F,G,H), ?port(P) -> prim_inet:connect(S, Addr, P). -recv(S,Len) -> +recv(S, Len) -> prim_inet:recvfrom(S, Len). -recv(S,Len,Time) -> +recv(S, Len, Time) -> prim_inet:recvfrom(S, Len, Time). +-spec close(inet:socket()) -> ok. close(S) -> inet:udp_close(S). @@ -85,4 +101,4 @@ controlling_process(Socket, NewOwner) -> %% Create a port/socket from a file descriptor %% fdopen(Fd, Opts) -> - inet:fdopen(Fd, Opts, udp, inet6, dgram, ?MODULE). + inet:fdopen(Fd, Opts, ?PROTO, ?FAMILY, ?TYPE, ?MODULE). diff --git a/lib/kernel/src/inet_int.hrl b/lib/kernel/src/inet_int.hrl index 65d78b66c9..c8a8962e78 100644 --- a/lib/kernel/src/inet_int.hrl +++ b/lib/kernel/src/inet_int.hrl @@ -25,10 +25,13 @@ %% %% family codes to open +-define(INET_AF_UNSPEC, 0). -define(INET_AF_INET, 1). -define(INET_AF_INET6, 2). -define(INET_AF_ANY, 3). % Fake for ANY in any address family -define(INET_AF_LOOPBACK, 4). % Fake for LOOPBACK in any address family +-define(INET_AF_LOCAL, 5). % For Unix Domain address family +-define(INET_AF_UNDEFINED, 6). % For any unknown address family %% type codes to open and gettype - INET_REQ_GETTYPE -define(INET_TYPE_STREAM, 1). @@ -378,7 +381,7 @@ { ifaddr = any, %% bind to interface address port = 0, %% bind to port (default is dynamic port) - fd = -1, %% fd >= 0 => already bound + fd = -1, %% fd >= 0 => already bound opts = [] %% [{active,true}] added in inet:connect_options }). diff --git a/lib/kernel/src/inet_sctp.erl b/lib/kernel/src/inet_sctp.erl index 60677cb7de..8569cacb29 100644 --- a/lib/kernel/src/inet_sctp.erl +++ b/lib/kernel/src/inet_sctp.erl @@ -30,6 +30,7 @@ -include("inet_sctp.hrl"). -include("inet_int.hrl"). +-define(PROTO, sctp). -define(FAMILY, inet). -export([getserv/1,getaddr/1,getaddr/2,translate_ip/1]). -export([open/1,close/1,listen/2,peeloff/2,connect/5]). @@ -38,25 +39,19 @@ getserv(Port) when is_integer(Port) -> {ok, Port}; -getserv(Name) when is_atom(Name) -> - inet:getservbyname(Name, sctp); -getserv(_) -> - {error,einval}. +getserv(Name) when is_atom(Name) -> inet:getservbyname(Name, ?PROTO); +getserv(_) -> {error,einval}. -getaddr(Address) -> - inet:getaddr(Address, ?FAMILY). -getaddr(Address, Timer) -> - inet:getaddr_tm(Address, ?FAMILY, Timer). - -translate_ip(IP) -> - inet:translate_ip(IP, ?FAMILY). +getaddr(Address) -> inet:getaddr(Address, ?FAMILY). +getaddr(Address, Timer) -> inet:getaddr_tm(Address, ?FAMILY, Timer). +translate_ip(IP) -> inet:translate_ip(IP, ?FAMILY). open(Opts) -> case inet:sctp_options(Opts, ?MODULE) of {ok,#sctp_opts{fd=Fd,ifaddr=Addr,port=Port,type=Type,opts=SOs}} -> - inet:open(Fd, Addr, Port, SOs, sctp, ?FAMILY, Type, ?MODULE); + inet:open(Fd, Addr, Port, SOs, ?PROTO, ?FAMILY, Type, ?MODULE); Error -> Error end. diff --git a/lib/kernel/src/inet_tcp.erl b/lib/kernel/src/inet_tcp.erl index ee885af3b0..dac6b3119d 100644 --- a/lib/kernel/src/inet_tcp.erl +++ b/lib/kernel/src/inet_tcp.erl @@ -27,13 +27,18 @@ -export([controlling_process/2]). -export([fdopen/2]). --export([family/0, mask/2, parse_address/1]). +-export([family/0, mask/2, parse_address/1]). % inet_tcp_dist -export([getserv/1, getaddr/1, getaddr/2, getaddrs/1, getaddrs/2]). +-export([translate_ip/1]). -include("inet_int.hrl"). +-define(FAMILY, inet). +-define(PROTO, tcp). +-define(TYPE, stream). + %% my address family -family() -> inet. +family() -> ?FAMILY. %% Apply netmask on address mask({M1,M2,M3,M4}, {IP1,IP2,IP3,IP4}) -> @@ -48,16 +53,19 @@ parse_address(Host) -> %% inet_tcp port lookup getserv(Port) when is_integer(Port) -> {ok, Port}; -getserv(Name) when is_atom(Name) -> inet:getservbyname(Name,tcp). +getserv(Name) when is_atom(Name) -> inet:getservbyname(Name, ?PROTO). %% inet_tcp address lookup -getaddr(Address) -> inet:getaddr(Address, inet). -getaddr(Address,Timer) -> inet:getaddr_tm(Address, inet, Timer). +getaddr(Address) -> inet:getaddr(Address, ?FAMILY). +getaddr(Address, Timer) -> inet:getaddr_tm(Address, ?FAMILY, Timer). %% inet_tcp address lookup -getaddrs(Address) -> inet:getaddrs(Address, inet). -getaddrs(Address,Timer) -> inet:getaddrs_tm(Address,inet,Timer). - +getaddrs(Address) -> inet:getaddrs(Address, ?FAMILY). +getaddrs(Address, Timer) -> inet:getaddrs_tm(Address, ?FAMILY, Timer). + +%% inet_udp special this side addresses +translate_ip(IP) -> inet:translate_ip(IP, ?FAMILY). + %% %% Send data on a socket %% @@ -77,7 +85,7 @@ unrecv(Socket, Data) -> prim_inet:unrecv(Socket, Data). %% shutdown(Socket, How) -> prim_inet:shutdown(Socket, How). - + %% %% Close a socket (async) %% @@ -88,7 +96,7 @@ close(Socket) -> %% Set controlling process %% controlling_process(Socket, NewOwner) -> - inet:tcp_controlling_process(Socket, NewOwner). + inet:tcp_controlling_process(Socket, NewOwner). %% %% Connect @@ -98,23 +106,28 @@ connect(Address, Port, Opts) -> connect(Address, Port, Opts, infinity) -> do_connect(Address, Port, Opts, infinity); -connect(Address, Port, Opts, Timeout) when is_integer(Timeout), - Timeout >= 0 -> +connect(Address, Port, Opts, Timeout) + when is_integer(Timeout), Timeout >= 0 -> do_connect(Address, Port, Opts, Timeout). -do_connect({A,B,C,D}, Port, Opts, Time) when ?ip(A,B,C,D), ?port(Port) -> - case inet:connect_options(Opts, inet) of +do_connect(Addr = {A,B,C,D}, Port, Opts, Time) + when ?ip(A,B,C,D), ?port(Port) -> + case inet:connect_options(Opts, ?MODULE) of {error, Reason} -> exit(Reason); - {ok, #connect_opts{fd=Fd, - ifaddr=BAddr={Ab,Bb,Cb,Db}, - port=BPort, - opts=SockOpts}} + {ok, + #connect_opts{ + fd = Fd, + ifaddr = BAddr = {Ab,Bb,Cb,Db}, + port = BPort, + opts = SockOpts}} when ?ip(Ab,Bb,Cb,Db), ?port(BPort) -> - case inet:open(Fd,BAddr,BPort,SockOpts,tcp,inet,stream,?MODULE) of + case inet:open( + Fd, BAddr, BPort, SockOpts, + ?PROTO, ?FAMILY, ?TYPE, ?MODULE) of {ok, S} -> - case prim_inet:connect(S, {A,B,C,D}, Port, Time) of - ok -> {ok,S}; - Error -> prim_inet:close(S), Error + case prim_inet:connect(S, Addr, Port, Time) of + ok -> {ok,S}; + Error -> prim_inet:close(S), Error end; Error -> Error end; @@ -125,14 +138,18 @@ do_connect({A,B,C,D}, Port, Opts, Time) when ?ip(A,B,C,D), ?port(Port) -> %% Listen %% listen(Port, Opts) -> - case inet:listen_options([{port,Port} | Opts], inet) of - {error,Reason} -> exit(Reason); - {ok, #listen_opts{fd=Fd, - ifaddr=BAddr={A,B,C,D}, - port=BPort, - opts=SockOpts}=R} + case inet:listen_options([{port,Port} | Opts], ?MODULE) of + {error, Reason} -> exit(Reason); + {ok, + #listen_opts{ + fd = Fd, + ifaddr = BAddr = {A,B,C,D}, + port = BPort, + opts = SockOpts} = R} when ?ip(A,B,C,D), ?port(BPort) -> - case inet:open(Fd,BAddr,BPort,SockOpts,tcp,inet,stream,?MODULE) of + case inet:open( + Fd, BAddr, BPort, SockOpts, + ?PROTO, ?FAMILY, ?TYPE, ?MODULE) of {ok, S} -> case prim_inet:listen(S, R#listen_opts.backlog) of ok -> {ok, S}; @@ -146,23 +163,24 @@ listen(Port, Opts) -> %% %% Accept %% -accept(L) -> +accept(L) -> case prim_inet:accept(L) of {ok, S} -> inet_db:register_socket(S, ?MODULE), {ok,S}; Error -> Error end. - -accept(L,Timeout) -> - case prim_inet:accept(L,Timeout) of + +accept(L, Timeout) -> + case prim_inet:accept(L, Timeout) of {ok, S} -> inet_db:register_socket(S, ?MODULE), {ok,S}; Error -> Error end. + %% %% Create a port/socket from a file descriptor %% fdopen(Fd, Opts) -> - inet:fdopen(Fd, Opts, tcp, inet, stream, ?MODULE). + inet:fdopen(Fd, Opts, ?PROTO, ?FAMILY, ?TYPE, ?MODULE). diff --git a/lib/kernel/src/inet_udp.erl b/lib/kernel/src/inet_udp.erl index 7e8d9cdb72..8a8aa8ecca 100644 --- a/lib/kernel/src/inet_udp.erl +++ b/lib/kernel/src/inet_udp.erl @@ -24,21 +24,26 @@ -export([controlling_process/2]). -export([fdopen/2]). --export([getserv/1, getaddr/1, getaddr/2]). +-export([getserv/1, getaddr/1, getaddr/2, translate_ip/1]). -include("inet_int.hrl"). +-define(FAMILY, inet). +-define(PROTO, udp). +-define(TYPE, dgram). -define(RECBUF, (8*1024)). - %% inet_udp port lookup getserv(Port) when is_integer(Port) -> {ok, Port}; -getserv(Name) when is_atom(Name) -> inet:getservbyname(Name,udp). +getserv(Name) when is_atom(Name) -> inet:getservbyname(Name, ?PROTO). %% inet_udp address lookup -getaddr(Address) -> inet:getaddr(Address, inet). -getaddr(Address,Timer) -> inet:getaddr_tm(Address, inet, Timer). +getaddr(Address) -> inet:getaddr(Address, ?FAMILY). +getaddr(Address, Timer) -> inet:getaddr(Address, ?FAMILY, Timer). + +%% inet_udp special this side addresses +translate_ip(IP) -> inet:translate_ip(IP, ?FAMILY). -spec open(_) -> {ok, inet:socket()} | {error, atom()}. open(Port) -> open(Port, []). @@ -47,33 +52,38 @@ open(Port) -> open(Port, []). open(Port, Opts) -> case inet:udp_options( [{port,Port}, {recbuf, ?RECBUF} | Opts], - inet) of + ?MODULE) of {error, Reason} -> exit(Reason); - {ok, #udp_opts{fd=Fd, - ifaddr=BAddr={A,B,C,D}, - port=BPort, - opts=SockOpts}} when ?ip(A,B,C,D), ?port(BPort) -> - inet:open(Fd,BAddr,BPort,SockOpts,udp,inet,dgram,?MODULE); + {ok, + #udp_opts{ + fd = Fd, + ifaddr = BAddr = {A,B,C,D}, + port = BPort, + opts = SockOpts}} + when ?ip(A,B,C,D), ?port(BPort) -> + inet:open( + Fd, BAddr, BPort, SockOpts, ?PROTO, ?FAMILY, ?TYPE, ?MODULE); {ok, _} -> exit(badarg) end. -send(S,{A,B,C,D},P,Data) when ?ip(A,B,C,D), ?port(P) -> - prim_inet:sendto(S, {A,B,C,D}, P, Data). +send(S, {A,B,C,D} = Addr, P, Data) + when ?ip(A,B,C,D), ?port(P) -> + prim_inet:sendto(S, Addr, P, Data). send(S, Data) -> prim_inet:sendto(S, {0,0,0,0}, 0, Data). -connect(S, {A,B,C,D}, P) when ?ip(A,B,C,D), ?port(P) -> - prim_inet:connect(S, {A,B,C,D}, P). +connect(S, Addr = {A,B,C,D}, P) + when ?ip(A,B,C,D), ?port(P) -> + prim_inet:connect(S, Addr, P). -recv(S,Len) -> +recv(S, Len) -> prim_inet:recvfrom(S, Len). -recv(S,Len,Time) -> +recv(S, Len, Time) -> prim_inet:recvfrom(S, Len, Time). -spec close(inet:socket()) -> ok. - close(S) -> inet:udp_close(S). @@ -92,9 +102,9 @@ controlling_process(Socket, NewOwner) -> %% Create a port/socket from a file descriptor %% fdopen(Fd, Opts) -> - inet:fdopen(Fd, - optuniquify([{recbuf, ?RECBUF} | Opts]), - udp, inet, dgram, ?MODULE). + inet:fdopen( + Fd, optuniquify([{recbuf, ?RECBUF} | Opts]), + ?PROTO, ?FAMILY, ?TYPE, ?MODULE). %% Remove all duplicate options from an option list. diff --git a/lib/kernel/src/kernel.app.src b/lib/kernel/src/kernel.app.src index 1acd6803b2..56d1699656 100644 --- a/lib/kernel/src/kernel.app.src +++ b/lib/kernel/src/kernel.app.src @@ -55,6 +55,8 @@ inet_tcp_dist, kernel, kernel_config, + local_tcp, + local_udp, net, net_adm, net_kernel, diff --git a/lib/kernel/src/local_tcp.erl b/lib/kernel/src/local_tcp.erl new file mode 100644 index 0000000000..e3c67dfbb7 --- /dev/null +++ b/lib/kernel/src/local_tcp.erl @@ -0,0 +1,178 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1997-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. +%% 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(local_tcp). + +%% Socket server for TCP/IP + +-export([connect/3, connect/4, listen/2, accept/1, accept/2, close/1]). +-export([send/2, send/3, recv/2, recv/3, unrecv/2]). +-export([shutdown/2]). +-export([controlling_process/2]). +-export([fdopen/2]). + +-export([getserv/1, getaddr/1, getaddr/2, getaddrs/1, getaddrs/2]). +-export([translate_ip/1]). + +-include("inet_int.hrl"). + +-define(FAMILY, local). +-define(PROTO, tcp). +-define(TYPE, stream). + +%% port lookup +getserv(0) -> {ok, 0}. + +%% no address lookup +getaddr({?FAMILY, _} = Address) -> {ok, Address}. +getaddr({?FAMILY, _} = Address, _Timer) -> {ok, Address}. + +%% no address lookup +getaddrs({?FAMILY, _} = Address) -> {ok, [Address]}. +getaddrs({?FAMILY, _} = Address, _Timer) -> {ok, [Address]}. + +%% special this side addresses +translate_ip(IP) -> IP. + +%% +%% Send data on a socket +%% +send(Socket, Packet, Opts) -> prim_inet:send(Socket, Packet, Opts). +send(Socket, Packet) -> prim_inet:send(Socket, Packet, []). + +%% +%% Receive data from a socket (inactive only) +%% +recv(Socket, Length) -> prim_inet:recv(Socket, Length). +recv(Socket, Length, Timeout) -> prim_inet:recv(Socket, Length, Timeout). + +unrecv(Socket, Data) -> prim_inet:unrecv(Socket, Data). + +%% +%% Shutdown one end of a socket +%% +shutdown(Socket, How) -> + prim_inet:shutdown(Socket, How). + +%% +%% Close a socket (async) +%% +close(Socket) -> + inet:tcp_close(Socket). + +%% +%% Set controlling process +%% FIXME: move messages to new owner!!! +%% +controlling_process(Socket, NewOwner) -> + inet:tcp_controlling_process(Socket, NewOwner). + +%% +%% Connect +%% +connect(Address, Port, Opts) -> + do_connect(Address, Port, Opts, infinity). +%% +connect(Address, Port, Opts, infinity) -> + do_connect(Address, Port, Opts, infinity); +connect(Address, Port, Opts, Timeout) + when is_integer(Timeout), Timeout >= 0 -> + do_connect(Address, Port, Opts, Timeout). + +do_connect(Addr = {?FAMILY, _}, 0, Opts, Time) -> + case inet:connect_options(Opts, ?MODULE) of + {error, Reason} -> exit(Reason); + {ok, + #connect_opts{ + fd = Fd, + ifaddr = BAddr, + port = 0, + opts = SockOpts}} + when tuple_size(BAddr) =:= 2, element(1, BAddr) =:= ?FAMILY; + BAddr =:= any -> + case inet:open( + Fd, + case BAddr of + any -> + undefined; + _ -> + BAddr + end, + 0, SockOpts, ?PROTO, ?FAMILY, ?TYPE, ?MODULE) of + {ok, S} -> + case prim_inet:connect(S, Addr, 0, Time) of + ok -> {ok,S}; + Error -> prim_inet:close(S), Error + end; + Error -> Error + end; + {ok, _} -> exit(badarg) + end. + +%% +%% Listen +%% +listen(0, Opts) -> + case inet:listen_options([{port,0} | Opts], ?MODULE) of + {error, Reason} -> exit(Reason); + {ok, + #listen_opts{ + fd = Fd, + ifaddr = BAddr, + port = 0, + opts = SockOpts} = R} + when tuple_size(BAddr) =:= 2, element(1, BAddr) =:= ?FAMILY; + BAddr =:= any -> + case inet:open( + Fd, BAddr, 0, SockOpts, + ?PROTO, ?FAMILY, ?TYPE, ?MODULE) of + {ok, S} -> + case prim_inet:listen(S, R#listen_opts.backlog) of + ok -> {ok, S}; + Error -> prim_inet:close(S), Error + end; + Error -> Error + end; + {ok, _} -> exit(badarg) + end. + +%% +%% Accept +%% +accept(L) -> + case prim_inet:accept(L) of + {ok, S} -> + inet_db:register_socket(S, ?MODULE), + {ok,S}; + Error -> Error + end. +%% +accept(L, Timeout) -> + case prim_inet:accept(L, Timeout) of + {ok, S} -> + inet_db:register_socket(S, ?MODULE), + {ok,S}; + Error -> Error + end. + +%% +%% Create a port/socket from a file descriptor +%% +fdopen(Fd, Opts) -> + inet:fdopen(Fd, Opts, ?PROTO, ?FAMILY, ?TYPE, ?MODULE). diff --git a/lib/kernel/src/local_udp.erl b/lib/kernel/src/local_udp.erl new file mode 100644 index 0000000000..481a8c4910 --- /dev/null +++ b/lib/kernel/src/local_udp.erl @@ -0,0 +1,106 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1997-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. +%% 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(local_udp). + +-export([open/1, open/2, close/1]). +-export([send/2, send/4, recv/2, recv/3, connect/3]). +-export([controlling_process/2]). +-export([fdopen/2]). + +-export([getserv/1, getaddr/1, getaddr/2, translate_ip/1]). + +-include("inet_int.hrl"). + +-define(FAMILY, local). +-define(PROTO, udp). +-define(TYPE, dgram). + + +%% port lookup +getserv(0) -> {ok, 0}. + +%% no address lookup +getaddr({?FAMILY, _} = Address) -> {ok, Address}. +getaddr({?FAMILY, _} = Address, _Timer) -> {ok, Address}. + +%% special this side addresses +translate_ip(IP) -> IP. + +open(0) -> open(0, []). +%% +open(0, Opts) -> + case inet:udp_options( + [{port,0} | Opts], + ?MODULE) of + {error, Reason} -> exit(Reason); + {ok, + #udp_opts{ + fd = Fd, + ifaddr = BAddr, + port = 0, + opts = SockOpts}} + when tuple_size(BAddr) =:= 2, element(1, BAddr) =:= ?FAMILY; + BAddr =:= any -> + inet:open( + Fd, + case BAddr of + any -> + undefined; + _ -> + BAddr + end, + 0, SockOpts, ?PROTO, ?FAMILY, ?TYPE, ?MODULE); + {ok, _} -> exit(badarg) + end. + +send(S, Addr = {?FAMILY,_}, 0, Data) -> + prim_inet:sendto(S, Addr, 0, Data). +%% +send(S, Data) -> + prim_inet:sendto(S, {?FAMILY,<<>>}, 0, Data). + +connect(S, Addr = {?FAMILY,_}, 0) -> + prim_inet:connect(S, Addr, 0). + +recv(S, Len) -> + prim_inet:recvfrom(S, Len). +%% +recv(S, Len, Time) -> + prim_inet:recvfrom(S, Len, Time). + +close(S) -> + inet:udp_close(S). + +%% +%% Set controlling process: +%% 1) First sync socket into a known state +%% 2) Move all messages onto the new owners message queue +%% 3) Commit the owner +%% 4) Wait for ack of new Owner (since socket does some link and unlink) +%% + +controlling_process(Socket, NewOwner) -> + inet:udp_controlling_process(Socket, NewOwner). + +%% +%% Create a port/socket from a file descriptor +%% +fdopen(Fd, Opts) -> + inet:fdopen(Fd, Opts, ?PROTO, ?FAMILY, ?TYPE, ?MODULE). diff --git a/lib/kernel/test/heart_SUITE.erl b/lib/kernel/test/heart_SUITE.erl index 548b27db97..e63ed34973 100644 --- a/lib/kernel/test/heart_SUITE.erl +++ b/lib/kernel/test/heart_SUITE.erl @@ -29,11 +29,11 @@ set_cmd/1, clear_cmd/1, get_cmd/1, callback_api/1, options_api/1, - dont_drop/1, kill_pid/1]). + dont_drop/1, kill_pid/1, heart_no_kill/1]). -export([init_per_testcase/2, end_per_testcase/2]). --export([start_heart_stress/1, mangle/1, suicide_by_heart/0]). +-export([start_heart_stress/1, mangle/1, suicide_by_heart/0, non_suicide_by_heart/0]). -define(DEFAULT_TIMEOUT_SECS, 120). @@ -491,6 +491,30 @@ do_kill_pid(_Config) -> false end. + +heart_no_kill(suite) -> + []; +heart_no_kill(doc) -> + ["Tests that heart doesn't kill the old erlang node when ", + "HEART_NO_KILL is set."]; +heart_no_kill(Config) when is_list(Config) -> + ok = do_no_kill(Config). + +do_no_kill(_Config) -> + Name = heart_test, + {ok,Node} = start_node_run(Name,[],non_suicide_by_heart,[]), + io:format("Node is ~p~n", [Node]), + ok = wait_for_node(Node,15), + io:format("wait_for_node is ~p~n", [ok]), + erlang:monitor_node(Node, true), + receive {nodedown,Node} -> false + after 30000 -> + io:format("Node didn't die..\n"), + rpc:call(Node,init,stop,[]), + io:format("done init:stop..\n"), + ok + end. + wait_for_node(_,0) -> false; wait_for_node(Node,N) -> @@ -609,6 +633,18 @@ suicide_by_heart() -> sallad end. +non_suicide_by_heart() -> + P = open_port({spawn,"heart -ht 11 -pid "++os:getpid()},[exit_status, {env, {"HEART_NO_KILL", "TRUE"}}, {packet,2}]), + receive X -> X end, + %% Just hang and wait for heart to timeout + receive + {P,{exit_status,_}} -> + ok + after + 20000 -> + exit(timeout) + end. + %% generate a module from binary generate(Module, Attributes, FunStrings) -> diff --git a/lib/kernel/test/inet_SUITE.erl b/lib/kernel/test/inet_SUITE.erl index c93b10fa1c..f60c13d2e3 100644 --- a/lib/kernel/test/inet_SUITE.erl +++ b/lib/kernel/test/inet_SUITE.erl @@ -139,11 +139,13 @@ t_gethostbyaddr(Config) when is_list(Config) -> ok; _ -> io:format("alias list: ~p", [HEnt#hostent.h_aliases]), - io:format("check alias list: ~p", [[Aliases,[Rname]]]), + io:format( + "check alias list: ~p", [[Aliases,tl(Aliases),[Rname]]]), io:format("name: ~p", [HEnt#hostent.h_name]), io:format("check name: ~p", [[Name,FullName]]), - check_elems([{HEnt#hostent.h_name,[Name,FullName]}, - {HEnt#hostent.h_aliases,[[],Aliases,[Rname]]}]) + check_elems( + [{HEnt#hostent.h_name,[Name,FullName]}, + {HEnt#hostent.h_aliases,[[],Aliases,tl(Aliases),[Rname]]}]) end, {_DName, _DFullName, DIPStr, DIP, _, _, _} = ct:get_config(test_dummy_host), @@ -171,8 +173,9 @@ t_gethostbyaddr_v6(Config) when is_list(Config) -> h_length = 16, h_addr_list = [IP6]}, HEnt6_ = HEnt6, - check_elems([{HEnt6#hostent.h_name,[Name6,FullName6]}, - {HEnt6#hostent.h_aliases,[[],Aliases6]}]), + check_elems( + [{HEnt6#hostent.h_name,[Name6,FullName6]}, + {HEnt6#hostent.h_aliases,[[],Aliases6,tl(Aliases6)]}]), {_DName6, _DFullName6, DIPStr6, DIP6, _} = ct:get_config(test_dummy_ipv6_host), @@ -195,14 +198,14 @@ t_gethostbyname(Config) when is_list(Config) -> HEnt_ = HEnt, check_elems([{HEnt#hostent.h_name,[Name,FullName]}, - {HEnt#hostent.h_aliases,[[],Aliases]}]), + {HEnt#hostent.h_aliases,[[],Aliases,tl(Aliases)]}]), {ok,HEntF} = inet:gethostbyname(FullName), HEntF_ = HEntF#hostent{h_name = FullName, h_addrtype = inet, h_length = 4, h_addr_list = [IP]}, HEntF_ = HEntF, - check_elems([{HEnt#hostent.h_aliases,[[],Aliases]}]), + check_elems([{HEnt#hostent.h_aliases,[[],Aliases,tl(Aliases)]}]), %% FullNameU = toupper(FullName), {ok,HEntU} = inet:gethostbyname(FullNameU), @@ -237,7 +240,7 @@ t_gethostbyname_v6(Config) when is_list(Config) -> h_length = 16} = HEnt, check_elems( [{HEnt#hostent.h_name,[Name,FullName]}, - {HEnt#hostent.h_aliases,[[],Aliases]}]); + {HEnt#hostent.h_aliases,[[],Aliases,tl(Aliases)]}]); [IP46] -> % IPv4 compatible address {ok,HEnt4} = inet:gethostbyname(Name, inet), #hostent{h_addrtype = inet, @@ -257,7 +260,7 @@ t_gethostbyname_v6(Config) when is_list(Config) -> h_addrtype = inet6, h_length = 16} = HEntF, check_elems( - [{HEnt#hostent.h_aliases,[[],Aliases]}]); + [{HEnt#hostent.h_aliases,[[],Aliases,tl(Aliases)]}]); [IP46F] -> % IPv4 compatible address {ok,HEnt4F} = inet:gethostbyname(FullName, inet), #hostent{h_addrtype = inet, @@ -363,7 +366,7 @@ ipv4_to_ipv6(Config) when is_list(Config) -> h_addr_list = [IP_46]}, HEnt_ = HEnt, check_elems([{HEnt#hostent.h_name,[IP_46_Str,IPStr]}, - {HEnt#hostent.h_aliases,[[],Aliases]}]); + {HEnt#hostent.h_aliases,[[],Aliases,tl(Aliases)]}]); {_,IP4to6Res} -> ok end, ok. @@ -1002,12 +1005,12 @@ getifaddrs(Config) when is_list (Config) -> [check_addr(Addr) || Addr <- Addrs], ok. -check_addr(Addr) +check_addr({addr,Addr}) when tuple_size(Addr) =:= 8, element(1, Addr) band 16#FFC0 =:= 16#FE80 -> io:format("Addr: ~p link local; SKIPPED!~n", [Addr]), ok; -check_addr(Addr) -> +check_addr({addr,Addr}) -> io:format("Addr: ~p.~n", [Addr]), Ping = "ping", Pong = "pong", @@ -1021,74 +1024,82 @@ check_addr(Addr) -> ok = gen_tcp:close(S1), {ok,Pong} = gen_tcp:recv(S2, length(Pong)), ok = gen_tcp:close(S2), - ok = gen_tcp:close(L), - ok. + ok = gen_tcp:close(L). -record(ifopts, {name,flags,addrs=[],hwaddr}). ifaddrs([]) -> []; ifaddrs([{If,Opts}|IOs]) -> - #ifopts{flags=Flags} = Ifopts = - check_ifopts(Opts, #ifopts{name=If}), - case Flags =/= undefined andalso lists:member(up, Flags) of - true -> - Ifopts#ifopts.addrs; - false -> - [] - end++ifaddrs(IOs). + #ifopts{flags=F} = Ifopts = check_ifopts(Opts, #ifopts{name=If}), + case F of + {flags,Flags} -> + case lists:member(up, Flags) of + true -> + Ifopts#ifopts.addrs; + false -> + [] + end ++ ifaddrs(IOs); + undefined -> + ifaddrs(IOs) + end. -check_ifopts([], #ifopts{name=If,flags=Flags,addrs=Raddrs}=Ifopts) -> +check_ifopts([], #ifopts{flags=F,addrs=Raddrs}=Ifopts) -> Addrs = lists:reverse(Raddrs), R = Ifopts#ifopts{addrs=Addrs}, io:format("~p.~n", [R]), %% See how we did... - if is_list(Flags) -> ok; - true -> - ct:fail({flags_undefined,If}) - end, + {flags,Flags} = F, case lists:member(broadcast, Flags) of true -> [case A of - {_,_,_} -> A; - {T,_} when tuple_size(T) =:= 8 -> A; - _ -> - ct:fail({broaddr_missing,If,A}) + {{addr,_},{netmask,_},{broadaddr,_}} -> + A; + {{addr,T},{netmask,_}} when tuple_size(T) =:= 8 -> + A end || A <- Addrs]; false -> - [case A of {_,_} -> A; - _ -> - ct:fail({should_have_netmask,If,A}) - end || A <- Addrs] + case lists:member(pointtopoint, Flags) of + true -> + [case A of + {{addr,_},{netmask,_},{dstaddr,_}} -> + A + end || A <- Addrs]; + false -> + [case A of + {{addr,_},{netmask,_}} -> + A + end || A <- Addrs] + end end, R; -check_ifopts([{flags,Flags}|Opts], #ifopts{flags=undefined}=Ifopts) -> - check_ifopts(Opts, Ifopts#ifopts{flags=Flags}); -check_ifopts([{flags,Fs}|Opts], #ifopts{flags=Flags}=Ifopts) -> - case Fs of +check_ifopts([{flags,_}=F|Opts], #ifopts{flags=undefined}=Ifopts) -> + check_ifopts(Opts, Ifopts#ifopts{flags=F}); +check_ifopts([{flags,_}=F|Opts], #ifopts{flags=Flags}=Ifopts) -> + case F of Flags -> - check_ifopts(Opts, Ifopts#ifopts{}); + check_ifopts(Opts, Ifopts); _ -> - ct:fail({multiple_flags,Fs,Ifopts}) + ct:fail({multiple_flags,F,Ifopts}) end; check_ifopts( - [{addr,Addr},{netmask,Netmask},{broadaddr,Broadaddr}|Opts], + [{addr,_}=A,{netmask,_}=N,{dstaddr,_}=D|Opts], #ifopts{addrs=Addrs}=Ifopts) -> - check_ifopts(Opts, Ifopts#ifopts{addrs=[{Addr,Netmask,Broadaddr}|Addrs]}); + check_ifopts(Opts, Ifopts#ifopts{addrs=[{A,N,D}|Addrs]}); check_ifopts( - [{addr,Addr},{netmask,Netmask},{dstaddr,_}|Opts], + [{addr,_}=A,{netmask,_}=N,{broadaddr,_}=B|Opts], #ifopts{addrs=Addrs}=Ifopts) -> - check_ifopts(Opts, Ifopts#ifopts{addrs=[{Addr,Netmask}|Addrs]}); + check_ifopts(Opts, Ifopts#ifopts{addrs=[{A,N,B}|Addrs]}); check_ifopts( - [{addr,Addr},{netmask,Netmask}|Opts], + [{addr,_}=A,{netmask,_}=N|Opts], #ifopts{addrs=Addrs}=Ifopts) -> - check_ifopts(Opts, Ifopts#ifopts{addrs=[{Addr,Netmask}|Addrs]}); -check_ifopts([{addr,Addr}|Opts], #ifopts{addrs=Addrs}=Ifopts) -> - check_ifopts(Opts, Ifopts#ifopts{addrs=[{Addr}|Addrs]}); -check_ifopts([{hwaddr,Hwaddr}|Opts], #ifopts{hwaddr=undefined}=Ifopts) + check_ifopts(Opts, Ifopts#ifopts{addrs=[{A,N}|Addrs]}); +check_ifopts([{addr,_}=A|Opts], #ifopts{addrs=Addrs}=Ifopts) -> + check_ifopts(Opts, Ifopts#ifopts{addrs=[{A}|Addrs]}); +check_ifopts([{hwaddr,Hwaddr}=H|Opts], #ifopts{hwaddr=undefined}=Ifopts) when is_list(Hwaddr) -> - check_ifopts(Opts, Ifopts#ifopts{hwaddr=Hwaddr}); -check_ifopts([{hwaddr,HwAddr}|_], #ifopts{}=Ifopts) -> - ct:fail({multiple_hwaddrs,HwAddr,Ifopts}). + check_ifopts(Opts, Ifopts#ifopts{hwaddr=H}); +check_ifopts([{hwaddr,_}=H|_], #ifopts{}=Ifopts) -> + ct:fail({multiple_hwaddrs,H,Ifopts}). %% Works just like lists:member/2, except that any {127,_,_,_} tuple %% matches any other {127,_,_,_}. We do this to handle Linux systems diff --git a/lib/kernel/test/inet_res_SUITE.erl b/lib/kernel/test/inet_res_SUITE.erl index 9662d1fef5..6691ad9c06 100644 --- a/lib/kernel/test/inet_res_SUITE.erl +++ b/lib/kernel/test/inet_res_SUITE.erl @@ -42,6 +42,21 @@ -define(RUN_NAMED, "run-named"). +%% This test suite use a script ?RUN_NAMED that tries to start +%% a temporary local nameserver BIND 8 or 9 that must be installed +%% on your machine. +%% +%% For example, on Ubuntu 14.04, as root: +%% apt-get install bind9 +%% Now, that is not enough since Apparmor will not allow +%% the nameserver daemon /usr/sbin/named to read from the test directory. +%% Assuming that you run tests in /ldisk/daily_build, and still on +%% Ubuntu 14.04, make /usr/apparmor.d/local/usr.sbin.named contain: +%% /ldisk/daily_build/** r, +%% And yes; the trailing comma must be there... + + + suite() -> [{ct_hooks,[ts_install_cth]}, {timetrap,{minutes,1}}]. diff --git a/lib/megaco/src/app/megaco.appup.src b/lib/megaco/src/app/megaco.appup.src index 93bf1d980c..46da79cfe3 100644 --- a/lib/megaco/src/app/megaco.appup.src +++ b/lib/megaco/src/app/megaco.appup.src @@ -192,34 +192,11 @@ {"%VSN%", [ - {"3.17.3", [{restart_application,megaco}]}, - {"3.17.2", []}, - {"3.17.1", [{restart_application,megaco}]}, - {"3.17.0.3", [{restart_application,megaco}]}, - {"3.17.0.2", []}, - {"3.17.0.1", []}, - {"3.17", []}, - {"3.16.0.3", - [ - {update, megaco_flex_scanner_handler, {advanced, upgrade_from_pre_3_17}, - soft_purge, soft_purge, []} - ] - } + {<<"3\\..*">>, [{restart_application, megaco}]} ], + [ - {"3.17.3", [{restart_application,megaco}]}, - {"3.17.2", []}, - {"3.17.1", [{restart_application,megaco}]}, - {"3.17.0.3", [{restart_application,megaco}]}, - {"3.17.0.2", []}, - {"3.17.0.1", []}, - {"3.17", []}, - {"3.16.0.3", - [ - {update, megaco_flex_scanner_handler, {advanced, downgrade_to_pre_3_17}, - soft_purge, soft_purge, []} - ] - } + {<<"3\\..*">>, [{restart_application, megaco}]} ] }. diff --git a/lib/observer/src/crashdump_viewer.erl b/lib/observer/src/crashdump_viewer.erl index de52e2a995..9268dac180 100644 --- a/lib/observer/src/crashdump_viewer.erl +++ b/lib/observer/src/crashdump_viewer.erl @@ -1491,6 +1491,9 @@ get_portinfo(Fd,Port) -> "Port controls linked-in driver" -> Str = lists:flatten(["Linked in driver: " | val(Fd)]), get_portinfo(Fd,Port#port{controls=Str}); + "Port controls forker process" -> + Str = lists:flatten(["Forker process: " | val(Fd)]), + get_portinfo(Fd,Port#port{controls=Str}); "Port controls external process" -> Str = lists:flatten(["External proc: " | val(Fd)]), get_portinfo(Fd,Port#port{controls=Str}); diff --git a/lib/observer/src/observer_procinfo.erl b/lib/observer/src/observer_procinfo.erl index fe2aa11450..620979dcc9 100644 --- a/lib/observer/src/observer_procinfo.erl +++ b/lib/observer/src/observer_procinfo.erl @@ -317,7 +317,7 @@ fetch_state_info2(Pid, M) -> of {status, _, {module, _}, [_PDict, _SysState, _Parent, _Dbg, - [Header,{data, First},{data, Second}]]} -> + [Header,{data, First},{data, Second}|_]]} -> [{"Behaviour", B}, Header] ++ First ++ Second; {status, _, {module, _}, [_PDict, _SysState, _Parent, _Dbg, diff --git a/lib/public_key/doc/src/Makefile b/lib/public_key/doc/src/Makefile index f3db24afc9..5bdc5d4159 100644 --- a/lib/public_key/doc/src/Makefile +++ b/lib/public_key/doc/src/Makefile @@ -38,7 +38,7 @@ RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN) # ---------------------------------------------------- XML_APPLICATION_FILES = ref_man.xml XML_REF3_FILES = public_key.xml -XML_REF6_FILES = +XML_REF6_FILES = public_key_app.xml XML_PART_FILES = part.xml part_notes.xml XML_CHAPTER_FILES = \ @@ -50,7 +50,7 @@ XML_CHAPTER_FILES = \ BOOK_FILES = book.xml XML_FILES = $(BOOK_FILES) $(XML_APPLICATION_FILES) $(XML_REF3_FILES) \ - $(XML_PART_FILES) $(XML_CHAPTER_FILES) + $(XML_REF6_FILES) $(XML_PART_FILES) $(XML_CHAPTER_FILES) GIF_FILES = note.gif @@ -67,9 +67,11 @@ EXTRA_FILES = \ $(DEFAULT_GIF_FILES) \ $(DEFAULT_HTML_FILES) \ $(XML_REF3_FILES:%.xml=$(HTMLDIR)/%.html) \ + $(XML_REF6_FILES:%.xml=$(HTMLDIR)/%.html) \ $(XML_CHAPTER_FILES:%.xml=$(HTMLDIR)/%.html) MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3) +MAN6_FILES = $(XML_REF6_FILES:%_app.xml=$(MAN6DIR)/%.6) HTML_REF_MAN_FILE = $(HTMLDIR)/index.html @@ -98,10 +100,11 @@ html: gifs $(HTML_REF_MAN_FILE) clean clean_docs: rm -rf $(HTMLDIR)/* rm -f $(MAN3DIR)/* + rm -f $(MAN6DIR)/* rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo) rm -f errs core *~ -man: $(MAN3_FILES) +man: $(MAN3_FILES) $(MAN6_FILES) gifs: $(GIF_FILES:%=$(HTMLDIR)/%) @@ -122,6 +125,8 @@ release_docs_spec: docs $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man6" + $(INSTALL_DATA) $(MAN6DIR)/* "$(RELEASE_PATH)/man/man6" release_spec: info: diff --git a/lib/public_key/doc/src/public_key.xml b/lib/public_key/doc/src/public_key.xml index 04daee460f..1aa601dc55 100644 --- a/lib/public_key/doc/src/public_key.xml +++ b/lib/public_key/doc/src/public_key.xml @@ -34,40 +34,13 @@ <module>public_key</module> <modulesummary>API module for public-key infrastructure.</modulesummary> <description> - <p>This module provides functions to handle public-key infrastructure. It can - encode/decode different file formats (PEM, OpenSSH), sign and verify digital signatures, - and validate certificate paths and certificate revocation lists. + <p>Provides functions to handle public-key infrastructure, + for details see + <seealso marker="public_key_app">public_key(6)</seealso>. </p> </description> <section> - <title>public_key</title> - - <list type="bulleted"> - <item> Public Key requires the Crypto and ASN1 applications, - the latter as OTP R16 (hopefully the runtime dependency on ASN1 will - be removed again in the future).</item> - - <item>Supports <url href="http://www.ietf.org/rfc/rfc5280.txt">RFC 5280 </url> - - Internet X.509 Public-Key Infrastructure Certificate and Certificate Revocation List - (CRL) Profile </item> - <item>Supports <url href="http://www.ietf.org/rfc/rfc3447.txt"> PKCS-1 </url> - - RSA Cryptography Standard </item> - <item>Supports <url href="http://csrc.nist.gov/publications/fips/fips186-3/fips_186-3.pdf"> DSS</url> - - Digital Signature Standard (DSA - Digital Signature Algorithm)</item> - <item>Supports - <url href="http://www.emc.com/emc-plus/rsa-labs/standards-initiatives/pkcs-3-diffie-hellman-key-agreement-standar.htm"> PKCS-3 </url> - - Diffie-Hellman Key Agreement Standard </item> - <item>Supports <url href="http://www.ietf.org/rfc/rfc2898.txt"> PKCS-5</url> - - Password-Based Cryptography Standard </item> - <item>Supports <url href="http://www.ietf.org/rfc/rfc5208.txt"> PKCS-8</url> - - Private-Key Information Syntax Standard</item> - <item>Supports <url href="http://www.ietf.org/rfc/rfc5967.txt"> PKCS-10</url> - - Certification Request Syntax Standard</item> - </list> - </section> - - <section> <title>DATA TYPES</title> <note><p>All records used in this Reference Manual diff --git a/lib/public_key/doc/src/public_key_app.xml b/lib/public_key/doc/src/public_key_app.xml new file mode 100644 index 0000000000..1f87932b6c --- /dev/null +++ b/lib/public_key/doc/src/public_key_app.xml @@ -0,0 +1,85 @@ +<?xml version="1.0" encoding="utf-8" ?> +<!DOCTYPE appref SYSTEM "appref.dtd"> + +<appref> + <header> + <copyright> + <year>2016</year><year>2016</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + 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. + + </legalnotice> + + <title>public_key</title> + <prepared></prepared> + <docno></docno> + <date></date> + <rev></rev> + <file>public_key_app.sgml</file> + </header> + <app>public_key</app> + <appsummary>Provides functions to handle public-key infrastructure. </appsummary> + <description> + + <p> Provides encode/decode of different file formats (PEM, OpenSSH), + digital signature and verification functions, + validation of certificate paths and certificate revocation lists (CRLs) and + other functions for handling of certificates, keys and CRLs.</p> + + <list type="bulleted"> + <item>Supports <url href="http://www.ietf.org/rfc/rfc5280.txt">RFC 5280 </url> - + Internet X.509 Public-Key Infrastructure Certificate and Certificate Revocation List + (CRL) Profile. Certificate policies are currently not supported. </item> + <item>Supports <url href="http://www.ietf.org/rfc/rfc3447.txt"> PKCS-1 </url> - + RSA Cryptography Standard </item> + <item>Supports <url href="http://csrc.nist.gov/publications/fips/fips186-3/fips_186-3.pdf"> DSS</url> - + Digital Signature Standard (DSA - Digital Signature Algorithm)</item> + <item>Supports + <url href="http://www.emc.com/emc-plus/rsa-labs/standards-initiatives/pkcs-3-diffie-hellman-key-agreement-standar.htm"> PKCS-3 </url> - + Diffie-Hellman Key Agreement Standard </item> + <item>Supports <url href="http://www.ietf.org/rfc/rfc2898.txt"> PKCS-5</url> - + Password-Based Cryptography Standard </item> + <item>Supports <url href="http://www.ietf.org/rfc/rfc5208.txt"> PKCS-8</url> - + Private-Key Information Syntax Standard</item> + <item>Supports <url href="http://www.ietf.org/rfc/rfc5967.txt"> PKCS-10</url> - + Certification Request Syntax Standard</item> + </list> + </description> + + <section> + <title>DEPENDENCIES</title> + <p>The <c>public_key</c> application uses the + Crypto application to preform cryptographic operations and the + ASN-1 application to handle PKIX-ASN-1 specifications, hence + these applications must be loaded for the <c>public_key</c> application to work. + In an embedded environment this means they must be started with + <c>application:start/[1,2]</c> before the <c>public_key</c> application is + started.</p> + </section> + + <section> + <title>ERROR LOGGER AND EVENT HANDLERS</title> + <p> The <c>public_key</c> application is a library application + and does not use the error logger. The functions will either sucssed + or fail with a runtime error. + </p> + </section> + + <section> + <title>SEE ALSO</title> + <p><seealso marker="kernel:application">application(3)</seealso></p> + </section> + +</appref> diff --git a/lib/public_key/doc/src/ref_man.xml b/lib/public_key/doc/src/ref_man.xml index 75c5374257..2bd1733dbc 100644 --- a/lib/public_key/doc/src/ref_man.xml +++ b/lib/public_key/doc/src/ref_man.xml @@ -36,6 +36,7 @@ from RFC 3280 (X.509 certificates) and parts of the PKCS standard. </p> </description> + <xi:include href="public_key_app.xml"/> <xi:include href="public_key.xml"/> </application> diff --git a/lib/public_key/src/pubkey_cert.erl b/lib/public_key/src/pubkey_cert.erl index c5e6ffded5..f45f2c2e9a 100644 --- a/lib/public_key/src/pubkey_cert.erl +++ b/lib/public_key/src/pubkey_cert.erl @@ -547,7 +547,9 @@ cert_auth_key_id(#'AuthorityKeyIdentifier'{authorityCertIssuer = {ok, {SerialNr, decode_general_name(AuthCertIssuer)}}. decode_general_name([{directoryName, Issuer}]) -> - normalize_general_name(Issuer). + normalize_general_name(Issuer); +decode_general_name([{_, Issuer}]) -> + Issuer. %% Strip all leading and trailing spaces and make %% sure there is no double spaces in between. diff --git a/lib/public_key/test/public_key_SUITE.erl b/lib/public_key/test/public_key_SUITE.erl index 9c39c36be4..71a77efa2e 100644 --- a/lib/public_key/test/public_key_SUITE.erl +++ b/lib/public_key/test/public_key_SUITE.erl @@ -44,7 +44,7 @@ all() -> encrypt_decrypt, {group, sign_verify}, pkix, pkix_countryname, pkix_emailaddress, pkix_path_validation, - pkix_iso_rsa_oid, pkix_iso_dsa_oid, pkix_crl]. + pkix_iso_rsa_oid, pkix_iso_dsa_oid, pkix_crl, general_name]. groups() -> [{pem_decode_encode, [], [dsa_pem, rsa_pem, ec_pem, encrypted_pem, @@ -644,11 +644,10 @@ pkix(Config) when is_list(Config) -> [{'AttributeTypeAndValue', {2,5,4,3},{printableString," erlang ca "}}]]}, VerifyStr = {rdnSequence, [[{'AttributeTypeAndValue', {2,5,4,3},{printableString,"erlangca"}}], - [{'AttributeTypeAndValue', {2,5,4,3},{printableString,"erlang ca"}}]]}, - VerifyStr = public_key:pkix_normalize_name(TestStr), - - ok. - + [{'AttributeTypeAndValue', {2,5,4,3},{printableString,"erlang ca"}}]]}, + VerifyStr = public_key:pkix_normalize_name(TestStr). + + %%-------------------------------------------------------------------- pkix_countryname() -> [{doc, "Test workaround for certs that code x509countryname as utf8"}]. @@ -805,6 +804,18 @@ pkix_crl(Config) when is_list(Config) -> reasons = asn1_NOVALUE, distributionPoint = Point} = public_key:pkix_dist_point(OTPIDPCert). +general_name() -> + [{doc, "Test that decoding of general name filed may have other values" + " than {rdnSequence, Seq}"}]. + +general_name(Config) when is_list(Config) -> + DummyRfc822Name = "CN=CNDummy, OU=OUDummy, O=ODummy, C=SE", + {ok, {1, DummyRfc822Name}} = + pubkey_cert:cert_auth_key_id( + #'AuthorityKeyIdentifier'{authorityCertIssuer = + [{rfc822Name, DummyRfc822Name}], + authorityCertSerialNumber = + 1}). %%-------------------------------------------------------------------- %% Internal functions ------------------------------------------------ %%-------------------------------------------------------------------- diff --git a/lib/runtime_tools/c_src/dyntrace_lttng.h b/lib/runtime_tools/c_src/dyntrace_lttng.h index 2a3224e191..5e838892d6 100644 --- a/lib/runtime_tools/c_src/dyntrace_lttng.h +++ b/lib/runtime_tools/c_src/dyntrace_lttng.h @@ -19,7 +19,7 @@ */ #undef TRACEPOINT_PROVIDER -#define TRACEPOINT_PROVIDER com_ericsson_dyntrace +#define TRACEPOINT_PROVIDER org_erlang_dyntrace #if !defined(DYNTRACE_LTTNG_H) || defined(TRACEPOINT_HEADER_MULTI_READ) #define DYNTRACE_LTTNG_H @@ -27,22 +27,22 @@ #include <lttng/tracepoint.h> #define LTTNG1(Name, Arg1) \ - tracepoint(com_ericsson_dyntrace, Name, (Arg1)) + tracepoint(org_erlang_dyntrace, Name, (Arg1)) #define LTTNG2(Name, Arg1, Arg2) \ - tracepoint(com_ericsson_dyntrace, Name, (Arg1), (Arg2)) + tracepoint(org_erlang_dyntrace, Name, (Arg1), (Arg2)) #define LTTNG3(Name, Arg1, Arg2, Arg3) \ - tracepoint(com_ericsson_dyntrace, Name, (Arg1), (Arg2), (Arg3)) + tracepoint(org_erlang_dyntrace, Name, (Arg1), (Arg2), (Arg3)) #define LTTNG4(Name, Arg1, Arg2, Arg3, Arg4) \ - tracepoint(com_ericsson_dyntrace, Name, (Arg1), (Arg2), (Arg3), (Arg4)) + tracepoint(org_erlang_dyntrace, Name, (Arg1), (Arg2), (Arg3), (Arg4)) #define LTTNG5(Name, Arg1, Arg2, Arg3, Arg4, Arg5) \ - tracepoint(com_ericsson_dyntrace, Name, (Arg1), (Arg2), (Arg3), (Arg4), (Arg5)) + tracepoint(org_erlang_dyntrace, Name, (Arg1), (Arg2), (Arg3), (Arg4), (Arg5)) #define LTTNG_ENABLED(Name) \ - tracepoint_enabled(com_ericsson_dyntrace, Name) + tracepoint_enabled(org_erlang_dyntrace, Name) #define LTTNG_BUFFER_SZ (256) #define LTTNG_PROC_BUFFER_SZ (16) @@ -76,7 +76,7 @@ /* Process scheduling */ TRACEPOINT_EVENT( - com_ericsson_dyntrace, + org_erlang_dyntrace, process_spawn, TP_ARGS( char*, p, @@ -91,7 +91,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_dyntrace, + org_erlang_dyntrace, process_link, TP_ARGS( char*, from, @@ -106,7 +106,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_dyntrace, + org_erlang_dyntrace, process_exit, TP_ARGS( char*, p, @@ -119,7 +119,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_dyntrace, + org_erlang_dyntrace, process_register, TP_ARGS( char*, pid, @@ -136,7 +136,7 @@ TRACEPOINT_EVENT( /* Scheduled */ TRACEPOINT_EVENT( - com_ericsson_dyntrace, + org_erlang_dyntrace, process_scheduled, TP_ARGS( char*, p, @@ -154,7 +154,7 @@ TRACEPOINT_EVENT( TRACEPOINT_EVENT( - com_ericsson_dyntrace, + org_erlang_dyntrace, port_open, TP_ARGS( char*, pid, @@ -169,7 +169,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_dyntrace, + org_erlang_dyntrace, port_exit, TP_ARGS( char*, port, @@ -182,7 +182,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_dyntrace, + org_erlang_dyntrace, port_link, TP_ARGS( char*, from, @@ -197,7 +197,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_dyntrace, + org_erlang_dyntrace, port_scheduled, TP_ARGS( char*, p, @@ -214,7 +214,7 @@ TRACEPOINT_EVENT( /* Call tracing */ TRACEPOINT_EVENT( - com_ericsson_dyntrace, + org_erlang_dyntrace, function_call, TP_ARGS( char*, pid, @@ -229,7 +229,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_dyntrace, + org_erlang_dyntrace, function_return, TP_ARGS( char*, pid, @@ -244,7 +244,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_dyntrace, + org_erlang_dyntrace, function_exception, TP_ARGS( char*, pid, @@ -261,7 +261,7 @@ TRACEPOINT_EVENT( /* Process messages */ TRACEPOINT_EVENT( - com_ericsson_dyntrace, + org_erlang_dyntrace, message_send, TP_ARGS( char*, sender, @@ -276,7 +276,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_dyntrace, + org_erlang_dyntrace, message_receive, TP_ARGS( char*, receiver, @@ -291,7 +291,7 @@ TRACEPOINT_EVENT( /* Process Memory */ TRACEPOINT_EVENT( - com_ericsson_dyntrace, + org_erlang_dyntrace, gc_minor_start, TP_ARGS( char*, p, @@ -308,7 +308,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_dyntrace, + org_erlang_dyntrace, gc_minor_end, TP_ARGS( char*, p, @@ -325,7 +325,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_dyntrace, + org_erlang_dyntrace, gc_major_start, TP_ARGS( char*, p, @@ -342,7 +342,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_dyntrace, + org_erlang_dyntrace, gc_major_end, TP_ARGS( char*, p, diff --git a/lib/runtime_tools/doc/src/LTTng.xml b/lib/runtime_tools/doc/src/LTTng.xml index 9b490a27a0..82a4c79379 100644 --- a/lib/runtime_tools/doc/src/LTTng.xml +++ b/lib/runtime_tools/doc/src/LTTng.xml @@ -66,7 +66,7 @@ $ make </code> <section> <title>Dyntrace Tracepoints</title> - <p>All tracepoints are in the domain of <c>com_ericsson_dyntrace</c></p> + <p>All tracepoints are in the domain of <c>org_erlang_dyntrace</c></p> <p>All Erlang types are the string equivalent in LTTng.</p> <p><em>process_spawn</em></p> @@ -310,7 +310,7 @@ $ make </code> <section> <title>BEAM Tracepoints</title> - <p>All tracepoints are in the domain of <c>com_ericsson_otp</c></p> + <p>All tracepoints are in the domain of <c>org_erlang_otp</c></p> <p>All Erlang types are the string equivalent in LTTng.</p> <p><em>scheduler_poll</em></p> @@ -550,8 +550,8 @@ Eshell V8.0 (abort with ^G) <p>Enable the process_register LTTng tracepoint for Erlang.</p> - <pre>$ lttng enable-event -u com_ericsson_dyntrace:process_register -UST event com_ericsson_dyntrace:process_register created in channel channel0</pre> + <pre>$ lttng enable-event -u org_erlang_dyntrace:process_register +UST event org_erlang_dyntrace:process_register created in channel channel0</pre> <p>Enable process tracing for new processes and use <c>dyntrace</c> as tracer backend.</p> @@ -573,23 +573,23 @@ Tracing started for session erlang-demo</pre> <pre>$ lttng stop Tracing stopped for session erlang-demo $ lttng view -[17:20:42.561168759] (+?.?????????) elxd1168lx9 com_ericsson_dyntrace:process_register: \ +[17:20:42.561168759] (+?.?????????) elxd1168lx9 org_erlang_dyntrace:process_register: \ { cpu_id = 5 }, { pid = "<0.66.0>", name = "sasl_sup", type = "register" } -[17:20:42.561215519] (+0.000046760) elxd1168lx9 com_ericsson_dyntrace:process_register: \ +[17:20:42.561215519] (+0.000046760) elxd1168lx9 org_erlang_dyntrace:process_register: \ { cpu_id = 5 }, { pid = "<0.67.0>", name = "sasl_safe_sup", type = "register" } -[17:20:42.562149024] (+0.000933505) elxd1168lx9 com_ericsson_dyntrace:process_register: \ +[17:20:42.562149024] (+0.000933505) elxd1168lx9 org_erlang_dyntrace:process_register: \ { cpu_id = 5 }, { pid = "<0.68.0>", name = "alarm_handler", type = "register" } -[17:20:42.571035803] (+0.008886779) elxd1168lx9 com_ericsson_dyntrace:process_register: \ +[17:20:42.571035803] (+0.008886779) elxd1168lx9 org_erlang_dyntrace:process_register: \ { cpu_id = 5 }, { pid = "<0.69.0>", name = "release_handler", type = "register" } -[17:20:42.574939868] (+0.003904065) elxd1168lx9 com_ericsson_dyntrace:process_register: \ +[17:20:42.574939868] (+0.003904065) elxd1168lx9 org_erlang_dyntrace:process_register: \ { cpu_id = 5 }, { pid = "<0.74.0>", name = "os_mon_sup", type = "register" } -[17:20:42.576818712] (+0.001878844) elxd1168lx9 com_ericsson_dyntrace:process_register: \ +[17:20:42.576818712] (+0.001878844) elxd1168lx9 org_erlang_dyntrace:process_register: \ { cpu_id = 5 }, { pid = "<0.75.0>", name = "disksup", type = "register" } -[17:20:42.580032013] (+0.003213301) elxd1168lx9 com_ericsson_dyntrace:process_register: \ +[17:20:42.580032013] (+0.003213301) elxd1168lx9 org_erlang_dyntrace:process_register: \ { cpu_id = 5 }, { pid = "<0.76.0>", name = "memsup", type = "register" } -[17:20:42.583046339] (+0.003014326) elxd1168lx9 com_ericsson_dyntrace:process_register: \ +[17:20:42.583046339] (+0.003014326) elxd1168lx9 org_erlang_dyntrace:process_register: \ { cpu_id = 5 }, { pid = "<0.78.0>", name = "cpu_sup", type = "register" } -[17:20:42.586206242] (+0.003159903) elxd1168lx9 com_ericsson_dyntrace:process_register: \ +[17:20:42.586206242] (+0.003159903) elxd1168lx9 org_erlang_dyntrace:process_register: \ { cpu_id = 5 }, { pid = "<0.82.0>", name = "timer_server", type = "register" }</pre> </section> </chapter> diff --git a/lib/runtime_tools/test/dyntrace_lttng_SUITE.erl b/lib/runtime_tools/test/dyntrace_lttng_SUITE.erl index e6c147b003..a98ac6e99c 100644 --- a/lib/runtime_tools/test/dyntrace_lttng_SUITE.erl +++ b/lib/runtime_tools/test/dyntrace_lttng_SUITE.erl @@ -84,25 +84,25 @@ end_per_testcase(Case, _Config) -> %% tracepoints %% -%% com_ericsson_dyntrace:gc_major_end -%% com_ericsson_dyntrace:gc_major_start -%% com_ericsson_dyntrace:gc_minor_end -%% com_ericsson_dyntrace:gc_minor_start -%% com_ericsson_dyntrace:message_receive -%% com_ericsson_dyntrace:message_send -%% -com_ericsson_dyntrace:message_queued -%% com_ericsson_dyntrace:function_exception -%% com_ericsson_dyntrace:function_return -%% com_ericsson_dyntrace:function_call -%% com_ericsson_dyntrace:port_link -%% com_ericsson_dyntrace:port_exit -%% com_ericsson_dyntrace:port_open -%% com_ericsson_dyntrace:port_scheduled -%% com_ericsson_dyntrace:process_scheduled -%% com_ericsson_dyntrace:process_register -%% com_ericsson_dyntrace:process_exit -%% com_ericsson_dyntrace:process_link -%% com_ericsson_dyntrace:process_spawn +%% org_erlang_dyntrace:gc_major_end +%% org_erlang_dyntrace:gc_major_start +%% org_erlang_dyntrace:gc_minor_end +%% org_erlang_dyntrace:gc_minor_start +%% org_erlang_dyntrace:message_receive +%% org_erlang_dyntrace:message_send +%% -org_erlang_dyntrace:message_queued +%% org_erlang_dyntrace:function_exception +%% org_erlang_dyntrace:function_return +%% org_erlang_dyntrace:function_call +%% org_erlang_dyntrace:port_link +%% org_erlang_dyntrace:port_exit +%% org_erlang_dyntrace:port_open +%% org_erlang_dyntrace:port_scheduled +%% org_erlang_dyntrace:process_scheduled +%% org_erlang_dyntrace:process_register +%% org_erlang_dyntrace:process_exit +%% org_erlang_dyntrace:process_link +%% org_erlang_dyntrace:process_spawn %% %% Testcases %% @@ -112,7 +112,7 @@ t_lttng_list(_Config) -> ok. t_procs(Config) when is_list(Config) -> - ok = lttng_start_event("com_ericsson_dyntrace:process_*", Config), + ok = lttng_start_event("org_erlang_dyntrace:process_*", Config), _ = erlang:trace(new, true, [{tracer, dyntrace, []},procs]), Pid = spawn_link(fun() -> waiter() end), @@ -122,27 +122,27 @@ t_procs(Config) when is_list(Config) -> _ = erlang:trace(all, false, [procs]), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_dyntrace:process_spawn", Res), - ok = check_tracepoint("com_ericsson_dyntrace:process_link", Res), - ok = check_tracepoint("com_ericsson_dyntrace:process_exit", Res), - ok = check_tracepoint("com_ericsson_dyntrace:process_register", Res), + ok = check_tracepoint("org_erlang_dyntrace:process_spawn", Res), + ok = check_tracepoint("org_erlang_dyntrace:process_link", Res), + ok = check_tracepoint("org_erlang_dyntrace:process_exit", Res), + ok = check_tracepoint("org_erlang_dyntrace:process_register", Res), ok. t_ports(Config) when is_list(Config) -> - ok = lttng_start_event("com_ericsson_dyntrace:port_*", Config), + ok = lttng_start_event("org_erlang_dyntrace:port_*", Config), _ = erlang:trace(new, true, [{tracer, dyntrace, []},ports]), _ = os:cmd("ls"), _ = erlang:trace(all, false, [{tracer, dyntrace, []},ports]), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_dyntrace:port_open", Res), - ok = check_tracepoint("com_ericsson_dyntrace:port_link", Res), - ok = check_tracepoint("com_ericsson_dyntrace:port_exit", Res), + ok = check_tracepoint("org_erlang_dyntrace:port_open", Res), + ok = check_tracepoint("org_erlang_dyntrace:port_link", Res), + ok = check_tracepoint("org_erlang_dyntrace:port_exit", Res), ok. t_running_process(Config) when is_list(Config) -> - ok = lttng_start_event("com_ericsson_dyntrace:process_scheduled", Config), + ok = lttng_start_event("org_erlang_dyntrace:process_scheduled", Config), _ = erlang:trace(new, true, [{tracer, dyntrace, []},running]), Pid = spawn_link(fun() -> waiter() end), @@ -152,11 +152,11 @@ t_running_process(Config) when is_list(Config) -> _ = erlang:trace(all, false, [running]), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_dyntrace:process_scheduled", Res), + ok = check_tracepoint("org_erlang_dyntrace:process_scheduled", Res), ok. t_running_port(Config) when is_list(Config) -> - ok = lttng_start_event("com_ericsson_dyntrace:port_scheduled", Config), + ok = lttng_start_event("org_erlang_dyntrace:port_scheduled", Config), _ = erlang:trace(new, true, [{tracer, dyntrace, []},running_ports]), _ = os:cmd("ls"), @@ -164,12 +164,12 @@ t_running_port(Config) when is_list(Config) -> _ = erlang:trace(all, false, [running_ports]), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_dyntrace:port_scheduled", Res), + ok = check_tracepoint("org_erlang_dyntrace:port_scheduled", Res), ok. t_call(Config) when is_list(Config) -> - ok = lttng_start_event("com_ericsson_dyntrace:function_*", Config), + ok = lttng_start_event("org_erlang_dyntrace:function_*", Config), _ = erlang:trace(new, true, [{tracer, dyntrace, []}, call]), _ = erlang:trace_pattern({?MODULE, '_', '_'}, [{'_',[],[{exception_trace}]}], [local]), @@ -184,13 +184,13 @@ t_call(Config) when is_list(Config) -> _ = erlang:trace_pattern({?MODULE, '_', '_'}, false, [local]), _ = erlang:trace(all, false, [call]), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_dyntrace:function_call", Res), - ok = check_tracepoint("com_ericsson_dyntrace:function_return", Res), - ok = check_tracepoint("com_ericsson_dyntrace:function_exception", Res), + ok = check_tracepoint("org_erlang_dyntrace:function_call", Res), + ok = check_tracepoint("org_erlang_dyntrace:function_return", Res), + ok = check_tracepoint("org_erlang_dyntrace:function_exception", Res), ok. t_send(Config) when is_list(Config) -> - ok = lttng_start_event("com_ericsson_dyntrace:message_send", Config), + ok = lttng_start_event("org_erlang_dyntrace:message_send", Config), _ = erlang:trace(new, true, [{tracer, dyntrace, []},send]), Pid = spawn_link(fun() -> waiter() end), @@ -201,11 +201,11 @@ t_send(Config) when is_list(Config) -> _ = erlang:trace(all, false, [send]), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_dyntrace:message_send", Res), + ok = check_tracepoint("org_erlang_dyntrace:message_send", Res), ok. t_call_return_to(Config) when is_list(Config) -> - ok = lttng_start_event("com_ericsson_dyntrace:function_*", Config), + ok = lttng_start_event("org_erlang_dyntrace:function_*", Config), _ = erlang:trace(new, true, [{tracer, dyntrace, []}, call, return_to]), _ = erlang:trace_pattern({lists, '_', '_'}, true, [local]), _ = erlang:trace_pattern({?MODULE, '_', '_'}, true, [local]), @@ -219,11 +219,11 @@ t_call_return_to(Config) when is_list(Config) -> _ = erlang:trace_pattern({lists, '_', '_'}, false, [local]), _ = erlang:trace(all, false, [call,return_to]), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_dyntrace:function_call", Res), + ok = check_tracepoint("org_erlang_dyntrace:function_call", Res), ok. t_call_silent(Config) when is_list(Config) -> - ok = lttng_start_event("com_ericsson_dyntrace:function_*", Config), + ok = lttng_start_event("org_erlang_dyntrace:function_*", Config), _ = erlang:trace(new, true, [{tracer, dyntrace, []}, call, silent]), _ = erlang:trace_pattern({?MODULE, '_', '_'}, [{'_',[],[{exception_trace}]}], [local]), @@ -238,27 +238,33 @@ t_call_silent(Config) when is_list(Config) -> _ = erlang:trace_pattern({?MODULE, '_', '_'}, false, [local]), _ = erlang:trace(all, false, [call]), Res = lttng_stop_and_view(Config), - notfound = check_tracepoint("com_ericsson_dyntrace:function_call", Res), - notfound = check_tracepoint("com_ericsson_dyntrace:function_return", Res), - notfound = check_tracepoint("com_ericsson_dyntrace:function_exception", Res), + notfound = check_tracepoint("org_erlang_dyntrace:function_call", Res), + notfound = check_tracepoint("org_erlang_dyntrace:function_return", Res), + notfound = check_tracepoint("org_erlang_dyntrace:function_exception", Res), ok. t_receive(Config) when is_list(Config) -> - ok = lttng_start_event("com_ericsson_dyntrace:message_receive", Config), + ok = lttng_start_event("org_erlang_dyntrace:message_receive", Config), _ = erlang:trace(new, true, [{tracer, dyntrace, []},'receive']), + timer:sleep(20), + + Pid1 = spawn_link(fun() -> waiter() end), + Pid1 ! {self(), ok}, + ok = receive {Pid1,ok} -> ok end, + + Pid2 = spawn_link(fun() -> waiter() end), + Pid2 ! {self(), ok}, + ok = receive {Pid2,ok} -> ok end, - Pid = spawn_link(fun() -> waiter() end), - Pid ! {self(), ok}, - ok = receive {Pid,ok} -> ok end, timer:sleep(10), _ = erlang:trace(all, false, ['receive']), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_dyntrace:message_receive", Res), + ok = check_tracepoint("org_erlang_dyntrace:message_receive", Res), ok. t_garbage_collection(Config) when is_list(Config) -> - ok = lttng_start_event("com_ericsson_dyntrace:gc_*", Config), + ok = lttng_start_event("org_erlang_dyntrace:gc_*", Config), _ = erlang:trace(new, true, [{tracer, dyntrace, []},garbage_collection]), Pid = spawn_link(fun() -> gcfier() end), @@ -267,14 +273,14 @@ t_garbage_collection(Config) when is_list(Config) -> timer:sleep(10), _ = erlang:trace(all, false, [garbage_collection]), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_dyntrace:gc_major_start", Res), - ok = check_tracepoint("com_ericsson_dyntrace:gc_major_end", Res), - ok = check_tracepoint("com_ericsson_dyntrace:gc_minor_start", Res), - ok = check_tracepoint("com_ericsson_dyntrace:gc_minor_end", Res), + ok = check_tracepoint("org_erlang_dyntrace:gc_major_start", Res), + ok = check_tracepoint("org_erlang_dyntrace:gc_major_end", Res), + ok = check_tracepoint("org_erlang_dyntrace:gc_minor_start", Res), + ok = check_tracepoint("org_erlang_dyntrace:gc_minor_end", Res), ok. t_all(Config) when is_list(Config) -> - ok = lttng_start_event("com_ericsson_dyntrace:*", Config), + ok = lttng_start_event("org_erlang_dyntrace:*", Config), _ = erlang:trace(new, true, [{tracer, dyntrace, []},all]), Pid1 = spawn_link(fun() -> waiter() end), @@ -291,21 +297,21 @@ t_all(Config) when is_list(Config) -> _ = erlang:trace(all, false, [all]), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_dyntrace:process_spawn", Res), - ok = check_tracepoint("com_ericsson_dyntrace:process_link", Res), - ok = check_tracepoint("com_ericsson_dyntrace:process_exit", Res), - ok = check_tracepoint("com_ericsson_dyntrace:process_register", Res), - ok = check_tracepoint("com_ericsson_dyntrace:port_open", Res), - ok = check_tracepoint("com_ericsson_dyntrace:port_link", Res), - ok = check_tracepoint("com_ericsson_dyntrace:port_exit", Res), - ok = check_tracepoint("com_ericsson_dyntrace:process_scheduled", Res), - ok = check_tracepoint("com_ericsson_dyntrace:port_scheduled", Res), - ok = check_tracepoint("com_ericsson_dyntrace:message_send", Res), - ok = check_tracepoint("com_ericsson_dyntrace:message_receive", Res), - ok = check_tracepoint("com_ericsson_dyntrace:gc_major_start", Res), - ok = check_tracepoint("com_ericsson_dyntrace:gc_major_end", Res), - ok = check_tracepoint("com_ericsson_dyntrace:gc_minor_start", Res), - ok = check_tracepoint("com_ericsson_dyntrace:gc_minor_end", Res), + ok = check_tracepoint("org_erlang_dyntrace:process_spawn", Res), + ok = check_tracepoint("org_erlang_dyntrace:process_link", Res), + ok = check_tracepoint("org_erlang_dyntrace:process_exit", Res), + ok = check_tracepoint("org_erlang_dyntrace:process_register", Res), + ok = check_tracepoint("org_erlang_dyntrace:port_open", Res), + ok = check_tracepoint("org_erlang_dyntrace:port_link", Res), + ok = check_tracepoint("org_erlang_dyntrace:port_exit", Res), + ok = check_tracepoint("org_erlang_dyntrace:process_scheduled", Res), + ok = check_tracepoint("org_erlang_dyntrace:port_scheduled", Res), + ok = check_tracepoint("org_erlang_dyntrace:message_send", Res), + ok = check_tracepoint("org_erlang_dyntrace:message_receive", Res), + ok = check_tracepoint("org_erlang_dyntrace:gc_major_start", Res), + ok = check_tracepoint("org_erlang_dyntrace:gc_major_end", Res), + ok = check_tracepoint("org_erlang_dyntrace:gc_minor_start", Res), + ok = check_tracepoint("org_erlang_dyntrace:gc_minor_end", Res), ok. diff --git a/lib/sasl/test/release_handler_SUITE.erl b/lib/sasl/test/release_handler_SUITE.erl index 4dcaec03a7..8134e02221 100644 --- a/lib/sasl/test/release_handler_SUITE.erl +++ b/lib/sasl/test/release_handler_SUITE.erl @@ -1366,7 +1366,7 @@ upgrade_supervisor(Conf) when is_list(Conf) -> ASupBeam2 = rpc:call(Node, code, which, [a_sup]), %% Check that the restart strategy and child spec is updated - {status, _, {module, _}, [_, _, _, _, [_,_,{data,[{"State",State}]}]]} = + {status, _, {module, _}, [_, _, _, _, [_,_,{data,[{"State",State}]}|_]]} = rpc:call(Node,sys,get_status,[a_sup]), {state,_,RestartStrategy,[Child],_,_,_,_,_,_,_} = State, one_for_all = RestartStrategy, % changed from one_for_one diff --git a/lib/ssh/doc/src/ssh.xml b/lib/ssh/doc/src/ssh.xml index bd330e479f..e6c54d27bf 100644 --- a/lib/ssh/doc/src/ssh.xml +++ b/lib/ssh/doc/src/ssh.xml @@ -124,10 +124,10 @@ </func> <func> - <name>connect(TcpSocket, Options) -> </name> - <name>connect(TcpSocket, Options, Timeout) -> </name> <name>connect(Host, Port, Options) -> </name> - <name>connect(Host, Port, Options, Timeout) -> + <name>connect(Host, Port, Options, Timeout) -> </name> + <name>connect(TcpSocket, Options) -> </name> + <name>connect(TcpSocket, Options, Timeout) -> {ok, ssh_connection_ref()} | {error, Reason}</name> <fsummary>Connects to an SSH server.</fsummary> <type> @@ -140,7 +140,7 @@ <d>Negotiation time-out in milli-seconds. The default value is <c>infinity</c>. For connection time-out, use option <c>{connect_timeout, timeout()}</c>.</d> <v>TcpSocket = port()</v> - <d>The socket is supposed to be from <c>gen_tcp:connect</c> with option <c>{active,false}</c></d> + <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> </type> <desc> <p>Connects to an SSH server. No channel is started. This is done @@ -351,8 +351,9 @@ <func> <name>daemon(Port) -> </name> <name>daemon(Port, Options) -> </name> - <name>daemon(HostAddress, Port, Options) -> {ok, - ssh_daemon_ref()} | {error, atom()}</name> + <name>daemon(HostAddress, Port, Options) -> </name> + <name>daemon(TcpSocket) -> </name> + <name>daemon(TcpSocket, Options) -> {ok, ssh_daemon_ref()} | {error, atom()}</name> <fsummary>Starts a server listening for SSH connections on the given port.</fsummary> <type> @@ -361,6 +362,8 @@ <v>Options = [{Option, Value}]</v> <v>Option = atom()</v> <v>Value = term()</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> </type> <desc> <p>Starts a server listening for SSH connections on the given @@ -722,12 +725,15 @@ <func> <name>shell(Host) -> </name> <name>shell(Host, Option) -> </name> - <name>shell(Host, Port, Option) -> _</name> + <name>shell(Host, Port, Option) -> </name> + <name>shell(TcpSocket) -> _</name> <fsummary>Starts an interactive shell over an SSH server.</fsummary> <type> <v>Host = string()</v> <v>Port = integer()</v> <v>Options - see ssh:connect/3</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> </type> <desc> <p>Starts an interactive shell over an SSH server on the diff --git a/lib/ssh/doc/src/ssh_sftp.xml b/lib/ssh/doc/src/ssh_sftp.xml index 67531b7d99..eb6f43d417 100644 --- a/lib/ssh/doc/src/ssh_sftp.xml +++ b/lib/ssh/doc/src/ssh_sftp.xml @@ -526,10 +526,6 @@ </func> <func> - <name>start_channel(TcpSocket) -></name> - <name>start_channel(TcpSocket, Options) -> - {ok, Pid, ConnectionRef} | {error, reason()|term()}</name> - <name>start_channel(ConnectionRef) -></name> <name>start_channel(ConnectionRef, Options) -> {ok, Pid} | {error, reason()|term()}</name> @@ -537,13 +533,18 @@ <name>start_channel(Host, Options) -></name> <name>start_channel(Host, Port, Options) -> {ok, Pid, ConnectionRef} | {error, reason()|term()}</name> + + <name>start_channel(TcpSocket) -></name> + <name>start_channel(TcpSocket, Options) -> + {ok, Pid, ConnectionRef} | {error, reason()|term()}</name> + <fsummary>Starts an SFTP client.</fsummary> <type> <v>Host = string()</v> <v>ConnectionRef = ssh_connection_ref()</v> <v>Port = integer()</v> <v>TcpSocket = port()</v> - <d>The socket is supposed to be from <c>gen_tcp:connect</c> with option <c>{active,false}</c></d> + <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> </type> <desc> diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl index 50dfe55798..65f1acc6a6 100644 --- a/lib/ssh/src/ssh.erl +++ b/lib/ssh/src/ssh.erl @@ -86,29 +86,19 @@ connect(Socket, Options) -> connect(Socket, Options, Timeout) when is_port(Socket) -> case handle_options(Options) of - {error, _Reason} = Error -> - Error; + {error, Error} -> + {error, Error}; {_SocketOptions, SshOptions} -> - case proplists:get_value(transport, Options, {tcp, gen_tcp, tcp_closed}) of - {tcp,_,_} -> - %% Is the socket a valid tcp socket? - case {{ok,[]} =/= inet:getopts(Socket, [delay_send]), - {ok,[{active,false}]} == inet:getopts(Socket, [active]) - } - of - {true, true} -> - {ok, {Host,_Port}} = inet:sockname(Socket), - Opts = [{user_pid,self()}, {host,fmt_host(Host)} | SshOptions], - ssh_connection_handler:start_connection(client, Socket, Opts, Timeout); - {true, false} -> - {error, not_passive_mode}; - _ -> - {error, not_tcp_socket} - end; - {L4,_,_} -> - {error, {unsupported,L4}} + case valid_socket_to_use(Socket, Options) of + ok -> + {ok, {Host,_Port}} = inet:sockname(Socket), + Opts = [{user_pid,self()}, {host,fmt_host(Host)} | SshOptions], + ssh_connection_handler:start_connection(client, Socket, Opts, Timeout); + {error,SockError} -> + {error,SockError} end end; + connect(Host, Port, Options) when is_integer(Port), Port>0 -> connect(Host, Port, Options, infinity). @@ -160,7 +150,7 @@ channel_info(ConnectionRef, ChannelId, Options) -> %%-------------------------------------------------------------------- -spec daemon(integer()) -> {ok, pid()} | {error, term()}. --spec daemon(integer(), proplists:proplist()) -> {ok, pid()} | {error, term()}. +-spec daemon(integer()|port(), proplists:proplist()) -> {ok, pid()} | {error, term()}. -spec daemon(any | inet:ip_address(), integer(), proplists:proplist()) -> {ok, pid()} | {error, term()}. %% Description: Starts a server listening for SSH connections @@ -169,28 +159,16 @@ channel_info(ConnectionRef, ChannelId, Options) -> daemon(Port) -> daemon(Port, []). -daemon(Port, Options) -> - daemon(any, Port, Options). +daemon(Port, Options) when is_integer(Port) -> + daemon(any, Port, Options); + +daemon(Socket, Options0) when is_port(Socket) -> + Options = daemon_shell_opt(Options0), + start_daemon(Socket, Options). daemon(HostAddr, Port, Options0) -> - Options1 = case proplists:get_value(shell, Options0) of - undefined -> - [{shell, {shell, start, []}} | Options0]; - _ -> - Options0 - end, - - {Host, Inet, Options} = case HostAddr of - any -> - {ok, Host0} = inet:gethostname(), - {Host0, proplists:get_value(inet, Options1, inet), Options1}; - {_,_,_,_} -> - {HostAddr, inet, - [{ip, HostAddr} | Options1]}; - {_,_,_,_,_,_,_,_} -> - {HostAddr, inet6, - [{ip, HostAddr} | Options1]} - end, + Options1 = daemon_shell_opt(Options0), + {Host, Inet, Options} = daemon_host_inet_opt(HostAddr, Options1), start_daemon(Host, Port, Options, Inet). %%-------------------------------------------------------------------- @@ -284,19 +262,128 @@ default_algorithms() -> %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- +valid_socket_to_use(Socket, Options) -> + case proplists:get_value(transport, Options, {tcp, gen_tcp, tcp_closed}) of + {tcp,_,_} -> + %% Is this tcp-socket a valid socket? + case {is_tcp_socket(Socket), + {ok,[{active,false}]} == inet:getopts(Socket, [active]) + } + of + {true, true} -> + ok; + {true, false} -> + {error, not_passive_mode}; + _ -> + {error, not_tcp_socket} + end; + {L4,_,_} -> + {error, {unsupported,L4}} + end. + +is_tcp_socket(Socket) -> {ok,[]} =/= inet:getopts(Socket, [delay_send]). + + + +daemon_shell_opt(Options) -> + case proplists:get_value(shell, Options) of + undefined -> + [{shell, {shell, start, []}} | Options]; + _ -> + Options + end. + +daemon_host_inet_opt(HostAddr, Options1) -> + case HostAddr of + any -> + {ok, Host0} = inet:gethostname(), + {Host0, proplists:get_value(inet, Options1, inet), Options1}; + {_,_,_,_} -> + {HostAddr, inet, + [{ip, HostAddr} | Options1]}; + {_,_,_,_,_,_,_,_} -> + {HostAddr, inet6, + [{ip, HostAddr} | Options1]} + end. + + +start_daemon(Socket, Options) -> + case handle_options(Options) of + {error, Error} -> + {error, Error}; + {SocketOptions, SshOptions} -> + case valid_socket_to_use(Socket, Options) of + ok -> + try + do_start_daemon(Socket, [{role,server}|SshOptions], SocketOptions) + catch + throw:bad_fd -> {error,bad_fd}; + _C:_E -> {error,{cannot_start_daemon,_C,_E}} + end; + {error,SockError} -> + {error,SockError} + end + end. + start_daemon(Host, Port, Options, Inet) -> case handle_options(Options) of {error, _Reason} = Error -> Error; {SocketOptions, SshOptions}-> try - do_start_daemon(Host, Port,[{role, server} |SshOptions] , [Inet | SocketOptions]) + do_start_daemon(Host, Port, [{role,server}|SshOptions] , [Inet|SocketOptions]) catch throw:bad_fd -> {error,bad_fd}; _C:_E -> {error,{cannot_start_daemon,_C,_E}} end end. +do_start_daemon(Socket, SshOptions, SocketOptions) -> + {ok, {IP,Port}} = + try {ok,_} = inet:sockname(Socket) + catch + _:_ -> throw(bad_socket) + end, + Host = fmt_host(IP), + Profile = proplists:get_value(profile, SshOptions, ?DEFAULT_PROFILE), + Opts = [{asocket, Socket}, + {asock_owner,self()}, + {address, Host}, + {port, Port}, + {role, server}, + {socket_opts, SocketOptions}, + {ssh_opts, SshOptions}], + {_, Callback, _} = proplists:get_value(transport, SshOptions, {tcp, gen_tcp, tcp_closed}), + case ssh_system_sup:system_supervisor(Host, Port, Profile) of + undefined -> + %% It would proably make more sense to call the + %% address option host but that is a too big change at the + %% monent. The name is a legacy name! + try sshd_sup:start_child(Opts) of + {error, {already_started, _}} -> + {error, eaddrinuse}; + Result = {ok,_} -> + ssh_acceptor:handle_connection(Callback, Host, Port, Opts, Socket), + Result; + Result = {error, _} -> + Result + catch + exit:{noproc, _} -> + {error, ssh_not_started} + end; + Sup -> + AccPid = ssh_system_sup:acceptor_supervisor(Sup), + case ssh_acceptor_sup:start_child(AccPid, Opts) of + {error, {already_started, _}} -> + {error, eaddrinuse}; + {ok, _} -> + ssh_acceptor:handle_connection(Callback, Host, Port, Opts, Socket), + {ok, Sup}; + Other -> + Other + end + end. + do_start_daemon(Host0, Port0, SshOptions, SocketOptions) -> {Host,Port1} = try @@ -312,7 +399,7 @@ do_start_daemon(Host0, Port0, SshOptions, SocketOptions) -> _:_ -> throw(bad_fd) end, Profile = proplists:get_value(profile, SshOptions, ?DEFAULT_PROFILE), - {Port, WaitRequestControl, Opts} = + {Port, WaitRequestControl, Opts0} = case Port1 of 0 -> %% Allocate the socket here to get the port number... {_, Callback, _} = @@ -326,17 +413,17 @@ do_start_daemon(Host0, Port0, SshOptions, SocketOptions) -> _ -> {Port1, false, []} end, + Opts = [{address, Host}, + {port, Port}, + {role, server}, + {socket_opts, SocketOptions}, + {ssh_opts, SshOptions} | Opts0], case ssh_system_sup:system_supervisor(Host, Port, Profile) of undefined -> %% It would proably make more sense to call the %% address option host but that is a too big change at the %% monent. The name is a legacy name! - try sshd_sup:start_child([{address, Host}, - {port, Port}, - {role, server}, - {socket_opts, SocketOptions}, - {ssh_opts, SshOptions} - | Opts]) of + try sshd_sup:start_child(Opts) of {error, {already_started, _}} -> {error, eaddrinuse}; Result = {ok,_} -> @@ -350,12 +437,7 @@ do_start_daemon(Host0, Port0, SshOptions, SocketOptions) -> end; Sup -> AccPid = ssh_system_sup:acceptor_supervisor(Sup), - case ssh_acceptor_sup:start_child(AccPid, [{address, Host}, - {port, Port}, - {role, server}, - {socket_opts, SocketOptions}, - {ssh_opts, SshOptions} - | Opts]) of + case ssh_acceptor_sup:start_child(AccPid, Opts) of {error, {already_started, _}} -> {error, eaddrinuse}; {ok, _} -> diff --git a/lib/ssh/src/ssh_acceptor.erl b/lib/ssh/src/ssh_acceptor.erl index 90fd951dcd..9f3e60bd62 100644 --- a/lib/ssh/src/ssh_acceptor.erl +++ b/lib/ssh/src/ssh_acceptor.erl @@ -27,7 +27,8 @@ %% Internal application API -export([start_link/5, number_of_connections/1, - callback_listen/3]). + callback_listen/3, + handle_connection/5]). %% spawn export -export([acceptor_init/6, acceptor_loop/6]). diff --git a/lib/ssh/src/ssh_system_sup.erl b/lib/ssh/src/ssh_system_sup.erl index 5035bc8f80..e97ac7b01a 100644 --- a/lib/ssh/src/ssh_system_sup.erl +++ b/lib/ssh/src/ssh_system_sup.erl @@ -131,7 +131,10 @@ init([ServerOpts]) -> RestartStrategy = one_for_one, MaxR = 0, MaxT = 3600, - Children = child_specs(ServerOpts), + Children = case proplists:get_value(asocket,ServerOpts) of + undefined -> child_specs(ServerOpts); + _ -> [] + end, {ok, {{RestartStrategy, MaxR, MaxT}, Children}}. %%%========================================================================= diff --git a/lib/ssh/test/ssh_algorithms_SUITE.erl b/lib/ssh/test/ssh_algorithms_SUITE.erl index ed9e7aacaa..0f68130a05 100644 --- a/lib/ssh/test/ssh_algorithms_SUITE.erl +++ b/lib/ssh/test/ssh_algorithms_SUITE.erl @@ -24,6 +24,7 @@ -include_lib("common_test/include/ct.hrl"). -include_lib("ssh/src/ssh_transport.hrl"). +-include("ssh_test_lib.hrl"). %% Note: This directive should only be used in test suites. -compile(export_all). @@ -70,32 +71,39 @@ two_way_tags() -> [cipher,mac,compression]. %%-------------------------------------------------------------------- init_per_suite(Config) -> - ct:log("~n" - "Environment:~n============~n" - "os:getenv(\"HOME\") = ~p~n" - "init:get_argument(home) = ~p~n~n~n" - "OS ssh:~n=======~n~p~n~n~n" - "Erl ssh:~n========~n~p~n~n~n" - "Installed ssh client:~n=====================~n~p~n~n~n" - "Installed ssh server:~n=====================~n~p~n~n~n" - "Misc values:~n============~n" - " -- Default dh group exchange parameters ({min,def,max}): ~p~n" - " -- dh_default_groups: ~p~n" - " -- Max num algorithms: ~p~n" - ,[os:getenv("HOME"), - init:get_argument(home), - os:cmd("ssh -V"), - ssh:default_algorithms(), - ssh_test_lib:default_algorithms(sshc), - ssh_test_lib:default_algorithms(sshd), - {?DEFAULT_DH_GROUP_MIN,?DEFAULT_DH_GROUP_NBITS,?DEFAULT_DH_GROUP_MAX}, - public_key:dh_gex_group_sizes(), - ?MAX_NUM_ALGORITHMS - ]), - ct:log("all() ->~n ~p.~n~ngroups()->~n ~p.~n",[all(),groups()]), - ssh:start(), - [{std_simple_sftp_size,25000} % Sftp transferred data size - | setup_pubkey(Config)]. + ?CHECK_CRYPTO( + begin + ct:log("~n" + "Environment:~n============~n" + "os:getenv(\"HOME\") = ~p~n" + "init:get_argument(home) = ~p~n~n~n" + "OS ssh:~n=======~n~p~n~n~n" + "Erl ssh:~n========~n~p~n~n~n" + "crypto:info_lib():~n========~n~p~n~n~n" + "Installed ssh client:~n=====================~n~p~n~n~n" + "Installed ssh server:~n=====================~n~p~n~n~n" + "Misc values:~n============~n" + " -- Default dh group exchange parameters ({min,def,max}): ~p~n" + " -- dh_default_groups: ~p~n" + " -- Max num algorithms: ~p~n" + ,[os:getenv("HOME"), + init:get_argument(home), + os:cmd("ssh -V"), + ssh:default_algorithms(), + crypto:info_lib(), + ssh_test_lib:default_algorithms(sshc), + ssh_test_lib:default_algorithms(sshd), + {?DEFAULT_DH_GROUP_MIN,?DEFAULT_DH_GROUP_NBITS,?DEFAULT_DH_GROUP_MAX}, + public_key:dh_gex_group_sizes(), + ?MAX_NUM_ALGORITHMS + ]), + ct:log("all() ->~n ~p.~n~ngroups()->~n ~p.~n",[all(),groups()]), + ssh:start(), + [{std_simple_sftp_size,25000} % Sftp transferred data size + | setup_pubkey(Config)] + end + ). + end_per_suite(_Config) -> ssh:stop(). diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl index 4991816850..733414e23a 100644 --- a/lib/ssh/test/ssh_basic_SUITE.erl +++ b/lib/ssh/test/ssh_basic_SUITE.erl @@ -25,6 +25,7 @@ -include_lib("common_test/include/ct.hrl"). -include_lib("kernel/include/inet.hrl"). -include_lib("kernel/include/file.hrl"). +-include("ssh_test_lib.hrl"). %% Note: This directive should only be used in test suites. %%-compile(export_all). @@ -130,7 +131,7 @@ basic_tests() -> %%-------------------------------------------------------------------- init_per_suite(Config) -> - Config. + ?CHECK_CRYPTO(Config). end_per_suite(_Config) -> ssh:stop(). diff --git a/lib/ssh/test/ssh_connection_SUITE.erl b/lib/ssh/test/ssh_connection_SUITE.erl index c9a321fbbd..bcf3b01824 100644 --- a/lib/ssh/test/ssh_connection_SUITE.erl +++ b/lib/ssh/test/ssh_connection_SUITE.erl @@ -43,11 +43,15 @@ suite() -> all() -> [ {group, openssh}, + small_interrupted_send, interrupted_send, start_shell, start_shell_exec, start_shell_exec_fun, start_shell_sock_exec_fun, + start_shell_sock_daemon_exec, + connect_sock_not_tcp, + daemon_sock_not_tcp, gracefull_invalid_version, gracefull_invalid_start, gracefull_invalid_long_start, @@ -57,13 +61,11 @@ all() -> max_channels_option ]. groups() -> - [{openssh, [], payload() ++ ptty()}]. + [{openssh, [], payload() ++ ptty() ++ sock()}]. payload() -> [simple_exec, simple_exec_sock, - connect_sock_not_tcp, - connect_sock_not_passive, small_cat, big_cat, send_after_exit]. @@ -73,9 +75,14 @@ ptty() -> ptty_alloc, ptty_alloc_pixel]. +sock() -> + [connect_sock_not_passive, + daemon_sock_not_passive + ]. + %%-------------------------------------------------------------------- init_per_suite(Config) -> - Config. + ?CHECK_CRYPTO(Config). end_per_suite(Config) -> Config. @@ -118,7 +125,7 @@ simple_exec(Config) when is_list(Config) -> do_simple_exec(ConnectionRef). -simple_exec_sock(Config) -> +simple_exec_sock(_Config) -> {ok, Sock} = gen_tcp:connect("localhost", ?SSH_DEFAULT_PORT, [{active,false}]), {ok, ConnectionRef} = ssh:connect(Sock, [{silently_accept_hosts, true}, {user_interaction, false}]), @@ -159,18 +166,30 @@ do_simple_exec(ConnectionRef) -> end. %%-------------------------------------------------------------------- -connect_sock_not_tcp(Config) -> +connect_sock_not_tcp(_Config) -> {ok,Sock} = gen_udp:open(0, []), {error, not_tcp_socket} = ssh:connect(Sock, []), gen_udp:close(Sock). %%-------------------------------------------------------------------- -connect_sock_not_passive(Config) -> +daemon_sock_not_tcp(_Config) -> + {ok,Sock} = gen_udp:open(0, []), + {error, not_tcp_socket} = ssh:daemon(Sock), + gen_udp:close(Sock). + +%%-------------------------------------------------------------------- +connect_sock_not_passive(_Config) -> {ok,Sock} = gen_tcp:connect("localhost", ?SSH_DEFAULT_PORT, []), {error, not_passive_mode} = ssh:connect(Sock, []), gen_tcp:close(Sock). %%-------------------------------------------------------------------- +daemon_sock_not_passive(_Config) -> + {ok,Sock} = gen_tcp:connect("localhost", ?SSH_DEFAULT_PORT, []), + {error, not_passive_mode} = ssh:daemon(Sock), + gen_tcp:close(Sock). + +%%-------------------------------------------------------------------- small_cat() -> [{doc, "Use 'cat' to echo small data block back to us."}]. @@ -343,44 +362,96 @@ ptty_alloc_pixel(Config) when is_list(Config) -> ssh:close(ConnectionRef). %%-------------------------------------------------------------------- +small_interrupted_send(Config) -> + K = 1024, + M = K*K, + do_interrupted_send(Config, 10*M, 4*K). interrupted_send(Config) -> + M = 1024*1024, + do_interrupted_send(Config, 10*M, 4*M). + +do_interrupted_send(Config, SendSize, EchoSize) -> PrivDir = proplists:get_value(priv_dir, Config), UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth file:make_dir(UserDir), SysDir = proplists:get_value(data_dir, Config), + EchoSS_spec = {ssh_echo_server, [EchoSize,[{dbg,true}]]}, {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir}, {user_dir, UserDir}, {password, "morot"}, - {subsystems, [{"echo_n", {ssh_echo_server, [4000000]}}]}]), - + {subsystems, [{"echo_n",EchoSS_spec}]}]), + + ct:log("connect", []), ConnectionRef = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true}, {user, "foo"}, {password, "morot"}, {user_interaction, false}, {user_dir, UserDir}]), - - {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity), - - success = ssh_connection:subsystem(ConnectionRef, ChannelId, "echo_n", infinity), - - %% build 10MB binary - Data = << <<X:32>> || X <- lists:seq(1,2500000)>>, - - %% expect remote end to send us 4MB back - <<ExpectedData:4000000/binary, _/binary>> = Data, - - %% pre-adjust receive window so the other end doesn't block - ssh_connection:adjust_window(ConnectionRef, ChannelId, size(ExpectedData) + 1), - - case ssh_connection:send(ConnectionRef, ChannelId, Data, 10000) of - {error, closed} -> - ok; - Msg -> - ct:fail({expected,{error,closed}, got, Msg}) - end, - receive_data(ExpectedData, ConnectionRef, ChannelId), - ssh:close(ConnectionRef), - ssh:stop_daemon(Pid). + ct:log("connected", []), + + %% build big binary + Data = << <<X:32>> || X <- lists:seq(1,SendSize div 4)>>, + + %% expect remote end to send us EchoSize back + <<ExpectedData:EchoSize/binary, _/binary>> = Data, + + %% Spawn listener. Otherwise we could get a deadlock due to filled buffers + Parent = self(), + ResultPid = spawn( + fun() -> + ct:log("open channel",[]), + {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity), + ct:log("start subsystem", []), + case ssh_connection:subsystem(ConnectionRef, ChannelId, "echo_n", infinity) of + success -> + Parent ! {self(), channelId, ChannelId}, + + Result = + try collect_data(ConnectionRef, ChannelId) + of + ExpectedData -> + ok; + _ -> + {fail,"unexpected result"} + catch + Class:Exception -> + {fail, io_lib:format("Exception ~p:~p",[Class,Exception])} + end, + Parent ! {self(), Result}; + Other -> + Parent ! {self(), channelId, error, Other} + end + end), + + receive + {ResultPid, channelId, ChannelId} -> + %% pre-adjust receive window so the other end doesn't block + ct:log("adjust window", []), + ssh_connection:adjust_window(ConnectionRef, ChannelId, size(ExpectedData) + 1), + + ct:log("going to send ~p bytes", [size(Data)]), + case ssh_connection:send(ConnectionRef, ChannelId, Data, 30000) of + {error, closed} -> + ct:log("{error,closed} - That's what we expect :)", []), + ok; + Msg -> + ct:log("Got ~p - that's bad, very bad indeed",[Msg]), + ct:fail({expected,{error,closed}, got, Msg}) + end, + ct:log("going to check the result (if it is available)", []), + receive + {ResultPid, Result} -> + ct:log("Got result: ~p", [Result]), + ssh:close(ConnectionRef), + ssh:stop_daemon(Pid), + Result + end; + + {ResultPid, channelId, error, Other} -> + ssh:close(ConnectionRef), + ssh:stop_daemon(Pid), + {fail, io_lib:format("ssh_connection:subsystem: ~p",[Other])} + end. %%-------------------------------------------------------------------- start_shell() -> @@ -520,7 +591,44 @@ start_shell_sock_exec_fun(Config) when is_list(Config) -> ssh:stop_daemon(Pid). %%-------------------------------------------------------------------- +start_shell_sock_daemon_exec(Config) -> + PrivDir = proplists:get_value(priv_dir, Config), + UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth + file:make_dir(UserDir), + SysDir = proplists:get_value(data_dir, Config), + + {ok,Sl} = gen_tcp:listen(0, [{active,false}]), + {ok,{_IP,Port}} = inet:sockname(Sl), % _IP is likely to be {0,0,0,0}. Win don't like... + + spawn_link(fun() -> + {ok,Ss} = gen_tcp:connect("localhost", Port, [{active,false}]), + {ok, _Pid} = ssh:daemon(Ss, [{system_dir, SysDir}, + {user_dir, UserDir}, + {password, "morot"}, + {exec, fun ssh_exec/1}]) + end), + {ok,Sc} = gen_tcp:accept(Sl), + {ok,ConnectionRef} = ssh:connect(Sc, [{silently_accept_hosts, true}, + {user, "foo"}, + {password, "morot"}, + {user_interaction, true}, + {user_dir, UserDir}]), + + {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity), + + success = ssh_connection:exec(ConnectionRef, ChannelId0, + "testing", infinity), + receive + {ssh_cm, ConnectionRef, {data, _ChannelId, 0, <<"testing\r\n">>}} -> + ok + after 5000 -> + ct:fail("Exec Timeout") + end, + + ssh:close(ConnectionRef). + +%%-------------------------------------------------------------------- gracefull_invalid_version(Config) when is_list(Config) -> PrivDir = proplists:get_value(priv_dir, Config), UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth @@ -801,20 +909,36 @@ big_cat_rx(ConnectionRef, ChannelId, Acc) -> timeout end. -receive_data(ExpectedData, ConnectionRef, ChannelId) -> - ExpectedData = collect_data(ConnectionRef, ChannelId). - collect_data(ConnectionRef, ChannelId) -> - collect_data(ConnectionRef, ChannelId, []). + ct:log("Listener ~p running! ConnectionRef=~p, ChannelId=~p",[self(),ConnectionRef,ChannelId]), + collect_data(ConnectionRef, ChannelId, [], 0). -collect_data(ConnectionRef, ChannelId, Acc) -> +collect_data(ConnectionRef, ChannelId, Acc, Sum) -> + TO = 5000, receive - {ssh_cm, ConnectionRef, {data, ChannelId, 0, Data}} -> - collect_data(ConnectionRef, ChannelId, [Data | Acc]); + {ssh_cm, ConnectionRef, {data, ChannelId, 0, Data}} when is_binary(Data) -> + ct:log("collect_data: received ~p bytes. total ~p bytes",[size(Data),Sum+size(Data)]), + collect_data(ConnectionRef, ChannelId, [Data | Acc], Sum+size(Data)); {ssh_cm, ConnectionRef, {eof, ChannelId}} -> - iolist_to_binary(lists:reverse(Acc)) - after 5000 -> - timeout + try + iolist_to_binary(lists:reverse(Acc)) + of + Bin -> + ct:log("collect_data: received eof.~nGot in total ~p bytes",[size(Bin)]), + Bin + catch + C:E -> + ct:log("collect_data: received eof.~nAcc is strange...~nException=~p:~p~nAcc=~p", + [C,E,Acc]), + {error,{C,E}} + end; + Msg -> + ct:log("collect_data: ***** unexpected message *****~n~p",[Msg]), + collect_data(ConnectionRef, ChannelId, Acc, Sum) + + after TO -> + ct:log("collect_data: ----- Nothing received for ~p seconds -----~n",[]), + collect_data(ConnectionRef, ChannelId, Acc, Sum) end. %%%------------------------------------------------------------------- diff --git a/lib/ssh/test/ssh_echo_server.erl b/lib/ssh/test/ssh_echo_server.erl index ed9bbe1b67..5387d21efd 100644 --- a/lib/ssh/test/ssh_echo_server.erl +++ b/lib/ssh/test/ssh_echo_server.erl @@ -26,15 +26,29 @@ -record(state, { n, id, - cm + cm, + dbg = false }). -export([init/1, handle_msg/2, handle_ssh_msg/2, terminate/2]). +-define(DBG(State,Fmt,Args), + case State#state.dbg of + true -> ct:log("~p:~p ~p "++Fmt, [?MODULE,?LINE,self()|Args]); + false -> ok + end). + + init([N]) -> - ct:pal("Echo server: ~p",[self()]), - {ok, #state{n = N}}. + {ok, #state{n = N}}; +init([N,Opts]) -> + State = #state{n = N, + dbg = proplists:get_value(dbg,Opts,false) + }, + ?DBG(State, "init([~p])",[N]), + {ok, State}. handle_msg({ssh_channel_up, ChannelId, ConnectionManager}, State) -> + ?DBG(State, "ssh_channel_up Cid=~p ConnMngr=~p",[ChannelId,ConnectionManager]), {ok, State#state{id = ChannelId, cm = ConnectionManager}}. @@ -42,32 +56,39 @@ handle_ssh_msg({ssh_cm, CM, {data, ChannelId, 0, Data}}, #state{n = N} = State) M = N - size(Data), case M > 0 of true -> + ?DBG(State, "ssh_cm data Cid=~p size(Data)=~p M=~p",[ChannelId,size(Data),M]), ssh_connection:send(CM, ChannelId, Data), {ok, State#state{n = M}}; false -> <<SendData:N/binary, _/binary>> = Data, + ?DBG(State, "ssh_cm data Cid=~p size(Data)=~p M=~p size(SendData)=~p~nSend eof",[ChannelId,size(Data),M,size(SendData)]), ssh_connection:send(CM, ChannelId, SendData), ssh_connection:send_eof(CM, ChannelId), {stop, ChannelId, State} end; handle_ssh_msg({ssh_cm, _ConnectionManager, {data, _ChannelId, 1, Data}}, State) -> + ?DBG(State, "stderr: ~p",[Data]), error_logger:format(standard_error, " ~p~n", [binary_to_list(Data)]), {ok, State}; handle_ssh_msg({ssh_cm, _ConnectionManager, {eof, _ChannelId}}, State) -> + ?DBG(State, "{eof ~p}",[_ChannelId]), {ok, State}; -handle_ssh_msg({ssh_cm, _, {signal, _, _}}, State) -> +handle_ssh_msg({ssh_cm, _, _Sig={signal, _, _}}, State) -> %% Ignore signals according to RFC 4254 section 6.9. + ?DBG(State, "~p",[_Sig]), {ok, State}; -handle_ssh_msg({ssh_cm, _, {exit_signal, ChannelId, _, _Error, _}}, - State) -> +handle_ssh_msg({ssh_cm, _, _Sig={exit_signal, ChannelId, _, _Error, _}}, State) -> + ?DBG(State, "~p",[_Sig]), {stop, ChannelId, State}; -handle_ssh_msg({ssh_cm, _, {exit_status, ChannelId, _Status}}, State) -> +handle_ssh_msg({ssh_cm, _, _Sig={exit_status, ChannelId, _Status}}, State) -> + ?DBG(State, "~p",[_Sig]), {stop, ChannelId, State}. terminate(_Reason, _State) -> + ?DBG(_State, "terminate ~p",[_Reason]), ok. diff --git a/lib/ssh/test/ssh_options_SUITE.erl b/lib/ssh/test/ssh_options_SUITE.erl index d1e3d6cb0e..61883c0647 100644 --- a/lib/ssh/test/ssh_options_SUITE.erl +++ b/lib/ssh/test/ssh_options_SUITE.erl @@ -27,7 +27,7 @@ -include_lib("common_test/include/ct.hrl"). -include_lib("kernel/include/file.hrl"). - +-include("ssh_test_lib.hrl"). %%% Test cases -export([connectfun_disconnectfun_client/1, @@ -126,7 +126,7 @@ groups() -> %%-------------------------------------------------------------------- init_per_suite(Config) -> - Config. + ?CHECK_CRYPTO(Config). end_per_suite(_Config) -> ssh:stop(). diff --git a/lib/ssh/test/ssh_protocol_SUITE.erl b/lib/ssh/test/ssh_protocol_SUITE.erl index 41faf951e1..4fac1f718a 100644 --- a/lib/ssh/test/ssh_protocol_SUITE.erl +++ b/lib/ssh/test/ssh_protocol_SUITE.erl @@ -26,6 +26,7 @@ -include_lib("ssh/src/ssh.hrl"). % ?UINT32, ?BYTE, #ssh{} ... -include_lib("ssh/src/ssh_transport.hrl"). -include_lib("ssh/src/ssh_auth.hrl"). +-include("ssh_test_lib.hrl"). %% Note: This directive should only be used in test suites. -compile(export_all). @@ -87,7 +88,7 @@ groups() -> init_per_suite(Config) -> - start_std_daemon( setup_dirs( start_apps(Config))). + ?CHECK_CRYPTO(start_std_daemon( setup_dirs( start_apps(Config)))). end_per_suite(Config) -> stop_apps(Config). diff --git a/lib/ssh/test/ssh_renegotiate_SUITE.erl b/lib/ssh/test/ssh_renegotiate_SUITE.erl index 300816276a..b10ec3707f 100644 --- a/lib/ssh/test/ssh_renegotiate_SUITE.erl +++ b/lib/ssh/test/ssh_renegotiate_SUITE.erl @@ -21,6 +21,7 @@ -module(ssh_renegotiate_SUITE). -include_lib("common_test/include/ct.hrl"). +-include("ssh_test_lib.hrl"). %% Note: This directive should only be used in test suites. -compile(export_all). @@ -45,7 +46,7 @@ tests() -> [rekey, rekey_limit, renegotiate1, renegotiate2]. %%-------------------------------------------------------------------- init_per_suite(Config) -> - Config. + ?CHECK_CRYPTO(Config). end_per_suite(_Config) -> ssh:stop(). diff --git a/lib/ssh/test/ssh_sftp_SUITE.erl b/lib/ssh/test/ssh_sftp_SUITE.erl index 4d40b4647c..19ad81e7da 100644 --- a/lib/ssh/test/ssh_sftp_SUITE.erl +++ b/lib/ssh/test/ssh_sftp_SUITE.erl @@ -26,7 +26,7 @@ -include_lib("common_test/include/ct.hrl"). -include_lib("kernel/include/file.hrl"). - +-include("ssh_test_lib.hrl"). % Default timetrap timeout -define(default_timeout, ?t:minutes(1)). @@ -45,10 +45,13 @@ all() -> init_per_suite(Config) -> - ct:log("file:native_name_encoding() = ~p,~nio:getopts() = ~p", - [file:native_name_encoding(),io:getopts()]), - ssh:start(), - Config. + ?CHECK_CRYPTO( + begin + ct:log("file:native_name_encoding() = ~p,~nio:getopts() = ~p", + [file:native_name_encoding(),io:getopts()]), + ssh:start(), + Config + end). end_per_suite(_onfig) -> ssh:stop(). @@ -673,6 +676,7 @@ start_channel_sock(Config) -> %% Test that the socket is closed when the Connection closes ok = ssh:close(Conn), + timer:sleep(400), %% Until the stop sequence is fixed {error,einval} = inet:getopts(Sock, [active]), ok. diff --git a/lib/ssh/test/ssh_sftpd_SUITE.erl b/lib/ssh/test/ssh_sftpd_SUITE.erl index 4a69fd36b3..52a26110c4 100644 --- a/lib/ssh/test/ssh_sftpd_SUITE.erl +++ b/lib/ssh/test/ssh_sftpd_SUITE.erl @@ -28,6 +28,7 @@ -include_lib("kernel/include/file.hrl"). -include("ssh_xfer.hrl"). -include("ssh.hrl"). +-include("ssh_test_lib.hrl"). -define(USER, "Alladin"). -define(PASSWD, "Sesame"). @@ -72,14 +73,17 @@ groups() -> %%-------------------------------------------------------------------- init_per_suite(Config) -> - DataDir = proplists:get_value(data_dir, Config), - PrivDir = proplists:get_value(priv_dir, Config), - ssh_test_lib:setup_dsa(DataDir, PrivDir), - %% to make sure we don't use public-key-auth - %% this should be tested by other test suites - UserDir = filename:join(proplists:get_value(priv_dir, Config), nopubkey), - file:make_dir(UserDir), - Config. + ?CHECK_CRYPTO( + begin + DataDir = proplists:get_value(data_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), + ssh_test_lib:setup_dsa(DataDir, PrivDir), + %% to make sure we don't use public-key-auth + %% this should be tested by other test suites + UserDir = filename:join(proplists:get_value(priv_dir, Config), nopubkey), + file:make_dir(UserDir), + Config + end). end_per_suite(Config) -> SysDir = proplists:get_value(priv_dir, Config), diff --git a/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl b/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl index 75b5090c2b..56a33d6349 100644 --- a/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl +++ b/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl @@ -26,6 +26,7 @@ -include_lib("common_test/include/ct.hrl"). -include_lib("kernel/include/file.hrl"). +-include("ssh_test_lib.hrl"). -define(USER, "Alladin"). -define(PASSWD, "Sesame"). @@ -53,17 +54,20 @@ groups() -> %%-------------------------------------------------------------------- init_per_suite(Config) -> - catch ssh:stop(), - DataDir = proplists:get_value(data_dir, Config), - PrivDir = proplists:get_value(priv_dir, Config), - FileAlt = filename:join(DataDir, "ssh_sftpd_file_alt.erl"), - c:c(FileAlt), - FileName = filename:join(DataDir, "test.txt"), - {ok, FileInfo} = file:read_file_info(FileName), - ok = file:write_file_info(FileName, - FileInfo#file_info{mode = 8#400}), - ssh_test_lib:setup_dsa(DataDir, PrivDir), - Config. + ?CHECK_CRYPTO( + begin + catch ssh:stop(), + DataDir = proplists:get_value(data_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), + FileAlt = filename:join(DataDir, "ssh_sftpd_file_alt.erl"), + c:c(FileAlt), + FileName = filename:join(DataDir, "test.txt"), + {ok, FileInfo} = file:read_file_info(FileName), + ok = file:write_file_info(FileName, + FileInfo#file_info{mode = 8#400}), + ssh_test_lib:setup_dsa(DataDir, PrivDir), + Config + end). end_per_suite(Config) -> UserDir = filename:join(proplists:get_value(priv_dir, Config), nopubkey), diff --git a/lib/ssh/test/ssh_sup_SUITE.erl b/lib/ssh/test/ssh_sup_SUITE.erl index 574564f6e9..ff53e1c4c6 100644 --- a/lib/ssh/test/ssh_sup_SUITE.erl +++ b/lib/ssh/test/ssh_sup_SUITE.erl @@ -53,11 +53,14 @@ end_per_group(_GroupName, Config) -> Config. init_per_suite(Config) -> - Port = ssh_test_lib:inet_port(node()), - PrivDir = proplists:get_value(priv_dir, Config), - UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth - file:make_dir(UserDir), - [{userdir, UserDir},{port, Port}, {host, "localhost"}, {host_ip, any} | Config]. + ?CHECK_CRYPTO( + begin + Port = ssh_test_lib:inet_port(node()), + PrivDir = proplists:get_value(priv_dir, Config), + UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth + file:make_dir(UserDir), + [{userdir, UserDir},{port, Port}, {host, "localhost"}, {host_ip, any} | Config] + end). end_per_suite(_) -> ok. diff --git a/lib/ssh/test/ssh_test_lib.hrl b/lib/ssh/test/ssh_test_lib.hrl index 7cb7edeaa8..54c93b7e87 100644 --- a/lib/ssh/test/ssh_test_lib.hrl +++ b/lib/ssh/test/ssh_test_lib.hrl @@ -1,4 +1,14 @@ %%------------------------------------------------------------------------- +%% Check for usable crypt +%%------------------------------------------------------------------------- +-define(CHECK_CRYPTO(Available), + try crypto:start() + of _ -> Available + catch _:_ -> {skip, "Can't start crypto"} + end + ). + +%%------------------------------------------------------------------------- %% Help macro %%------------------------------------------------------------------------- -define(wait_match(Pattern, FunctionCall, Bind, Timeout, Ntries), diff --git a/lib/ssh/test/ssh_to_openssh_SUITE.erl b/lib/ssh/test/ssh_to_openssh_SUITE.erl index f96a2cc62b..a914938c41 100644 --- a/lib/ssh/test/ssh_to_openssh_SUITE.erl +++ b/lib/ssh/test/ssh_to_openssh_SUITE.erl @@ -22,6 +22,7 @@ -module(ssh_to_openssh_SUITE). -include_lib("common_test/include/ct.hrl"). +-include("ssh_test_lib.hrl"). %% Note: This directive should only be used in test suites. -compile(export_all). @@ -62,12 +63,14 @@ groups() -> ]. init_per_suite(Config) -> - case gen_tcp:connect("localhost", 22, []) of - {error,econnrefused} -> - {skip,"No openssh deamon"}; - _ -> - ssh_test_lib:openssh_sanity_check(Config) - end. + ?CHECK_CRYPTO( + case gen_tcp:connect("localhost", 22, []) of + {error,econnrefused} -> + {skip,"No openssh deamon"}; + _ -> + ssh_test_lib:openssh_sanity_check(Config) + end + ). end_per_suite(_Config) -> ok. diff --git a/lib/ssh/test/ssh_upgrade_SUITE.erl b/lib/ssh/test/ssh_upgrade_SUITE.erl index 9d9b2b78fb..b5b27c369a 100644 --- a/lib/ssh/test/ssh_upgrade_SUITE.erl +++ b/lib/ssh/test/ssh_upgrade_SUITE.erl @@ -23,6 +23,7 @@ -compile(export_all). -include_lib("common_test/include/ct.hrl"). +-include("ssh_test_lib.hrl"). -record(state, { config, @@ -48,13 +49,15 @@ all() -> ]. init_per_suite(Config0) -> - case ct_release_test:init(Config0) of - {skip, Reason} -> - {skip, Reason}; - Config -> - ssh:start(), - Config - end. + ?CHECK_CRYPTO( + case ct_release_test:init(Config0) of + {skip, Reason} -> + {skip, Reason}; + Config -> + ssh:start(), + Config + end + ). end_per_suite(Config) -> ct_release_test:cleanup(Config), diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml index 33ece8f769..bed82cdb91 100644 --- a/lib/ssl/doc/src/ssl.xml +++ b/lib/ssl/doc/src/ssl.xml @@ -423,7 +423,7 @@ fun(srp, Username :: string(), UserState :: term()) -> <tag><c>{beast_mitigation, one_n_minus_one | zero_n | disabled}</c></tag> <item><p>Affects SSL-3.0 and TLS-1.0 connections only. Used to change the BEAST mitigation strategy to interoperate with legacy software. - Defaults to <c>one_n_minus_one</c></p>. + Defaults to <c>one_n_minus_one</c>.</p> <p><c>one_n_minus_one</c> - Perform 1/n-1 BEAST mitigation.</p> @@ -706,6 +706,12 @@ fun(srp, Username :: string(), UserState :: term()) -> client certificate is requested. For more details see the <seealso marker="#client_signature_algs">corresponding client option</seealso>. </p> </item> + <tag><c>{v2_hello_compatible, boolean()}</c></tag> + <item>If true, the server accepts clients that send hello messages on SSL-2.0 format but offers + supported SSL/TLS versions. Defaults to false, that is the server will not interoperate with clients that + offers SSL-2.0. + </item> + </taglist> </section> @@ -895,6 +901,23 @@ fun(srp, Username :: string(), UserState :: term()) -> </func> <func> + <name>getstat(Socket) -> + {ok, OptionValues} | {error, inet:posix()}</name> + <name>getstat(Socket, OptionNames) -> + {ok, OptionValues} | {error, inet:posix()}</name> + <fsummary>Get one or more statistic options for a socket</fsummary> + <type> + <v>Socket = sslsocket()</v> + <v>OptionNames = [atom()]</v> + <v>OptionValues = [{inet:stat_option(), integer()}]</v> + </type> + <desc> + <p>Gets one or more statistic options for the underlying TCP socket.</p> + <p>See inet:getstat/2 for statistic options description.</p> + </desc> + </func> + + <func> <name>listen(Port, Options) -> {ok, ListenSocket} | {error, Reason}</name> <fsummary>Creates an SSL listen socket.</fsummary> diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index 33d5c1c6d6..d2aeb3258f 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -34,7 +34,8 @@ listen/2, transport_accept/1, transport_accept/2, ssl_accept/1, ssl_accept/2, ssl_accept/3, controlling_process/2, peername/1, peercert/1, sockname/1, - close/1, close/2, shutdown/2, recv/2, recv/3, send/2, getopts/2, setopts/2 + close/1, close/2, shutdown/2, recv/2, recv/3, send/2, + getopts/2, setopts/2, getstat/1, getstat/2 ]). %% SSL/TLS protocol handling -export([cipher_suites/0, cipher_suites/1, @@ -469,6 +470,32 @@ setopts(#sslsocket{}, Options) -> {error, {options,{not_a_proplist, Options}}}. %%--------------------------------------------------------------- +-spec getstat(Socket) -> + {ok, OptionValues} | {error, inet:posix()} when + Socket :: #sslsocket{}, + OptionValues :: [{inet:stat_option(), integer()}]. +%% +%% Description: Get all statistic options for a socket. +%%-------------------------------------------------------------------- +getstat(Socket) -> + getstat(Socket, inet:stats()). + +%%--------------------------------------------------------------- +-spec getstat(Socket, Options) -> + {ok, OptionValues} | {error, inet:posix()} when + Socket :: #sslsocket{}, + Options :: [inet:stat_option()], + OptionValues :: [{inet:stat_option(), integer()}]. +%% +%% Description: Get one or more statistic options for a socket. +%%-------------------------------------------------------------------- +getstat(#sslsocket{pid = {Listen, #config{transport_info = {Transport, _, _, _}}}}, Options) when is_port(Listen), is_list(Options) -> + ssl_socket:getstat(Transport, Listen, Options); + +getstat(#sslsocket{pid = Pid, fd = {Transport, Socket, _, _}}, Options) when is_pid(Pid), is_list(Options) -> + ssl_socket:getstat(Transport, Socket, Options). + +%%--------------------------------------------------------------- -spec shutdown(#sslsocket{}, read | write | read_write) -> ok | {error, reason()}. %% %% Description: Same as gen_tcp:shutdown/2 @@ -732,7 +759,8 @@ handle_options(Opts0, Role) -> false, Role)), client, Role), crl_check = handle_option(crl_check, Opts, false), - crl_cache = handle_option(crl_cache, Opts, {ssl_crl_cache, {internal, []}}) + crl_cache = handle_option(crl_cache, Opts, {ssl_crl_cache, {internal, []}}), + v2_hello_compatible = handle_option(v2_hello_compatible, Opts, false) }, CbInfo = proplists:get_value(cb_info, Opts, {gen_tcp, tcp, tcp_closed, tcp_error}), @@ -747,7 +775,7 @@ handle_options(Opts0, Role) -> alpn_preferred_protocols, next_protocols_advertised, client_preferred_next_protocols, log_alert, server_name_indication, honor_cipher_order, padding_check, crl_check, crl_cache, - fallback, signature_algs, beast_mitigation], + fallback, signature_algs, beast_mitigation, v2_hello_compatible], SockOpts = lists:foldl(fun(Key, PropList) -> proplists:delete(Key, PropList) @@ -991,6 +1019,8 @@ validate_option(beast_mitigation, Value) when Value == one_n_minus_one orelse Value == zero_n orelse Value == disabled -> Value; +validate_option(v2_hello_compatible, Value) when is_boolean(Value) -> + Value; validate_option(Opt, Value) -> throw({error, {options, {Opt, Value}}}). diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index b45c5c8fc6..90e0810241 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -465,6 +465,14 @@ certify(internal, #certificate{asn1_certificates = []}, Connection:next_record(State0#state{client_certificate_requested = false}), Connection:next_event(certify, Record, State); +certify(internal, #certificate{}, + #state{role = server, + negotiated_version = Version, + ssl_options = #ssl_options{verify = verify_none}} = + State, Connection) -> + Alert = ?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE, unrequested_certificate), + Connection:handle_own_alert(Alert, Version, certify, State); + certify(internal, #certificate{} = Cert, #state{negotiated_version = Version, role = Role, diff --git a/lib/ssl/src/ssl_crl.erl b/lib/ssl/src/ssl_crl.erl index faf5007b16..d9f21e04ac 100644 --- a/lib/ssl/src/ssl_crl.erl +++ b/lib/ssl/src/ssl_crl.erl @@ -39,13 +39,12 @@ trusted_cert_and_path(CRL, {SerialNumber, Issuer},{Db, DbRef} = DbHandle) -> end; trusted_cert_and_path(CRL, issuer_not_found, {Db, DbRef} = DbHandle) -> - try find_issuer(CRL, DbHandle) of - OtpCert -> + case find_issuer(CRL, DbHandle) of + {ok, OtpCert} -> {ok, Root, Chain} = ssl_certificate:certificate_chain(OtpCert, Db, DbRef), - {ok, Root, lists:reverse(Chain)} - catch - throw:_ -> - {error, issuer_not_found} + {ok, Root, lists:reverse(Chain)}; + {error, issuer_not_found} -> + {ok, unknown_crl_ca, []} end. find_issuer(CRL, {Db,_}) -> @@ -61,11 +60,10 @@ find_issuer(CRL, {Db,_}) -> issuer_not_found -> {error, issuer_not_found} catch - {ok, IssuerCert} -> - IssuerCert + {ok, _} = Result -> + Result end. - verify_crl_issuer(CRL, ErlCertCandidate, Issuer, NotIssuer) -> TBSCert = ErlCertCandidate#'OTPCertificate'.tbsCertificate, case public_key:pkix_normalize_name(TBSCert#'OTPTBSCertificate'.subject) of diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl index dddcbdeeda..c19c1787ff 100644 --- a/lib/ssl/src/ssl_internal.hrl +++ b/lib/ssl/src/ssl_internal.hrl @@ -139,7 +139,8 @@ fallback = false :: boolean(), crl_check :: boolean() | peer | best_effort, crl_cache, - signature_algs + signature_algs, + v2_hello_compatible :: boolean() }). -record(socket_options, diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl index 60b4fbe995..c7dcbaabe9 100644 --- a/lib/ssl/src/ssl_manager.erl +++ b/lib/ssl/src/ssl_manager.erl @@ -67,6 +67,7 @@ -define(CLEAN_SESSION_DB, 60000). -define(CLEAN_CERT_DB, 500). -define(DEFAULT_MAX_SESSION_CACHE, 1000). +-define(LOAD_MITIGATION, 10). %%==================================================================== %% API @@ -196,10 +197,12 @@ register_session(Port, Session) -> %%-------------------------------------------------------------------- -spec invalidate_session(host(), inet:port_number(), #session{}) -> ok. invalidate_session(Host, Port, Session) -> + load_mitigation(), cast({invalidate_session, Host, Port, Session}). -spec invalidate_session(inet:port_number(), #session{}) -> ok. invalidate_session(Port, Session) -> + load_mitigation(), cast({invalidate_session, Port, Session}). -spec invalidate_pem(File::binary()) -> ok. @@ -719,3 +722,11 @@ invalidate_session_cache(undefined, CacheCb, Cache) -> start_session_validator(Cache, CacheCb, {invalidate_before, erlang:monotonic_time()}, undefined); invalidate_session_cache(Pid, _CacheCb, _Cache) -> Pid. + +load_mitigation() -> + MSec = rand:uniform(?LOAD_MITIGATION), + receive + after + MSec -> + continue + end. diff --git a/lib/ssl/src/ssl_socket.erl b/lib/ssl/src/ssl_socket.erl index 95a70a4602..b2aea2ba9c 100644 --- a/lib/ssl/src/ssl_socket.erl +++ b/lib/ssl/src/ssl_socket.erl @@ -24,7 +24,7 @@ -include("ssl_internal.hrl"). -include("ssl_api.hrl"). --export([socket/5, setopts/3, getopts/3, peername/2, sockname/2, port/2]). +-export([socket/5, setopts/3, getopts/3, getstat/3, peername/2, sockname/2, port/2]). -export([emulated_options/0, internal_inet_values/0, default_inet_values/0, init/1, start_link/3, terminate/2, inherit_tracker/3, get_emulated_opts/1, set_emulated_opts/2, get_all_opts/1, handle_call/3, handle_cast/2, @@ -74,6 +74,11 @@ getopts(gen_tcp, Socket, Options) -> getopts(Transport, Socket, Options) -> Transport:getopts(Socket, Options). +getstat(gen_tcp, Socket, Options) -> + inet:getstat(Socket, Options); +getstat(Transport, Socket, Options) -> + Transport:getstat(Socket, Options). + peername(gen_tcp, Socket) -> inet:peername(Socket); peername(Transport, Socket) -> diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl index 56e516bce2..eaf2dd002d 100644 --- a/lib/ssl/src/tls_connection.erl +++ b/lib/ssl/src/tls_connection.erl @@ -399,9 +399,10 @@ handle_common_event(internal, #alert{} = Alert, StateName, handle_common_event(internal, #ssl_tls{type = ?HANDSHAKE, fragment = Data}, StateName, #state{protocol_buffers = #protocol_buffers{tls_handshake_buffer = Buf0} = Buffers, - negotiated_version = Version} = State0) -> - try - {Packets, Buf} = tls_handshake:get_tls_handshake(Version,Data,Buf0), + negotiated_version = Version, + ssl_options = Options} = State0) -> + try + {Packets, Buf} = tls_handshake:get_tls_handshake(Version,Data,Buf0, Options), State = State0#state{protocol_buffers = Buffers#protocol_buffers{tls_handshake_buffer = Buf}}, diff --git a/lib/ssl/src/tls_handshake.erl b/lib/ssl/src/tls_handshake.erl index 871eb970eb..397f963ad5 100644 --- a/lib/ssl/src/tls_handshake.erl +++ b/lib/ssl/src/tls_handshake.erl @@ -33,7 +33,7 @@ -include_lib("public_key/include/public_key.hrl"). -export([client_hello/8, hello/4, - get_tls_handshake/3, encode_handshake/2, decode_handshake/3]). + get_tls_handshake/4, encode_handshake/2, decode_handshake/4]). -type tls_handshake() :: #client_hello{} | ssl_handshake:ssl_handshake(). @@ -133,17 +133,17 @@ encode_handshake(Package, Version) -> [MsgType, ?uint24(Len), Bin]. %%-------------------------------------------------------------------- --spec get_tls_handshake(tls_record:tls_version(), binary(), binary() | iolist()) -> +-spec get_tls_handshake(tls_record:tls_version(), binary(), binary() | iolist(), #ssl_options{}) -> {[tls_handshake()], binary()}. %% %% Description: Given buffered and new data from ssl_record, collects %% and returns it as a list of handshake messages, also returns leftover %% data. %%-------------------------------------------------------------------- -get_tls_handshake(Version, Data, <<>>) -> - get_tls_handshake_aux(Version, Data, []); -get_tls_handshake(Version, Data, Buffer) -> - get_tls_handshake_aux(Version, list_to_binary([Buffer, Data]), []). +get_tls_handshake(Version, Data, <<>>, Options) -> + get_tls_handshake_aux(Version, Data, Options, []); +get_tls_handshake(Version, Data, Buffer, Options) -> + get_tls_handshake_aux(Version, list_to_binary([Buffer, Data]), Options, []). %%-------------------------------------------------------------------- %%% Internal functions @@ -184,24 +184,24 @@ handle_client_hello(Version, #client_hello{session_id = SugesstedId, end. get_tls_handshake_aux(Version, <<?BYTE(Type), ?UINT24(Length), - Body:Length/binary,Rest/binary>>, Acc) -> + Body:Length/binary,Rest/binary>>, #ssl_options{v2_hello_compatible = V2Hello} = Opts, Acc) -> Raw = <<?BYTE(Type), ?UINT24(Length), Body/binary>>, - Handshake = decode_handshake(Version, Type, Body), - get_tls_handshake_aux(Version, Rest, [{Handshake,Raw} | Acc]); -get_tls_handshake_aux(_Version, Data, Acc) -> + Handshake = decode_handshake(Version, Type, Body, V2Hello), + get_tls_handshake_aux(Version, Rest, Opts, [{Handshake,Raw} | Acc]); +get_tls_handshake_aux(_Version, Data, _, Acc) -> {lists:reverse(Acc), Data}. -decode_handshake(_, ?HELLO_REQUEST, <<>>) -> +decode_handshake(_, ?HELLO_REQUEST, <<>>, _) -> #hello_request{}; %% Client hello v2. %% The server must be able to receive such messages, from clients that %% are willing to use ssl v3 or higher, but have ssl v2 compatibility. decode_handshake(_Version, ?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), - ?UINT16(CSLength), ?UINT16(0), - ?UINT16(CDLength), - CipherSuites:CSLength/binary, - ChallengeData:CDLength/binary>>) -> + ?UINT16(CSLength), ?UINT16(0), + ?UINT16(CDLength), + CipherSuites:CSLength/binary, + ChallengeData:CDLength/binary>>, true) -> #client_hello{client_version = {Major, Minor}, random = ssl_v2:client_random(ChallengeData, CDLength), session_id = 0, @@ -209,12 +209,18 @@ decode_handshake(_Version, ?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), compression_methods = [?NULL], extensions = #hello_extensions{} }; +decode_handshake(_Version, ?CLIENT_HELLO, <<?BYTE(_), ?BYTE(_), + ?UINT16(CSLength), ?UINT16(0), + ?UINT16(CDLength), + _CipherSuites:CSLength/binary, + _ChallengeData:CDLength/binary>>, false) -> + throw(?ALERT_REC(?FATAL, ?PROTOCOL_VERSION, ssl_v2_client_hello_no_supported)); decode_handshake(_Version, ?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, - ?BYTE(SID_length), Session_ID:SID_length/binary, - ?UINT16(Cs_length), CipherSuites:Cs_length/binary, - ?BYTE(Cm_length), Comp_methods:Cm_length/binary, - Extensions/binary>>) -> - + ?BYTE(SID_length), Session_ID:SID_length/binary, + ?UINT16(Cs_length), CipherSuites:Cs_length/binary, + ?BYTE(Cm_length), Comp_methods:Cm_length/binary, + Extensions/binary>>, _) -> + DecodedExtensions = ssl_handshake:decode_hello_extensions({client, Extensions}), #client_hello{ @@ -226,7 +232,7 @@ decode_handshake(_Version, ?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:3 extensions = DecodedExtensions }; -decode_handshake(Version, Tag, Msg) -> +decode_handshake(Version, Tag, Msg, _) -> ssl_handshake:decode_handshake(Version, Tag, Msg). enc_handshake(#hello_request{}, _Version) -> diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl index 99f7c9b780..93b6ae66dc 100644 --- a/lib/ssl/test/ssl_basic_SUITE.erl +++ b/lib/ssl/test/ssl_basic_SUITE.erl @@ -150,6 +150,7 @@ api_tests() -> sockname, versions, controlling_process, + getstat, close_with_timeout, hibernate, hibernate_right_away, @@ -691,6 +692,75 @@ controlling_process(Config) when is_list(Config) -> 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) -> @@ -3059,7 +3129,7 @@ tls_ciphersuite_vs_version(Config) when is_list(Config) -> >>), {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), + ServerHello = tls_handshake:decode_handshake({RecMajor, RecMinor}, 2, HelloBin, false), case ServerHello of #server_hello{server_version = {3,0}, cipher_suite = <<0,57>>} -> ok; @@ -4592,4 +4662,6 @@ first_rsa_suite([{rsa, _, _, _} = Suite| _]) -> first_rsa_suite([_ | Rest]) -> first_rsa_suite(Rest). - +wait_for_send(Socket) -> + %% Make sure TLS process processed send message event + _ = ssl:connection_information(Socket). diff --git a/lib/ssl/test/ssl_handshake_SUITE.erl b/lib/ssl/test/ssl_handshake_SUITE.erl index 26e83413c1..a671e3e307 100644 --- a/lib/ssl/test/ssl_handshake_SUITE.erl +++ b/lib/ssl/test/ssl_handshake_SUITE.erl @@ -99,7 +99,8 @@ decode_hello_handshake(_Config) -> 16#70, 16#64, 16#79, 16#2f, 16#32>>, Version = {3, 0}, - {Records, _Buffer} = tls_handshake:get_tls_handshake(Version, HelloPacket, <<>>), + {Records, _Buffer} = tls_handshake:get_tls_handshake(Version, HelloPacket, <<>>, + #ssl_options{v2_hello_compatible = false}), {Hello, _Data} = hd(Records), #renegotiation_info{renegotiated_connection = <<0>>} diff --git a/lib/ssl/test/ssl_npn_hello_SUITE.erl b/lib/ssl/test/ssl_npn_hello_SUITE.erl index 533501e788..00eb9fee4f 100644 --- a/lib/ssl/test/ssl_npn_hello_SUITE.erl +++ b/lib/ssl/test/ssl_npn_hello_SUITE.erl @@ -57,7 +57,7 @@ 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), <<>>), + tls_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>, #ssl_options{}), NextProtocolNegotiation = (DecodedHandshakeMessage#client_hello.extensions)#hello_extensions.next_protocol_negotiation, NextProtocolNegotiation = undefined. %%-------------------------------------------------------------------- @@ -65,7 +65,7 @@ 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), <<>>), + tls_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>, #ssl_options{}), NextProtocolNegotiation = (DecodedHandshakeMessage#client_hello.extensions)#hello_extensions.next_protocol_negotiation, NextProtocolNegotiation = #next_protocol_negotiation{extension_data = <<>>}. %%-------------------------------------------------------------------- @@ -73,7 +73,7 @@ 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), <<>>), + tls_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>, #ssl_options{}), NextProtocolNegotiation = (DecodedHandshakeMessage#server_hello.extensions)#hello_extensions.next_protocol_negotiation, NextProtocolNegotiation = undefined. %%-------------------------------------------------------------------- @@ -81,7 +81,7 @@ 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), <<>>), + tls_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>, #ssl_options{}), NextProtocolNegotiation = (DecodedHandshakeMessage#server_hello.extensions)#hello_extensions.next_protocol_negotiation, ct:log("~p ~n", [NextProtocolNegotiation]), NextProtocolNegotiation = #next_protocol_negotiation{extension_data = <<6, "spdy/2">>}. diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl index 9df31a3381..d9a4657a79 100644 --- a/lib/ssl/test/ssl_to_openssl_SUITE.erl +++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl @@ -131,6 +131,13 @@ end_per_suite(_Config) -> ssl:stop(), application:stop(crypto). +init_per_group(basic, Config) -> + case ssl_test_lib:supports_ssl_tls_version(sslv2) of + true -> + [{v2_hello_compatible, true} | Config]; + false -> + [{v2_hello_compatible, false} | Config] + end; init_per_group(GroupName, Config) -> case ssl_test_lib:is_tls_version(GroupName) of true -> @@ -296,15 +303,18 @@ basic_erlang_server_openssl_client() -> basic_erlang_server_openssl_client(Config) when is_list(Config) -> process_flag(trap_exit, true), ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), + V2Compat = proplists:get_value(v2_hello_compatible, Config), {_, ServerNode, _} = ssl_test_lib:run_where(Config), Data = "From openssl to erlang", + ct:pal("v2_hello_compatible: ~p", [V2Compat]), Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, - {options, ServerOpts}]), + {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, + {options,[{v2_hello_compatible, V2Compat} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), Exe = "openssl", @@ -318,8 +328,8 @@ basic_erlang_server_openssl_client(Config) when is_list(Config) -> %% 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. + process_flag(trap_exit, false). + %%-------------------------------------------------------------------- erlang_client_openssl_server() -> [{doc,"Test erlang client with openssl server"}]. diff --git a/lib/stdlib/src/supervisor.erl b/lib/stdlib/src/supervisor.erl index a594c66fa7..c81e72689c 100644 --- a/lib/stdlib/src/supervisor.erl +++ b/lib/stdlib/src/supervisor.erl @@ -30,7 +30,7 @@ %% Internal exports -export([init/1, handle_call/3, handle_cast/2, handle_info/2, - terminate/2, code_change/3]). + terminate/2, code_change/3, format_status/2]). -export([try_again_restart/2]). %% For release_handler only @@ -264,8 +264,13 @@ cast(Supervisor, Req) -> get_callback_module(Pid) -> {status, _Pid, {module, _Mod}, [_PDict, _SysState, _Parent, _Dbg, Misc]} = sys:get_status(Pid), - [_Header, _Data, {data, [{"State", State}]}] = Misc, - State#state.module. + case lists:keyfind(supervisor, 1, Misc) of + {supervisor, [{"Callback", Mod}]} -> + Mod; + _ -> + [_Header, _Data, {data, [{"State", State}]} | _] = Misc, + State#state.module + end. %%% --------------------------------------------------- %%% @@ -1450,3 +1455,9 @@ report_progress(Child, SupName) -> Progress = [{supervisor, SupName}, {started, extract_child(Child)}], error_logger:info_report(progress, Progress). + +format_status(terminate, [_PDict, State]) -> + State; +format_status(_, [_PDict, State]) -> + [{data, [{"State", State}]}, + {supervisor, [{"Callback", State#state.module}]}]. diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl index 8c1c625676..40764a943d 100644 --- a/lib/stdlib/test/ets_SUITE.erl +++ b/lib/stdlib/test/ets_SUITE.erl @@ -215,7 +215,7 @@ memory_check_summary(_Config) -> receive {get_failed_memchecks, FailedMemchecks} -> ok end, io:format("Failed memchecks: ~p\n",[FailedMemchecks]), NoFailedMemchecks = length(FailedMemchecks), - if NoFailedMemchecks > 3 -> + if NoFailedMemchecks > 300 -> ct:fail("Too many failed (~p) memchecks", [NoFailedMemchecks]); true -> ok @@ -604,9 +604,9 @@ memory(Config) when is_list(Config) -> memory_do(Opts) -> L = [T1,T2,T3,T4] = fill_sets_int(1000,Opts), XR1 = case mem_mode(T1) of - {normal,_} -> {13836,13046,13046,13052}; %{13862,13072,13072,13078}; - {compressed,4} -> {11041,10251,10251,10252}; %{11067,10277,10277,10278}; - {compressed,8} -> {10050,9260,9260,9260} %{10076,9286,9286,9286} + {normal,_} -> {13836,13560,13560,13566}; %{13836,13046,13046,13052} + {compressed,4} -> {11041,10865,10865,10866}; %{11041,10251,10251,10252} + {compressed,8} -> {10050,9774,9774,9774} % {10050,9260,9260,9260} end, XRes1 = adjust_xmem(L, XR1), Res1 = {?S(T1),?S(T2),?S(T3),?S(T4)}, @@ -620,9 +620,9 @@ memory_do(Opts) -> end, L), XR2 = case mem_mode(T1) of - {normal,_} -> {13826,13037,13028,13034}; %{13852,13063,13054,13060}; - {compressed,4} -> {11031,10242,10233,10234}; %{11057,10268,10259,10260}; - {compressed,8} -> {10040,9251,9242,9242} %10066,9277,9268,9268} + {normal,_} -> {13826,13551,13542,13548}; %{13826,13037,13028,13034}; + {compressed,4} -> {11031,10856,10747,10748}; %{11031,10242,10233,10234}; + {compressed,8} -> {10040,9765,9756,9756} %{10040,9251,9242,9242} end, XRes2 = adjust_xmem(L, XR2), Res2 = {?S(T1),?S(T2),?S(T3),?S(T4)}, @@ -636,9 +636,9 @@ memory_do(Opts) -> end, L), XR3 = case mem_mode(T1) of - {normal,_} -> {13816,13028,13010,13016}; %{13842,13054,13036,13042}; - {compressed,4} -> {11021,10233,10215,10216}; %{11047,10259,10241,10242}; - {compressed,8} -> {10030,9242,9224,9224} %{10056,9268,9250,9250} + {normal,_} -> {13816,13542,13524,13530}; %{13816,13028,13010,13016} + {compressed,4} -> {11021,10747,10729,10730}; %{11021,10233,10215,10216} + {compressed,8} -> {10030,9756,9738,9738} %{10030,9242,9224,9224} end, XRes3 = adjust_xmem(L, XR3), Res3 = {?S(T1),?S(T2),?S(T3),?S(T4)}, @@ -5350,12 +5350,12 @@ verify_table_load(T) -> Stats = ets:info(T,stats), {Buckets,AvgLen,StdDev,ExpSD,_MinLen,_MaxLen,_} = Stats, ok = if - AvgLen > 7 -> + AvgLen > 2 -> io:format("Table overloaded: Stats=~p\n~p\n", [Stats, ets:info(T)]), false; - Buckets>256, AvgLen < 6 -> + Buckets>256, AvgLen < 0.5 -> io:format("Table underloaded: Stats=~p\n~p\n", [Stats, ets:info(T)]), false; diff --git a/lib/stdlib/test/id_transform_SUITE.erl b/lib/stdlib/test/id_transform_SUITE.erl index 54f452825f..3d4ae1a189 100644 --- a/lib/stdlib/test/id_transform_SUITE.erl +++ b/lib/stdlib/test/id_transform_SUITE.erl @@ -61,13 +61,8 @@ id_transform(Config) when is_list(Config) -> "erl_id_trans.erl"]), {ok,erl_id_trans,Bin} = compile:file(File,[binary]), {module,erl_id_trans} = code:load_binary(erl_id_trans, File, Bin), - case test_server:purify_is_running() of - false -> - ct:timetrap({hours,1}), - run_in_test_suite(); - true -> - {skip,"Valgrind (too slow)"} - end. + ct:timetrap({hours,1}), + run_in_test_suite(). run_in_test_suite() -> SuperDir = filename:dirname(filename:dirname(code:which(?MODULE))), diff --git a/lib/stdlib/test/supervisor_SUITE.erl b/lib/stdlib/test/supervisor_SUITE.erl index 3f1aa0e7a3..cd2c6b0cbb 100644 --- a/lib/stdlib/test/supervisor_SUITE.erl +++ b/lib/stdlib/test/supervisor_SUITE.erl @@ -66,7 +66,7 @@ %% Misc tests -export([child_unlink/1, tree/1, count_children/1, - count_restarting_children/1, + count_restarting_children/1, get_callback_module/1, do_not_save_start_parameters_for_temporary_children/1, do_not_save_child_specs_for_temporary_children/1, simple_one_for_one_scale_many_temporary_children/1, @@ -91,7 +91,7 @@ all() -> {group, normal_termination}, {group, shutdown_termination}, {group, abnormal_termination}, child_unlink, tree, - count_children, count_restarting_children, + count_children, count_restarting_children, get_callback_module, do_not_save_start_parameters_for_temporary_children, do_not_save_child_specs_for_temporary_children, simple_one_for_one_scale_many_temporary_children, temporary_bystander, @@ -1507,6 +1507,14 @@ count_restarting_children(Config) when is_list(Config) -> [1,0,0,0] = get_child_counts(SupPid). %%------------------------------------------------------------------------- +%% Test get_callback_module +get_callback_module(Config) when is_list(Config) -> + Child = {child, {supervisor_1, start_child, []}, temporary, 1000, + worker, []}, + {ok, SupPid} = start_link({ok, {{simple_one_for_one, 2, 3600}, [Child]}}), + supervisor_SUITE = supervisor:get_callback_module(SupPid). + +%%------------------------------------------------------------------------- %% Temporary children shall not be restarted so they should not save %% start parameters, as it potentially can take up a huge amount of %% memory for no purpose. diff --git a/lib/wx/src/wxe_master.erl b/lib/wx/src/wxe_master.erl index 06be0367f8..e17a3327ac 100644 --- a/lib/wx/src/wxe_master.erl +++ b/lib/wx/src/wxe_master.erl @@ -185,10 +185,10 @@ handle_cast(_Msg, State) -> %% Description: Handling all non call/cast messages %%-------------------------------------------------------------------- handle_info({wxe_driver, error, Msg}, State) -> - error_logger:format("WX ERROR: ~s~n", [Msg]), + error_logger:error_report([{wx, error}, {message, lists:flatten(Msg)}]), {noreply, State}; handle_info({wxe_driver, internal_error, Msg}, State) -> - error_logger:format("WX INTERNAL ERROR: ~s~n", [Msg]), + error_logger:error_report([{wx, internal_error}, {message, lists:flatten(Msg)}]), {noreply, State}; handle_info({wxe_driver, debug, Msg}, State) -> io:format("WX DBG: ~s~n", [Msg]), diff --git a/lib/wx/src/wxe_util.erl b/lib/wx/src/wxe_util.erl index 3eaf6aebed..bbcd9a65ea 100644 --- a/lib/wx/src/wxe_util.erl +++ b/lib/wx/src/wxe_util.erl @@ -82,9 +82,11 @@ rec(Op) -> {'_wxe_error_', Op, Error} -> [{_,MF}] = ets:lookup(wx_debug_info,Op), erlang:error({Error, MF}); - {'_wxe_error_', Old, Error} -> - [{_,MF}] = ets:lookup(wx_debug_info,Old), - erlang:exit({Error, MF}) + {'_wxe_error_', Old, Error} -> + [{_,{M,F,A}}] = ets:lookup(wx_debug_info,Old), + Msg = io_lib:format("~p in ~w:~w/~w", [Error, M, F, A]), + wxe_master ! {wxe_driver, error, Msg}, + rec(Op) end. construct(Op, Args) -> diff --git a/lib/wx/test/wx_basic_SUITE.erl b/lib/wx/test/wx_basic_SUITE.erl index f89f25274a..6a2528780e 100644 --- a/lib/wx/test/wx_basic_SUITE.erl +++ b/lib/wx/test/wx_basic_SUITE.erl @@ -192,7 +192,9 @@ wx_api(Config) -> ?m(ok,wxButton:setLabel(Temp, "Testing")), ?m(ok,wxButton:destroy(Temp)), ?m({'EXIT',_},wxButton:getLabel(Temp)), - + ?m(ok,wxButton:setLabel(Temp, "Testing")), %% Should generate an error report + ?m({'EXIT',_},wxButton:getLabel(Temp)), + case wx_test_lib:user_available(Config) of true -> %% Hmm popup doesn't return until mouse is pressed. @@ -1393,9 +1393,6 @@ case "$1" in TYPE=opt fi; FLAVOR=$1 - if [ $FLAVOR = opt ]; then - FLAVOR=plain - fi do_boot;; update_primary) case $version_controller in |