diff options
112 files changed, 2968 insertions, 1061 deletions
diff --git a/bootstrap/bin/start.script b/bootstrap/bin/start.script index 085ea1f112..3f472b5e8c 100644 --- a/bootstrap/bin/start.script +++ b/bootstrap/bin/start.script @@ -1,4 +1,4 @@ -%% script generated at {2010,10,7} {12,5,16} +%% script generated at {2010,10,18} {14,19,26} {script, {"OTP APN 181 01","R14B01"}, [{preLoaded, diff --git a/bootstrap/bin/start_clean.script b/bootstrap/bin/start_clean.script index 085ea1f112..3f472b5e8c 100644 --- a/bootstrap/bin/start_clean.script +++ b/bootstrap/bin/start_clean.script @@ -1,4 +1,4 @@ -%% script generated at {2010,10,7} {12,5,16} +%% script generated at {2010,10,18} {14,19,26} {script, {"OTP APN 181 01","R14B01"}, [{preLoaded, diff --git a/bootstrap/lib/compiler/ebin/beam_block.beam b/bootstrap/lib/compiler/ebin/beam_block.beam Binary files differindex 4d71b65e23..41be7667fc 100644 --- a/bootstrap/lib/compiler/ebin/beam_block.beam +++ b/bootstrap/lib/compiler/ebin/beam_block.beam diff --git a/bootstrap/lib/compiler/ebin/core_lint.beam b/bootstrap/lib/compiler/ebin/core_lint.beam Binary files differindex 3ee981e50a..813c444d9c 100644 --- a/bootstrap/lib/compiler/ebin/core_lint.beam +++ b/bootstrap/lib/compiler/ebin/core_lint.beam diff --git a/bootstrap/lib/kernel/ebin/dist_util.beam b/bootstrap/lib/kernel/ebin/dist_util.beam Binary files differindex 167a70e99d..93f0a3754a 100644 --- a/bootstrap/lib/kernel/ebin/dist_util.beam +++ b/bootstrap/lib/kernel/ebin/dist_util.beam diff --git a/bootstrap/lib/stdlib/ebin/supervisor.beam b/bootstrap/lib/stdlib/ebin/supervisor.beam Binary files differindex 54811dad66..a9e30b4d5d 100644 --- a/bootstrap/lib/stdlib/ebin/supervisor.beam +++ b/bootstrap/lib/stdlib/ebin/supervisor.beam diff --git a/erts/doc/src/driver.xml b/erts/doc/src/driver.xml index 006a6160de..db455312ec 100644 --- a/erts/doc/src/driver.xml +++ b/erts/doc/src/driver.xml @@ -196,11 +196,14 @@ static ErlDrvData start(ErlDrvPort port, char *command) <p>We call disconnect to log out from the database. (This should have been done from Erlang, but just in case.)</p> <code type="none"><![CDATA[ - static int do_disconnect(our_data_t* data, ei_x_buff* x); +static int do_disconnect(our_data_t* data, ei_x_buff* x); static void stop(ErlDrvData drv_data) { - do_disconnect((our_data_t*)drv_data, NULL); + our_data_t* data = (our_data_t*)drv_data; + + do_disconnect(data, NULL); + driver_free(data); } ]]></code> <p>We use the binary format only to return data to the emulator; diff --git a/erts/doc/src/driver_entry.xml b/erts/doc/src/driver_entry.xml index e71b48bd92..dd949d4048 100644 --- a/erts/doc/src/driver_entry.xml +++ b/erts/doc/src/driver_entry.xml @@ -172,7 +172,7 @@ typedef struct erl_drv_entry { added to the driver list.) The driver should return 0, or if the driver can't initialize, -1.</p> </item> - <tag><marker id="start"/>int (*start)(ErlDrvPort port, char* command)</tag> + <tag><marker id="start"/>ErlDrvData (*start)(ErlDrvPort port, char* command)</tag> <item> <p>This is called when the driver is instantiated, when <c>open_port/2</c> is called. The driver should return a @@ -188,7 +188,9 @@ typedef struct erl_drv_entry { <p>This is called when the port is closed, with <c>port_close/1</c> or <c>Port ! {self(), close}</c>. Note that terminating the port owner process also closes the - port.</p> + port. If <c>drv_data</c> is a pointer to memory allocated in + <c>start</c>, then <c>stop</c> is the place to deallocate that + memory.</p> </item> <tag><marker id="output"/>void (*output)(ErlDrvData drv_data, char *buf, int len)</tag> <item> diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml index e36d0adb0d..09c9cf6812 100644 --- a/erts/doc/src/erl.xml +++ b/erts/doc/src/erl.xml @@ -906,6 +906,25 @@ <seealso marker="kernel:error_logger#warning_map/0">error_logger(3)</seealso> for further information.</p> </item> + <tag><c><![CDATA[+zFlag Value]]></c></tag> + <item> + <p>Miscellaneous flags.</p> + <taglist> + <tag><marker id="+zdbbl"><c>+zdbbl size</c></marker></tag> + <item> + <p>Set the distribution buffer busy limit + (<seealso marker="erlang#system_info_dist_buf_busy_limit">dist_buf_busy_limit</seealso>) + in kilobytes. Valid range is 1-2097151. Default is 128.</p> + <p>A larger buffer limit will allow processes to buffer + more outgoing messages over the distribution. When the + buffer limit has been reached, sending processes will be + suspended until the buffer size has shrunk. The buffer + limit is per distribution channel. A higher limit will + give lower latency and higher throughput at the expense + of higher memory usage.</p> + </item> + </taglist> + </item> </taglist> </section> diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 59ac3dc66c..25c92bdbb7 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -5624,6 +5624,13 @@ true</pre> The return value will always be <c>false</c> since the elib_malloc allocator has been removed.</p> </item> + <tag><marker id="system_info_dist_buf_busy_limit"><c>dist_buf_busy_limit</c></marker></tag> + <item> + <p>Returns the value of the distribution buffer busy limit + in bytes. This limit can be set on startup by passing the + <seealso marker="erl#+zdbbl">+zdbbl</seealso> command line + flag to <c>erl</c>.</p> + </item> <tag><c>fullsweep_after</c></tag> <item> <p>Returns <c>{fullsweep_after, int()}</c> which is the diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index 532ebc29e2..1703ce0942 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -30,6 +30,35 @@ </header> <p>This document describes the changes made to the ERTS application.</p> +<section><title>Erts 5.8.1.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> Fix that the documentation top index generator can + handle an Ericsson internal application group. </p> + <p> + Own Id: OTP-8875</p> + </item> + <item> + <p>In embedded mode, on_load handlers that called + <c>code:priv_dir/1</c> or other functions in <c>code</c> + would hang the system. Since the <c>crypto</c> + application now contains an on_loader handler that calls + <c>code:priv_dir/1</c>, including the <c>crypto</c> + application in the boot file would prevent the system + from starting.</p> + <p>Also extended the <c>-init_debug</c> option to print + information about on_load handlers being run to + facilitate debugging.</p> + <p> + Own Id: OTP-8902 Aux Id: seq11703 </p> + </item> + </list> + </section> + +</section> + <section><title>Erts 5.8.1.1</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c index 16b6aeac3f..4497e17d79 100644 --- a/erts/emulator/beam/dist.c +++ b/erts/emulator/beam/dist.c @@ -97,6 +97,8 @@ dist_msg_dbg(ErtsDistExternal *edep, char *what, byte *buf, int sz) #define PASS_THROUGH 'p' /* This code should go */ int erts_is_alive; /* System must be blocked on change */ +int erts_dist_buf_busy_limit; + /* distribution trap functions */ Export* dsend2_trap = NULL; @@ -1453,8 +1455,6 @@ int erts_net_message(Port *prt, return -1; } -#define ERTS_DE_BUSY_LIMIT (128*1024) - static int dsig_send(ErtsDSigData *dsdp, Eterm ctl, Eterm msg, int force_busy) { @@ -1540,7 +1540,7 @@ dsig_send(ErtsDSigData *dsdp, Eterm ctl, Eterm msg, int force_busy) ErtsProcList *plp = NULL; erts_smp_spin_lock(&dep->qlock); dep->qsize += size_obuf(obuf); - if (dep->qsize >= ERTS_DE_BUSY_LIMIT) + if (dep->qsize >= erts_dist_buf_busy_limit) dep->qflgs |= ERTS_DE_QFLG_BUSY; if (!force_busy && (dep->qflgs & ERTS_DE_QFLG_BUSY)) { erts_smp_spin_unlock(&dep->qlock); @@ -1911,7 +1911,7 @@ erts_dist_command(Port *prt, int reds_limit) ASSERT(dep->qsize >= obufsize); dep->qsize -= obufsize; obufsize = 0; - if (de_busy && !prt_busy && dep->qsize < ERTS_DE_BUSY_LIMIT) { + if (de_busy && !prt_busy && dep->qsize < erts_dist_buf_busy_limit) { ErtsProcList *suspendees; int resumed; suspendees = get_suspended_on_de(dep, ERTS_DE_QFLG_BUSY); diff --git a/erts/emulator/beam/dist.h b/erts/emulator/beam/dist.h index fa19c7fb45..28cdd05c3c 100644 --- a/erts/emulator/beam/dist.h +++ b/erts/emulator/beam/dist.h @@ -99,7 +99,8 @@ typedef struct { #define ERTS_DE_IS_CONNECTED(DEP) \ (!ERTS_DE_IS_NOT_CONNECTED((DEP))) - +#define ERTS_DE_BUSY_LIMIT (128*1024) +extern int erts_dist_buf_busy_limit; extern int erts_is_alive; /* diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 40d8dc097c..801263ec26 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -2533,6 +2533,13 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) BIF_RET(erts_nif_taints(BIF_P)); } else if (ERTS_IS_ATOM_STR("reader_groups_map", BIF_ARG_1)) { BIF_RET(erts_get_reader_groups_map(BIF_P)); + } else if (ERTS_IS_ATOM_STR("dist_buf_busy_limit", BIF_ARG_1)) { + Uint hsz = 0; + + (void) erts_bld_uint(NULL, &hsz, erts_dist_buf_busy_limit); + hp = hsz ? HAlloc(BIF_P, hsz) : NULL; + res = erts_bld_uint(&hp, NULL, erts_dist_buf_busy_limit); + BIF_RET(res); } BIF_ERROR(BIF_P, BADARG); diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index 4ae656a3ad..36fefc5cba 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -535,7 +535,8 @@ void erts_usage(void) erts_fprintf(stderr, "-W<i|w> set error logger warnings mapping,\n"); erts_fprintf(stderr, " see error_logger documentation for details\n"); - + erts_fprintf(stderr, "-zdbbl size set the distribution buffer busy limit in kilobytes\n"); + erts_fprintf(stderr, " valid range is [1-%d]\n", INT_MAX/1024); erts_fprintf(stderr, "\n"); erts_fprintf(stderr, "Note that if the emulator is started with erlexec (typically\n"); erts_fprintf(stderr, "from the erl script), these flags should be specified with +.\n"); @@ -818,7 +819,7 @@ early_init(int *argc, char **argv) /* erl_sys_args(argc, argv); erts_ets_realloc_always_moves = 0; - + erts_dist_buf_busy_limit = ERTS_DE_BUSY_LIMIT; } #ifndef ERTS_SMP @@ -1346,6 +1347,26 @@ erl_start(int argc, char **argv) } break; + case 'z': { + char *sub_param = argv[i]+2; + int new_limit; + + if (has_prefix("dbbl", sub_param)) { + arg = get_arg(sub_param+4, argv[i+1], &i); + new_limit = atoi(arg); + if (new_limit < 1 || INT_MAX/1024 < new_limit) { + erts_fprintf(stderr, "Invalid dbbl limit: %d\n", new_limit); + erts_usage(); + } else { + erts_dist_buf_busy_limit = new_limit*1024; + } + } else { + erts_fprintf(stderr, "bad -z option %s\n", argv[i]); + erts_usage(); + } + break; + } + default: erts_fprintf(stderr, "%s unknown flag %s\n", argv[0], argv[i]); erts_usage(); diff --git a/erts/emulator/test/distribution_SUITE.erl b/erts/emulator/test/distribution_SUITE.erl index 7c19274696..79252d0593 100644 --- a/erts/emulator/test/distribution_SUITE.erl +++ b/erts/emulator/test/distribution_SUITE.erl @@ -27,6 +27,7 @@ -export([all/1, ping/1, bulk_send/1, bulk_send_small/1, bulk_send_big/1, + bulk_send_bigbig/1, local_send/1, local_send_small/1, local_send_big/1, local_send_legal/1, link_to_busy/1, exit_to_busy/1, lost_exit/1, link_to_dead/1, link_to_dead_new_node/1, @@ -50,7 +51,8 @@ -export([sender/3, receiver2/2, dummy_waiter/0, dead_process/0, roundtrip/1, bounce/1, do_dist_auto_connect/1, inet_rpc_server/1, dist_parallel_sender/3, dist_parallel_receiver/0, - dist_evil_parallel_receiver/0]). + dist_evil_parallel_receiver/0, + sendersender/4, sendersender2/4]). all(suite) -> [ ping, bulk_send, local_send, link_to_busy, exit_to_busy, @@ -121,7 +123,7 @@ bulk_send(doc) -> "the time. This tests that a process that is suspended on a ", "busy port will eventually be resumed."]; bulk_send(suite) -> - [bulk_send_small, bulk_send_big]. + [bulk_send_small, bulk_send_big, bulk_send_bigbig]. bulk_send_small(Config) when is_list(Config) -> ?line bulk_send(64, 32). @@ -129,6 +131,9 @@ bulk_send_small(Config) when is_list(Config) -> bulk_send_big(Config) when is_list(Config) -> ?line bulk_send(32, 64). +bulk_send_bigbig(Config) when is_list(Config) -> + ?line bulk_sendsend(32*5, 4). + bulk_send(Terms, BinSize) -> ?line Dog = test_server:timetrap(test_server:seconds(30)), @@ -145,6 +150,53 @@ bulk_send(Terms, BinSize) -> ?line test_server:timetrap_cancel(Dog), {comment, integer_to_list(trunc(Size/1024/Elapsed+0.5)) ++ " K/s"}. +bulk_sendsend(Terms, BinSize) -> + {Rate1, MonitorCount1} = bulk_sendsend2(Terms, BinSize, 5), + {Rate2, MonitorCount2} = bulk_sendsend2(Terms, BinSize, 995), + Ratio = if MonitorCount2 == 0 -> MonitorCount1 / 1.0; + true -> MonitorCount1 / MonitorCount2 + end, + %% A somewhat arbitrary ratio, but hopefully one that will accomodate + %% a wide range of CPU speeds. + true = (Ratio > 8.0), + {comment, + integer_to_list(Rate1) ++ " K/s, " ++ + integer_to_list(Rate2) ++ " K/s, " ++ + integer_to_list(MonitorCount1) ++ " monitor msgs, " ++ + integer_to_list(MonitorCount2) ++ " monitor msgs, " ++ + float_to_list(Ratio) ++ " monitor ratio"}. + +bulk_sendsend2(Terms, BinSize, BusyBufSize) -> + ?line Dog = test_server:timetrap(test_server:seconds(30)), + + ?line io:format("Sending ~w binaries, each of size ~w K", + [Terms, BinSize]), + ?line {ok, NodeRecv} = start_node(bulk_receiver), + ?line Recv = spawn(NodeRecv, erlang, apply, [fun receiver/2, [0, 0]]), + ?line Bin = list_to_binary(lists:duplicate(BinSize*1024, 253)), + ?line Size = Terms*size(Bin), + + %% SLF LEFT OFF HERE. + %% When the caller uses small hunks, like 4k via + %% bulk_sendsend(32*5, 4), then (on my laptop at least), we get + %% zero monitor messages. But if we use "+zdbbl 5", then we + %% get a lot of monitor messages. So, if we can count up the + %% total number of monitor messages that we get when running both + %% default busy size and "+zdbbl 5", and if the 5 case gets + %% "many many more" monitor messages, then we know we're working. + + ?line {ok, NodeSend} = start_node(bulk_sender, "+zdbbl " ++ integer_to_list(BusyBufSize)), + ?line _Send = spawn(NodeSend, erlang, apply, [fun sendersender/4, [self(), Recv, Bin, Terms]]), + ?line {Elapsed, {TermsN, SizeN}, MonitorCount} = + receive {sendersender, BigRes} -> + BigRes + end, + ?line stop_node(NodeRecv), + ?line stop_node(NodeSend), + + ?line test_server:timetrap_cancel(Dog), + {trunc(SizeN/1024/Elapsed+0.5), MonitorCount}. + sender(To, _Bin, 0) -> To ! {done, self()}, receive @@ -155,6 +207,43 @@ sender(To, Bin, Left) -> To ! {term, Bin}, sender(To, Bin, Left-1). +%% Sender process to be run on a slave node + +sendersender(Parent, To, Bin, Left) -> + erlang:system_monitor(self(), [busy_dist_port]), + [spawn(fun() -> sendersender2(To, Bin, Left, false) end) || + _ <- lists:seq(1,1)], + {USec, {Res, MonitorCount}} = + timer:tc(?MODULE, sendersender2, [To, Bin, Left, true]), + Parent ! {sendersender, {USec/1000000, Res, MonitorCount}}. + +sendersender2(To, Bin, Left, SendDone) -> + sendersender3(To, Bin, Left, SendDone, 0). + +sendersender3(To, _Bin, 0, SendDone, MonitorCount) -> + if SendDone -> + To ! {done, self()}; + true -> + ok + end, + receive + {monitor, _Pid, _Type, _Info} = M -> + sendersender3(To, _Bin, 0, SendDone, MonitorCount + 1) + after 0 -> + if SendDone -> + receive + Any when is_tuple(Any), size(Any) == 2 -> + {Any, MonitorCount} + end; + true -> + exit(normal) + end + end; +sendersender3(To, Bin, Left, SendDone, MonitorCount) -> + To ! {term, Bin}, + %%timer:sleep(50), + sendersender3(To, Bin, Left-1, SendDone, MonitorCount). + %% Receiver process to be run on a slave node. receiver(Terms, Size) -> diff --git a/erts/epmd/src/epmd_srv.c b/erts/epmd/src/epmd_srv.c index df4d1a5715..ef471a473a 100644 --- a/erts/epmd/src/epmd_srv.c +++ b/erts/epmd/src/epmd_srv.c @@ -39,8 +39,10 @@ * server keeps the socket open where the request for registration was * made. * - * The protocol is briefly documented in "erl_ext_dist.txt". All requests - * to this server are done with a packet + * The protocol is briefly documented in the ERTS User's Guide, see + * http://www.erlang.org/doc/apps/erts/erl_dist_protocol.html + * + * All requests to this server are done with a packet * * 2 n * +--------+---------+ diff --git a/erts/etc/common/Makefile.in b/erts/etc/common/Makefile.in index 96655662b8..e866511153 100644 --- a/erts/etc/common/Makefile.in +++ b/erts/etc/common/Makefile.in @@ -327,31 +327,31 @@ $(OBJDIR)/$(ERLEXEC).o: $(ERLEXECDIR)/$(ERLEXEC).c $(CC) -I$(EMUDIR) $(CFLAGS) -o $@ -c $(ERLEXECDIR)/$(ERLEXEC).c endif $(BINDIR)/erlc@EXEEXT@: $(OBJDIR)/erlc.o - $(PURIFY) $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/erlc.o -L$(OBJDIR) $(LIBS) + $(PURIFY) $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/erlc.o -L$(OBJDIR) $(LIBS) $(ERTS_INTERNAL_LIBS) $(OBJDIR)/erlc.o: erlc.c $(CC) $(CFLAGS) -o $@ -c erlc.c $(BINDIR)/dialyzer@EXEEXT@: $(OBJDIR)/dialyzer.o - $(PURIFY) $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/dialyzer.o -L$(OBJDIR) $(LIBS) + $(PURIFY) $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/dialyzer.o -L$(OBJDIR) $(LIBS) $(ERTS_INTERNAL_LIBS) $(OBJDIR)/dialyzer.o: dialyzer.c $(CC) $(CFLAGS) -o $@ -c dialyzer.c $(BINDIR)/typer@EXEEXT@: $(OBJDIR)/typer.o - $(PURIFY) $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/typer.o -L$(OBJDIR) $(LIBS) + $(PURIFY) $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/typer.o -L$(OBJDIR) $(LIBS) $(ERTS_INTERNAL_LIBS) $(OBJDIR)/typer.o: typer.c $(CC) $(CFLAGS) -o $@ -c typer.c $(BINDIR)/escript@EXEEXT@: $(OBJDIR)/escript.o - $(PURIFY) $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/escript.o -L$(OBJDIR) $(LIBS) + $(PURIFY) $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/escript.o -L$(OBJDIR) $(LIBS) $(ERTS_INTERNAL_LIBS) $(OBJDIR)/escript.o: escript.c $(CC) $(CFLAGS) -o $@ -c escript.c $(BINDIR)/run_test@EXEEXT@: $(OBJDIR)/run_test.o - $(PURIFY) $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/run_test.o -L$(OBJDIR) $(LIBS) + $(PURIFY) $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/run_test.o -L$(OBJDIR) $(LIBS) $(ERTS_INTERNAL_LIBS) $(OBJDIR)/run_test.o: run_test.c $(CC) $(CFLAGS) -o $@ -c run_test.c diff --git a/erts/etc/common/dialyzer.c b/erts/etc/common/dialyzer.c index 4b4c1124f1..4453e63f1c 100644 --- a/erts/etc/common/dialyzer.c +++ b/erts/etc/common/dialyzer.c @@ -147,6 +147,9 @@ main(int argc, char** argv) env = get_env("DIALYZER_EMULATOR"); emulator = env ? env : get_default_emulator(argv[0]); + if (strlen(emulator) >= MAXPATHLEN) + error("Value of environment variable DIALYZER_EMULATOR is too large"); + /* * Allocate the argv vector to be used for arguments to Erlang. * Arrange for starting to pushing information in the middle of @@ -228,7 +231,7 @@ main(int argc, char** argv) static void push_words(char* src) { - char sbuf[1024]; + char sbuf[MAXPATHLEN]; char* dst; dst = sbuf; @@ -360,7 +363,7 @@ error(char* format, ...) va_list ap; va_start(ap, format); - vsprintf(sbuf, format, ap); + erts_vsnprintf(sbuf, sizeof(sbuf), format, ap); va_end(ap); fprintf(stderr, "dialyzer: %s\n", sbuf); exit(1); @@ -389,6 +392,9 @@ get_default_emulator(char* progname) char sbuf[MAXPATHLEN]; char* s; + if (strlen(progname) >= sizeof(sbuf)) + return ERL_NAME; + strcpy(sbuf, progname); for (s = sbuf+strlen(sbuf); s >= sbuf; s--) { if (IS_DIRSEP(*s)) { diff --git a/erts/etc/common/erlc.c b/erts/etc/common/erlc.c index 09aca19e6c..cd137435d1 100644 --- a/erts/etc/common/erlc.c +++ b/erts/etc/common/erlc.c @@ -148,10 +148,6 @@ int main(int argc, char** argv) { char cwd[MAXPATHLEN]; /* Current working directory. */ - char** rpc_eargv; /* Pointer to the beginning of arguments - * if calling a running Erlang system - * via erl_rpc(). - */ int eargv_size; int eargc_base; /* How many arguments in the base of eargv. */ char* emulator; @@ -160,6 +156,9 @@ main(int argc, char** argv) env = get_env("ERLC_EMULATOR"); emulator = env ? env : get_default_emulator(argv[0]); + if (strlen(emulator) >= MAXPATHLEN) + error("Value of environment variable ERLC_EMULATOR is too large"); + /* * Allocate the argv vector to be used for arguments to Erlang. * Arrange for starting to pushing information in the middle of @@ -170,7 +169,7 @@ main(int argc, char** argv) * base of the eargv vector, and move it up later. */ - eargv_size = argc*4+100; + eargv_size = argc*6+100; eargv_base = (char **) emalloc(eargv_size*sizeof(char*)); eargv = eargv_base; eargc = 0; @@ -189,7 +188,6 @@ main(int argc, char** argv) PUSH2("-mode", "minimal"); PUSH2("-boot", "start_clean"); PUSH3("-s", "erl_compile", "compile_cmdline"); - rpc_eargv = eargv+eargc; /* * Push standard arguments to Erlang. @@ -419,7 +417,7 @@ process_opt(int* pArgc, char*** pArgv, int offset) static void push_words(char* src) { - char sbuf[1024]; + char sbuf[MAXPATHLEN]; char* dst; dst = sbuf; @@ -595,7 +593,7 @@ error(char* format, ...) va_list ap; va_start(ap, format); - vsprintf(sbuf, format, ap); + erts_vsnprintf(sbuf, sizeof(sbuf), format, ap); va_end(ap); fprintf(stderr, "erlc: %s\n", sbuf); exit(1); @@ -624,6 +622,9 @@ get_default_emulator(char* progname) char sbuf[MAXPATHLEN]; char* s; + if (strlen(progname) >= sizeof(sbuf)) + return ERL_NAME; + strcpy(sbuf, progname); for (s = sbuf+strlen(sbuf); s >= sbuf; s--) { if (IS_DIRSEP(*s)) { diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c index c1fc2aebee..60b3af7db7 100644 --- a/erts/etc/common/erlexec.c +++ b/erts/etc/common/erlexec.c @@ -138,6 +138,12 @@ static char *plusr_val_switches[] = { NULL }; +/* +z arguments with values */ +static char *plusz_val_switches[] = { + "dbbl", + NULL +}; + /* * Define sleep(seconds) in terms of Sleep() on Windows. @@ -309,7 +315,7 @@ free_env_val(char *value) } /* - * Add the arcitecture suffix to the program name if needed, + * Add the architecture suffix to the program name if needed, * except on Windows, where we insert it just before ".DLL". */ static char* @@ -560,7 +566,7 @@ int main(int argc, char **argv) usage("+MYm"); } emu = add_extra_suffixes(emu, emu_type); - sprintf(tmpStr, "%s" DIRSEP "%s" BINARY_EXT, bindir, emu); + erts_snprintf(tmpStr, sizeof(tmpStr), "%s" DIRSEP "%s" BINARY_EXT, bindir, emu); emu = strsave(tmpStr); add_Eargs(emu); /* Will be argv[0] -- necessary! */ @@ -571,12 +577,12 @@ int main(int argc, char **argv) s = get_env("PATH"); if (!s) { - sprintf(tmpStr, "%s" PATHSEP "%s" DIRSEP "bin", bindir, rootdir); + erts_snprintf(tmpStr, sizeof(tmpStr), "%s" PATHSEP "%s" DIRSEP "bin", bindir, rootdir); } else if (strstr(s, bindir) == NULL) { - sprintf(tmpStr, "%s" PATHSEP "%s" DIRSEP "bin" PATHSEP "%s", bindir, + erts_snprintf(tmpStr, sizeof(tmpStr), "%s" PATHSEP "%s" DIRSEP "bin" PATHSEP "%s", bindir, rootdir, s); } else { - sprintf(tmpStr, "%s", s); + erts_snprintf(tmpStr, sizeof(tmpStr), "%s", s); } free_env_val(s); set_env("PATH", tmpStr); @@ -714,7 +720,7 @@ int main(int argc, char **argv) error("-man not supported on Windows"); #else argv[i] = "man"; - sprintf(tmpStr, "%s/man", rootdir); + erts_snprintf(tmpStr, sizeof(tmpStr), "%s/man", rootdir); set_env("MANPATH", tmpStr); execvp("man", argv+i); error("Could not execute the 'man' command."); @@ -909,6 +915,20 @@ int main(int argc, char **argv) i++; } break; + case 'z': + if (!is_one_of_strings(&argv[i][2], plusz_val_switches)) { + goto the_default; + } else { + if (i+1 >= argc + || argv[i+1][0] == '-' + || argv[i+1][0] == '+') + usage(argv[i]); + argv[i][0] = '-'; + add_Eargs(argv[i]); + add_Eargs(argv[i+1]); + i++; + } + break; default: the_default: argv[i][0] = '-'; /* Change +option to -option. */ @@ -1096,7 +1116,7 @@ usage_aux(void) "[+l] [+M<SUBSWITCH> <ARGUMENT>] [+P MAX_PROCS] [+R COMPAT_REL] " "[+r] [+rg READER_GROUPS_LIMIT] [+s SCHEDULER_OPTION] " "[+S NO_SCHEDULERS:NO_SCHEDULERS_ONLINE] [+T LEVEL] [+V] [+v] " - "[+W<i|w>] [args ...]\n"); + "[+W<i|w>] [+z MISC_OPTION] [args ...]\n"); exit(1); } @@ -1145,10 +1165,10 @@ start_epmd(char *epmd) if (!epmd) { epmd = epmd_cmd; #ifdef __WIN32__ - sprintf(epmd_cmd, "%s" DIRSEP "epmd", bindir); + erts_snprintf(epmd_cmd, sizeof(epmd_cmd), "%s" DIRSEP "epmd", bindir); arg1 = "-daemon"; #else - sprintf(epmd_cmd, "%s" DIRSEP "epmd -daemon", bindir); + erts_snprintf(epmd_cmd, sizeof(epmd_cmd), "%s" DIRSEP "epmd -daemon", bindir); #endif } #ifdef __WIN32__ @@ -1224,7 +1244,7 @@ void error(char* format, ...) va_list ap; va_start(ap, format); - vsprintf(sbuf, format, ap); + erts_vsnprintf(sbuf, sizeof(sbuf), format, ap); va_end(ap); fprintf(stderr, "erlexec: %s\n", sbuf); exit(1); @@ -1304,14 +1324,14 @@ static void get_start_erl_data(char *file) if (env) reldir = strsave(env); else { - sprintf(tmpbuffer, "%s/releases", rootdir); + erts_snprintf(tmpbuffer, sizeof(tmpbuffer), "%s/releases", rootdir); reldir = strsave(tmpbuffer); } free_env_val(env); if (file == NULL) - sprintf(start_erl_data, "%s/start_erl.data", reldir); + erts_snprintf(start_erl_data, sizeof(start_erl_data), "%s/start_erl.data", reldir); else - sprintf(start_erl_data, "%s", file); + erts_snprintf(start_erl_data, sizeof(start_erl_data), "%s", file); fp = _open(start_erl_data, _O_RDONLY ); if( fp == -1 ) error( "open failed on %s",start_erl_data ); @@ -1341,16 +1361,16 @@ static void get_start_erl_data(char *file) } bindir = emalloc(512); - sprintf(bindir,"%s/erts-%s/bin",rootdir,tmpbuffer); + erts_snprintf(bindir,512,"%s/erts-%s/bin",rootdir,tmpbuffer); /* BINDIR=$ROOTDIR/erts-$ERTS_VSN/bin */ tprogname = progname; progname = emalloc(strlen(tprogname) + 20); - sprintf(progname,"%s -start_erl",tprogname); + erts_snprintf(progname,strlen(tprogname) + 20,"%s -start_erl",tprogname); boot_script = emalloc(512); config_script = emalloc(512); - sprintf(boot_script, "%s/%s/start", reldir, otpstring); - sprintf(config_script, "%s/%s/sys", reldir, otpstring); + erts_snprintf(boot_script, 512, "%s/%s/start", reldir, otpstring); + erts_snprintf(config_script, 512, "%s/%s/sys", reldir, otpstring); } @@ -1358,7 +1378,7 @@ static void get_start_erl_data(char *file) static char *replace_filename(char *path, char *new_base) { int plen = strlen(path); - char *res = malloc((plen+strlen(new_base)+1)*sizeof(char)); + char *res = emalloc((plen+strlen(new_base)+1)*sizeof(char)); char *p; strcpy(res,path); @@ -1373,7 +1393,7 @@ static char *path_massage(char *long_path) { char *p; - p = malloc(MAX_PATH+1); + p = emalloc(MAX_PATH+1); strcpy(p, long_path); GetShortPathName(p, p, MAX_PATH); return p; @@ -1509,7 +1529,8 @@ get_parameters(int argc, char** argv) /* Determine bindir from absolute path to executable */ char *p; char buffer[PATH_MAX]; - strcpy(buffer, argv[0]); + strncpy(buffer, argv[0], sizeof(buffer)); + buffer[sizeof(buffer)-1] = '\0'; for (p = buffer+strlen(buffer)-1 ; p >= buffer && *p != '/'; --p) ; @@ -1522,7 +1543,8 @@ get_parameters(int argc, char** argv) /* Determine rootdir from absolute path to bindir */ char *p; char buffer[PATH_MAX]; - strcpy(buffer, bindir); + strncpy(buffer, bindir, sizeof(buffer)); + buffer[sizeof(buffer)-1] = '\0'; for (p = buffer+strlen(buffer)-1; p >= buffer && *p != '/'; --p) ; diff --git a/erts/etc/common/escript.c b/erts/etc/common/escript.c index 1bc5eb7651..6ed79c91e3 100644 --- a/erts/etc/common/escript.c +++ b/erts/etc/common/escript.c @@ -151,6 +151,9 @@ find_prog(char *origpath) char relpath[PMAX]; char abspath[PMAX]; + if (strlen(origpath) >= sizeof(relpath)) + error("Path too long"); + strcpy(relpath, origpath); if (strstr(relpath, DIRSEPSTR) == NULL) { @@ -180,19 +183,21 @@ find_prog(char *origpath) end = strstr(beg, PATHSEPSTR); if (end != NULL) { sz = end - beg; - strncpy(dir, beg, sz); - dir[sz] = '\0'; } else { sz = strlen(beg); - strcpy(dir, beg); look_for_sep = FALSE; } + if (sz >= sizeof(dir)) { + beg = end + 1; + continue; + } + strncpy(dir, beg, sz); + dir[sz] = '\0'; beg = end + 1; #ifdef __WIN32__ - strcpy(wildcard, dir); - strcat(wildcard, DIRSEPSTR); - strcat(wildcard, relpath); /* basename */ + erts_snprintf(wildcard, sizeof(wildcard), "%s" DIRSEPSTR "%s", + dir, relpath /* basename */); dir_handle = FindFirstFile(wildcard, &find_data); if (dir_handle == INVALID_HANDLE_VALUE) { /* Try next directory in path */ @@ -217,9 +222,8 @@ find_prog(char *origpath) if (strcmp(origpath, dirp->d_name) == 0) { /* Wow we found the executable. */ - strcpy(relpath, dir); - strcat(relpath, DIRSEPSTR); - strcat(relpath, dirp->d_name); + erts_snprintf(relpath, sizeof(relpath), "%s" DIRSEPSTR "%s", + dir, dirp->d_name); closedir(dp); look_for_sep = FALSE; break; @@ -291,7 +295,7 @@ append_shebang_args(char* scriptname) /* Find end of arg */ end = beg; - while (end && end[0] != ' ') { + while (end && end < (linebuf+LINEBUFSZ-1) && end[0] != ' ') { if (end[0] == '\n') { newline = TRUE; end[0]= '\0'; @@ -335,13 +339,16 @@ main(int argc, char** argv) emulator = get_default_emulator(argv[0]); } + if (strlen(emulator) >= PMAX) + error("Value of environment variable ESCRIPT_EMULATOR is too large"); + /* * Allocate the argv vector to be used for arguments to Erlang. * Arrange for starting to pushing information in the middle of * the array, to allow easy addition of commands in the beginning. */ - eargv_size = argc*4+1000; + eargv_size = argc*4+1000+LINEBUFSZ/2; eargv_base = (char **) emalloc(eargv_size*sizeof(char*)); eargv = eargv_base; eargc = 0; @@ -387,7 +394,8 @@ main(int argc, char** argv) if (argc <= 1) { error("Missing filename\n"); } - strcpy(scriptname, argv[1]); + strncpy(scriptname, argv[1], sizeof(scriptname)); + scriptname[sizeof(scriptname)-1] = '\0'; argc--; argv++; } else { @@ -395,16 +403,17 @@ main(int argc, char** argv) int len; #endif absname = find_prog(argv[0]); - strcpy(scriptname, absname); - efree(absname); #ifdef __WIN32__ - len = strlen(scriptname); - if (len >= 4 && _stricmp(scriptname+len-4, ".exe") == 0) { - scriptname[len-4] = '\0'; + len = strlen(absname); + if (len >= 4 && _stricmp(absname+len-4, ".exe") == 0) { + absname[len-4] = '\0'; } #endif - strcat(scriptname, ".escript"); + erts_snprintf(scriptname, sizeof(scriptname), "%s.escript", + absname); + efree(absname); + } /* @@ -455,7 +464,7 @@ main(int argc, char** argv) static void push_words(char* src) { - char sbuf[1024]; + char sbuf[PMAX]; char* dst; dst = sbuf; @@ -584,7 +593,7 @@ error(char* format, ...) va_list ap; va_start(ap, format); - vsprintf(sbuf, format, ap); + erts_vsnprintf(sbuf, sizeof(sbuf), format, ap); va_end(ap); fprintf(stderr, "escript: %s\n", sbuf); exit(1); @@ -619,6 +628,9 @@ get_default_emulator(char* progname) char sbuf[MAXPATHLEN]; char* s; + if (strlen(progname) >= sizeof(sbuf)) + return ERL_NAME; + strcpy(sbuf, progname); for (s = sbuf+strlen(sbuf); s >= sbuf; s--) { if (IS_DIRSEP(*s)) { diff --git a/erts/etc/common/heart.c b/erts/etc/common/heart.c index 4f738947b7..3e19e5f386 100644 --- a/erts/etc/common/heart.c +++ b/erts/etc/common/heart.c @@ -375,7 +375,8 @@ main(int argc, char **argv) _setmode(erlin_fd,_O_BINARY); _setmode(erlout_fd,_O_BINARY); #endif - strcpy(program_name, argv[0]); + strncpy(program_name, argv[0], sizeof(program_name)); + program_name[sizeof(program_name)-1] = '\0'; notify_ack(erlout_fd); cmd[0] = '\0'; do_terminate(message_loop(erlin_fd,erlout_fd)); @@ -728,7 +729,11 @@ heart_cmd_reply(int fd, char *s) struct msg m; int len = strlen(s) + 1; /* Include \0 */ - /* FIXME if s >= MSG_BODY_SIZE error */ + /* if s >= MSG_BODY_SIZE, return a write + * failure immediately. + */ + if (len > sizeof(m.fill)) + return -1; m.op = HEART_CMD; m.len = htons(len + 2); /* Include Op */ diff --git a/erts/etc/common/run_test.c b/erts/etc/common/run_test.c index 016d9c6afd..042b8571ca 100644 --- a/erts/etc/common/run_test.c +++ b/erts/etc/common/run_test.c @@ -164,11 +164,13 @@ main(int argc, char** argv) erl_args = cnt; } else if (strcmp(argv[1], "-sname") == 0) { - strcpy(nodename, argv[2]); + strncpy(nodename, argv[2], sizeof(nodename)); + nodename[sizeof(nodename)-1] = '\0'; cnt++, argv++; } else if (strcmp(argv[1], "-name") == 0) { - strcpy(nodename, argv[2]); + strncpy(nodename, argv[2], sizeof(nodename)); + nodename[sizeof(nodename)-1] = '\0'; dist_mode = FULL_NAME; cnt++, argv++; } @@ -178,7 +180,8 @@ main(int argc, char** argv) ct_mode = VTS_MODE; } else if (strcmp(argv[1], "-browser") == 0) { - strcpy(browser, argv[2]); + strncpy(browser, argv[2], sizeof(browser)); + browser[sizeof(browser)-1] = '\0'; cnt++, argv++; } else if (strcmp(argv[1], "-shell") == 0) { @@ -189,7 +192,8 @@ main(int argc, char** argv) ct_mode = MASTER_MODE; } else if (strcmp(argv[1], "-ctname") == 0) { - strcpy(nodename, argv[2]); + strncpy(nodename, argv[2], sizeof(nodename)); + nodename[sizeof(nodename)-1] = '\0'; ct_mode = ERL_SHELL_MODE; cnt++, argv++; } @@ -273,7 +277,7 @@ main(int argc, char** argv) static void push_words(char* src) { - char sbuf[1024]; + char sbuf[MAXPATHLEN]; char* dst; dst = sbuf; @@ -405,7 +409,7 @@ error(char* format, ...) va_list ap; va_start(ap, format); - vsprintf(sbuf, format, ap); + erts_vsnprintf(sbuf, sizeof(sbuf), format, ap); va_end(ap); fprintf(stderr, "run_test: %s\n", sbuf); exit(1); @@ -434,6 +438,9 @@ get_default_emulator(char* progname) char sbuf[MAXPATHLEN]; char* s; + if (strlen(progname) >= sizeof(sbuf)) + return ERL_NAME; + strcpy(sbuf, progname); for (s = sbuf+strlen(sbuf); s >= sbuf; s--) { if (IS_DIRSEP(*s)) { diff --git a/erts/etc/common/typer.c b/erts/etc/common/typer.c index c2567cb8b4..de48daf002 100644 --- a/erts/etc/common/typer.c +++ b/erts/etc/common/typer.c @@ -175,7 +175,7 @@ main(int argc, char** argv) static void push_words(char* src) { - char sbuf[1024]; + char sbuf[MAXPATHLEN]; char* dst; dst = sbuf; @@ -307,7 +307,7 @@ error(char* format, ...) va_list ap; va_start(ap, format); - vsprintf(sbuf, format, ap); + erts_vsnprintf(sbuf, sizeof(sbuf), format, ap); va_end(ap); fprintf(stderr, "typer: %s\n", sbuf); exit(1); @@ -336,6 +336,9 @@ get_default_emulator(char* progname) char sbuf[MAXPATHLEN]; char* s; + if (strlen(progname) >= sizeof(sbuf)) + return ERL_NAME; + strcpy(sbuf, progname); for (s = sbuf+strlen(sbuf); s >= sbuf; s--) { if (IS_DIRSEP(*s)) { diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam Binary files differindex f1b54b7fcb..c8d3b78b35 100644 --- a/erts/preloaded/ebin/init.beam +++ b/erts/preloaded/ebin/init.beam diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl index 3b98b9cddc..3ab9a1cd6d 100644 --- a/erts/preloaded/src/init.erl +++ b/erts/preloaded/src/init.erl @@ -51,6 +51,9 @@ get_status/0,boot/1,get_arguments/0,get_plain_arguments/0, get_argument/1,script_id/0]). +%% for the on_load functionality; not for general use +-export([run_on_load_handlers/0]). + %% internal exports -export([fetch_loaded/0,ensure_loaded/1,make_permanent/2, notify_when_started/1,wait_until_started/0, @@ -308,24 +311,6 @@ boot_loop(BootPid, State) -> {stop,Reason} -> stop(Reason,State); {From,fetch_loaded} -> %% Fetch and reset initially loaded modules. - case whereis(?ON_LOAD_HANDLER) of - undefined -> - %% There is no on_load handler process, - %% probably because init:restart/0 has been - %% called and it is not the first time we - %% pass through here. - ok; - Pid when is_pid(Pid) -> - Pid ! run_on_load, - receive - {'EXIT',Pid,on_load_done} -> - ok; - {'EXIT',Pid,Res} -> - %% Failure to run an on_load handler. - %% This is fatal during start-up. - exit(Res) - end - end, From ! {init,State#state.loaded}, garb_boot_loop(BootPid,State#state{loaded = []}); {From,{ensure_loaded,Module}} -> @@ -736,6 +721,7 @@ do_boot(Init,Flags,Start) -> BootList = get_boot(BootFile,Root), LoadMode = b2a(get_flag('-mode',Flags,false)), Deb = b2a(get_flag('-init_debug',Flags,false)), + catch ?ON_LOAD_HANDLER ! {init_debug_flag,Deb}, BootVars = get_flag_args('-boot_var',Flags), ParallelLoad = (Pgm =:= "efile") and (erlang:system_info(thread_pool_size) > 0), @@ -1335,23 +1321,44 @@ archive_extension() -> %%% Support for handling of on_load functions. %%% +run_on_load_handlers() -> + Ref = monitor(process, ?ON_LOAD_HANDLER), + catch ?ON_LOAD_HANDLER ! run_on_load, + receive + {'DOWN',Ref,process,_,noproc} -> + %% There is no on_load handler process, + %% probably because init:restart/0 has been + %% called and it is not the first time we + %% pass through here. + ok; + {'DOWN',Ref,process,_,on_load_done} -> + ok; + {'DOWN',Ref,process,_,Res} -> + %% Failure to run an on_load handler. + %% This is fatal during start-up. + exit(Res) + end. + start_on_load_handler_process() -> register(?ON_LOAD_HANDLER, - spawn_link(fun on_load_handler_init/0)). + spawn(fun on_load_handler_init/0)). on_load_handler_init() -> - on_load_loop([]). + on_load_loop([], false). -on_load_loop(Mods) -> +on_load_loop(Mods, Debug0) -> receive + {init_debug_flag,Debug} -> + on_load_loop(Mods, Debug); {loaded,Mod} -> - on_load_loop([Mod|Mods]); + on_load_loop([Mod|Mods], Debug0); run_on_load -> - run_on_load_handlers(Mods), + run_on_load_handlers(Mods, Debug0), exit(on_load_done) end. -run_on_load_handlers([M|Ms]) -> +run_on_load_handlers([M|Ms], Debug) -> + debug(Debug, {running_on_load_handler,M}), Fun = fun() -> Res = erlang:call_on_load_function(M), exit(Res) @@ -1363,9 +1370,12 @@ run_on_load_handlers([M|Ms]) -> erlang:finish_after_on_load(M, Keep), case Keep of false -> - exit({on_load_function_failed,M}); + Error = {on_load_function_failed,M}, + debug(Debug, Error), + exit(Error); true -> - run_on_load_handlers(Ms) + debug(Debug, {on_load_handler_returned_ok,M}), + run_on_load_handlers(Ms, Debug) end end; -run_on_load_handlers([]) -> ok. +run_on_load_handlers([], _) -> ok. diff --git a/erts/test/erlc_SUITE.erl b/erts/test/erlc_SUITE.erl index 437f020f99..4797f78be2 100644 --- a/erts/test/erlc_SUITE.erl +++ b/erts/test/erlc_SUITE.erl @@ -21,13 +21,13 @@ %% Tests the erlc command by compiling various types of files. -export([all/1, compile_erl/1, compile_yecc/1, compile_script/1, - compile_mib/1, good_citizen/1, deep_cwd/1]). + compile_mib/1, good_citizen/1, deep_cwd/1, arg_overflow/1]). -include_lib("test_server/include/test_server.hrl"). all(suite) -> [compile_erl, compile_yecc, compile_script, compile_mib, - good_citizen, deep_cwd]. + good_citizen, deep_cwd, arg_overflow]. %% Copy from erlc_SUITE_data/include/erl_test.hrl. @@ -189,6 +189,18 @@ deep_cwd_1(PrivDir) -> ?line true = filelib:is_file("test.beam"), ok. +%% Test that a large number of command line switches does not +%% overflow the argument buffer +arg_overflow(Config) when is_list(Config) -> + ?line {SrcDir, _OutDir, Cmd} = get_cmd(Config), + ?line FileName = filename:join(SrcDir, "erl_test_ok.erl"), + ?line Args = lists:flatten([ ["-D", integer_to_list(N), "=1 "] || + N <- lists:seq(1,10000) ]), + ?line run(Config, Cmd, FileName, Args, + ["Warning: function foo/0 is unused\$", + "_OK_"]), + ok. + erlc() -> case os:find_executable("erlc") of false -> diff --git a/erts/test/erlexec_SUITE.erl b/erts/test/erlexec_SUITE.erl index 164ce9faaf..6adb865f6d 100644 --- a/erts/test/erlexec_SUITE.erl +++ b/erts/test/erlexec_SUITE.erl @@ -33,7 +33,7 @@ -export([all/1, init_per_testcase/2, fin_per_testcase/2]). --export([args_file/1, evil_args_file/1, env/1, args_file_env/1, otp_7461/1, otp_7461_remote/1, otp_8209/1]). +-export([args_file/1, evil_args_file/1, env/1, args_file_env/1, otp_7461/1, otp_7461_remote/1, otp_8209/1, zdbbl_dist_buf_busy_limit/1]). -include_lib("test_server/include/test_server.hrl"). @@ -53,7 +53,8 @@ fin_per_testcase(_Case, Config) -> all(doc) -> []; all(suite) -> - [args_file, evil_args_file, env, args_file_env, otp_7461, otp_8209]. + [args_file, evil_args_file, env, args_file_env, otp_7461, otp_8209, + zdbbl_dist_buf_busy_limit]. otp_8209(doc) -> @@ -330,6 +331,25 @@ otp_7461_remote([halt, Pid]) -> io:format("halt order from ~p to node ~p\n",[Pid,node()]), halt(). +zdbbl_dist_buf_busy_limit(doc) -> + ["Check +zdbbl flag"]; +zdbbl_dist_buf_busy_limit(suite) -> + []; +zdbbl_dist_buf_busy_limit(Config) when is_list(Config) -> + LimKB = 1122233, + LimB = LimKB*1024, + ?line {ok,[[PName]]} = init:get_argument(progname), + ?line SNameS = "erlexec_test_02", + ?line SName = list_to_atom(SNameS++"@"++ + hd(tl(string:tokens(atom_to_list(node()),"@")))), + ?line Cmd = PName ++ " -sname "++SNameS++" -setcookie "++ + atom_to_list(erlang:get_cookie()) ++ + " +zdbbl " ++ integer_to_list(LimKB), + ?line open_port({spawn,Cmd},[]), + ?line pong = loop_ping(SName,40), + ?line LimB = rpc:call(SName,erlang,system_info,[dist_buf_busy_limit]), + ?line ok = cleanup_node(SNameS, 10), + ok. %% diff --git a/lib/common_test/doc/src/ct_slave.xml b/lib/common_test/doc/src/ct_slave.xml deleted file mode 100644 index ceebf51f1a..0000000000 --- a/lib/common_test/doc/src/ct_slave.xml +++ /dev/null @@ -1,139 +0,0 @@ -<?xml version="1.0" encoding="latin1" ?> -<!DOCTYPE erlref SYSTEM "erlref.dtd"> -<erlref> -<header> -<title>ct_slave</title> -<prepared></prepared> -<responsible></responsible> -<docno>1</docno> -<approved></approved> -<checked></checked> -<date></date> -<rev>A</rev> -<file>ct_slave.xml</file></header> -<module>ct_slave</module> -<modulesummary>Common Test Framework functions for starting and stopping nodes for -Large Scale Testing.</modulesummary> -<description> -<p>Common Test Framework functions for starting and stopping nodes for -Large Scale Testing.</p> - - <p>This module exports functions which are used by the Common Test Master - to start and stop "slave" nodes. It is the default callback module for the - <c>{init, node_start}</c> term of the Test Specification.</p></description> -<funcs> -<func> -<name>start(Node) -> Result</name> -<fsummary>Starts an Erlang node with name Node on the local host.</fsummary> -<type> -<v>Node = atom()</v><v>Result = {ok, NodeName} | {error, already_started, NodeName} | {error, started_not_connected, NodeName} | {error, boot_timeout, NodeName} | {error, init_timeout, NodeName} | {error, startup_timeout, NodeName} | {error, not_alive, NodeName}</v><v>NodeName = atom()</v></type> -<desc><marker id="start-1"/> - -<p>Starts an Erlang node with name <c>Node</c> on the local host.</p> -<p><em>See also:</em> <seealso marker="#start-3">start/3</seealso>.</p> -</desc></func> -<func> -<name>start(Host, Node) -> Result</name> -<fsummary>Starts an Erlang node with name Node on host - Host with the default options.</fsummary> -<type> -<v>Node = atom()</v><v>Host = atom()</v><v>Result = {ok, NodeName} | {error, already_started, NodeName} | {error, started_not_connected, NodeName} | {error, boot_timeout, NodeName} | {error, init_timeout, NodeName} | {error, startup_timeout, NodeName} | {error, not_alive, NodeName}</v><v>NodeName = atom()</v></type> -<desc><marker id="start-2"/> - -<p>Starts an Erlang node with name <c>Node</c> on host - <c>Host</c> with the default options.</p> -<p><em>See also:</em> <seealso marker="#start-3">start/3</seealso>.</p> -</desc></func> -<func> -<name>start(Host, Node, Options::Opts) -> Result</name> -<fsummary>Starts an Erlang node with name Node on host - Host as specified by the combination of options in - Opts.</fsummary> -<type> -<v>Node = atom()</v><v>Host = atom()</v><v>Opts = [OptTuples]</v><v>OptTuples = {username, Username} | {password, Password} | {boot_timeout, BootTimeout} | {init_timeout, InitTimeout} | {startup_timeout, StartupTimeout} | {startup_functions, StartupFunctions} | {monitor_master, Monitor} | {kill_if_fail, KillIfFail} | {erl_flags, ErlangFlags}</v><v>Username = string()</v><v>Password = string()</v><v>BootTimeout = integer()</v><v>InitTimeout = integer()</v><v>StartupTimeout = integer()</v><v>StartupFunctions = [StartupFunctionSpec]</v><v>StartupFunctionSpec = {Module, Function, Arguments}</v><v>Module = atom()</v><v>Function = atom()</v><v>Arguments = [term]</v><v>Monitor = bool()</v><v>KillIfFail = bool()</v><v>ErlangFlags = string()</v><v>Result = {ok, NodeName} | {error, already_started, NodeName} | {error, started_not_connected, NodeName} | {error, boot_timeout, NodeName} | {error, init_timeout, NodeName} | {error, startup_timeout, NodeName} | {error, not_alive, NodeName}</v><v>NodeName = atom()</v></type> -<desc><marker id="start-3"/> - -<p>Starts an Erlang node with name <c>Node</c> on host - <c>Host</c> as specified by the combination of options in - <c>Opts</c>.</p> - - <p>Options <c>Username</c> and <c>Password</c> will be used - to log in onto the remote host <c>Host</c>. - Username, if omitted, defaults to the current user name, - and password is empty by default.</p> - - <p>A list of functions specified in the <c>Startup</c> option will be - executed after startup of the node. Note that all used modules should be - present in the code path on the <c>Host</c>.</p> - - <p>The timeouts are applied as follows: - <list> - <item> - <c>BootTimeout</c> - time to start the Erlang node, in seconds. - Defaults to 3 seconds. If node does not become pingable within this time, - the result <c>{error, boot_timeout, NodeName}</c> is returned; - </item> - <item> - <c>InitTimeout</c> - time to wait for the node until it calls the - internal callback function informing master about successfull startup. - Defaults to one second. - In case of timed out message the result - <c>{error, init_timeout, NodeName}</c> is returned; - </item> - <item> - <c>StartupTimeout</c> - time to wait intil the node finishes to run - the <c>StartupFunctions</c>. Defaults to one second. - If this timeout occurs, the result - <c>{error, startup_timeout, NodeName}</c> is returned. - </item> - </list></p> - - <p>Option <c>monitor_master</c> specifies, if the slave node should be - stopped in case of master node stop. Defaults to false.</p> - - <p>Option <c>kill_if_fail</c> specifies, if the slave node should be - killed in case of a timeout during initialization or startup. - Defaults to true. Note that node also may be still alive it the boot - timeout occurred, but it will not be killed in this case.</p> - - <p>Option <c>erlang_flags</c> specifies, which flags will be added - to the parameters of the <c>erl</c> executable.</p> - - <p>Special return values are: - <list> - <item><c>{error, already_started, NodeName}</c> - if the node with - the given name is already started on a given host;</item> - <item><c>{error, started_not_connected, NodeName}</c> - if node is - started, but not connected to the master node.</item> - <item><c>{error, not_alive, NodeName}</c> - if node on which the - <c>ct_slave:start/3</c> is called, is not alive. Note that - <c>NodeName</c> is the name of current node in this case.</item> - </list></p> - -</desc></func> -<func> -<name>stop(Node) -> Result</name> -<fsummary>Stops the running Erlang node with name Node on - the localhost.</fsummary> -<type> -<v>Node = atom()</v><v>Result = {ok, NodeName} | {error, not_started, NodeName} | {error, not_connected, NodeName} | {error, stop_timeout, NodeName}</v><v>NodeName = atom()</v></type> -<desc><marker id="stop-1"/> - -<p>Stops the running Erlang node with name <c>Node</c> on - the localhost.</p> -</desc></func> -<func> -<name>stop(Host, Node) -> Result</name> -<fsummary>Stops the running Erlang node with name Node on - host Host.</fsummary> -<type> -<v>Host = atom()</v><v>Node = atom()</v><v>Result = {ok, NodeName} | {error, not_started, NodeName} | {error, not_connected, NodeName} | {error, stop_timeout, NodeName}</v><v>NodeName = atom()</v></type> -<desc><marker id="stop-2"/> - -<p>Stops the running Erlang node with name <c>Node</c> on - host <c>Host</c>.</p> -</desc></func></funcs> - -<authors> -<aname> </aname> -<email> </email></authors></erlref>
\ No newline at end of file diff --git a/lib/common_test/priv/Makefile.in b/lib/common_test/priv/Makefile.in index 6372bbc8d5..a6ac0f1a02 100644 --- a/lib/common_test/priv/Makefile.in +++ b/lib/common_test/priv/Makefile.in @@ -56,8 +56,8 @@ ifneq ($(findstring win32,$(TARGET)),win32) # # Files # -FILES = -SCRIPTS = +FILES = vts.tool +SCRIPTS = IMAGES = tile1.jpg # diff --git a/lib/common_test/src/ct.erl b/lib/common_test/src/ct.erl index 8ae175f10d..1dbf83ee10 100644 --- a/lib/common_test/src/ct.erl +++ b/lib/common_test/src/ct.erl @@ -694,7 +694,7 @@ userdata(TestDir, Suite, Case) -> %%%----------------------------------------------------------------- -%%% @spec get_status() -> TestStatus | {error,Reason} +%%% @spec get_status() -> TestStatus | {error,Reason} | no_tests_running %%% TestStatus = [StatusElem] %%% StatusElem = {current,{Suite,TestCase}} | {successful,Successful} | %%% {failed,Failed} | {skipped,Skipped} | {total,Total} diff --git a/lib/common_test/src/ct_util.erl b/lib/common_test/src/ct_util.erl index 0a434666fa..b5ab4cbb6e 100644 --- a/lib/common_test/src/ct_util.erl +++ b/lib/common_test/src/ct_util.erl @@ -556,10 +556,37 @@ listenv(Telnet) -> %%% @hidden %%% @equiv ct:parse_table/1 parse_table(Data) -> - [Heading|Lines]= - [remove_space(string:tokens(L, "|"),[]) || L <- Data, hd(L)==$|], + {Heading, Rest} = get_headings(Data), + Lines = parse_row(Rest,[],size(Heading)), {Heading,Lines}. +get_headings(["|" ++ Headings | Rest]) -> + {remove_space(string:tokens(Headings, "|"),[]), Rest}; +get_headings([_ | Rest]) -> + get_headings(Rest); +get_headings([]) -> + {{},[]}. + +parse_row(["|" ++ _ = Row | T], Rows, NumCols) when NumCols > 1 -> + case string:tokens(Row, "|") of + Values when length(Values) =:= NumCols -> + parse_row(T,[remove_space(Values,[])|Rows], NumCols); + Values when length(Values) < NumCols -> + parse_row([Row ++"\n"++ hd(T) | tl(T)], Rows, NumCols) + end; +parse_row(["|" ++ _ = Row | T], Rows, 1 = NumCols) -> + case string:rchr(Row, $|) of + 1 -> + parse_row([Row ++"\n"++hd(T) | tl(T)], Rows, NumCols); + _Else -> + parse_row(T, [remove_space(string:tokens(Row,"|"),[])|Rows], + NumCols) + end; +parse_row([_Skip | T], Rows, NumCols) -> + parse_row(T, Rows, NumCols); +parse_row([], Rows, _NumCols) -> + lists:reverse(Rows). + remove_space([Str|Rest],Acc) -> remove_space(Rest,[string:strip(string:strip(Str),both,$')|Acc]); remove_space([],Acc) -> diff --git a/lib/common_test/test/ct_misc_1_SUITE.erl b/lib/common_test/test/ct_misc_1_SUITE.erl index eb6c6aa101..8c8b2d0d41 100644 --- a/lib/common_test/test/ct_misc_1_SUITE.erl +++ b/lib/common_test/test/ct_misc_1_SUITE.erl @@ -62,7 +62,7 @@ all(doc) -> all(suite) -> [ - beam_me_up + beam_me_up, parse_table ]. %%-------------------------------------------------------------------- @@ -106,6 +106,66 @@ beam_me_up(Config) when is_list(Config) -> TestEvents = events_to_check(beam_me_up, 1), ok = ct_test_support:verify_events(TestEvents, Events, Config). + +parse_table(suite) -> + [parse_table_empty, parse_table_single, + parse_table_multiline_row, + parse_table_one_column_multiline, + parse_table_one_column_simple]. + +parse_table_empty(Config) when is_list(Config) -> + + String = ["+----+-------+---------+---------+----------+------+--------+", + "| id | col11 | col2222 | col3333 | col4 | col5 | col6666 |", + "+----+-------+---------+---------+----------+------+--------+", + "+----+-------+---------+---------+----------+------+--------+", + "Query Done: 0 records selected"], + + {{"id","col11","col2222","col3333","col4","col5","col6666"},[]} = + ct:parse_table(String). + + +parse_table_single(Config) when is_list(Config) -> + + String = ["+------+--------+--------------+------------+------------------+---------+--------+---------+-----------+", + "| id | col1 | col2 | col3 | col4 | col5 | col6 | col7 | col8 |", +"+------+--------+--------------+------------+------------------+---------+--------+---------+-----------+", + "| 0 | 0 | -1407231560 | -256 | -1407231489 | 1500 | 1 | 1 | 1 |", + "+------+--------+--------------+------------+------------------+---------+--------+---------+-----------+" + "Query Done: 1 record selected"], + + {{"id","col1","col2","col3","col4","col5","col6","col7","col8"}, + [{"0","0","-1407231560","-256","-1407231489", "1500","1","1","1"}]} = + ct:parse_table(String). + +parse_table_multiline_row(Config) when is_list(Config) -> + + String = ["+------+--------+--------------+------------+------------------+---------+--------+---------+-----------+", + "| id | col1 | col2 | col3 | col4 | col5 | col6 | col7 | col8 |", +"+------+--------+--------------+------------+------------------+---------+--------+---------+-----------+", + "| 0 | 0 | Free test string", + " on more lines", + "than one", + "| -256 | -1407231489 | 1500 | 1 | 1 | 1 |", + "+------+--------+--------------+------------+------------------+---------+--------+---------+-----------+" + "Query Done: 1 record selected"], + + {{"id","col1","col2","col3","col4","col5","col6","col7","col8"}, + [{"0","0","Free test string\n on more lines\nthan one\n", + "-256","-1407231489", "1500","1","1","1"}]} = + ct:parse_table(String). + +parse_table_one_column_simple(Config) when is_list(Config) -> + + String = ["|test|","|test value|"], + + {{"test"},[{"test value"}]} = ct:parse_table(String). + +parse_table_one_column_multiline(Config) when is_list(Config) -> + String = ["|test|","|test","value|"], + + {{"test"},[{"test\nvalue"}]} = ct:parse_table(String). + %%%----------------------------------------------------------------- %%% HELP FUNCTIONS %%%----------------------------------------------------------------- diff --git a/lib/compiler/src/beam_block.erl b/lib/compiler/src/beam_block.erl index 9c6f835ab0..c45874597a 100644 --- a/lib/compiler/src/beam_block.erl +++ b/lib/compiler/src/beam_block.erl @@ -36,12 +36,13 @@ function({function,Name,Arity,CLabel,Is0}, Lc0) -> %% Collect basic blocks and optimize them. Is2 = blockify(Is1), - Is3 = beam_utils:live_opt(Is2), - Is4 = opt_blocks(Is3), - Is5 = beam_utils:delete_live_annos(Is4), + Is3 = move_allocates(Is2), + Is4 = beam_utils:live_opt(Is3), + Is5 = opt_blocks(Is4), + Is6 = beam_utils:delete_live_annos(Is5), %% Optimize bit syntax. - {Is,Lc} = bsm_opt(Is5, Lc0), + {Is,Lc} = bsm_opt(Is6, Lc0), %% Done. {{function,Name,Arity,CLabel,Is},Lc} @@ -156,11 +157,7 @@ opt_blocks([I|Is]) -> opt_blocks([]) -> []. opt_block(Is0) -> - %% We explicitly move any allocate instruction upwards before optimising - %% moves, to avoid any potential problems with the calculation of live - %% registers. - Is1 = move_allocates(Is0), - Is = find_fixpoint(fun opt/1, Is1), + Is = find_fixpoint(fun opt/1, Is0), opt_alloc(Is). find_fixpoint(OptFun, Is0) -> @@ -170,11 +167,21 @@ find_fixpoint(OptFun, Is0) -> end. %% move_allocates(Is0) -> Is -%% Move allocates upwards in the instruction stream, in the hope of -%% getting more possibilities for optimizing away moves later. - -move_allocates(Is) -> - move_allocates_1(reverse(Is), []). +%% Move allocate instructions upwards in the instruction stream, in the +%% hope of getting more possibilities for optimizing away moves later. +%% +%% NOTE: Moving allocation instructions is only safe because it is done +%% immediately after code generation so that we KNOW that if {x,X} is +%% initialized, all x registers with lower numbers are also initialized. +%% That assumption may not be true after other optimizations, such as +%% the beam_utils:live_opt/1 optimization. + +move_allocates([{block,Bl0}|Is]) -> + Bl = move_allocates_1(reverse(Bl0), []), + [{block,Bl}|move_allocates(Is)]; +move_allocates([I|Is]) -> + [I|move_allocates(Is)]; +move_allocates([]) -> []. move_allocates_1([{set,[],[],{alloc,_,_}=Alloc}|Is0], Acc0) -> {Is,Acc} = move_allocates_2(Alloc, Is0, Acc0), diff --git a/lib/compiler/src/cerl.erl b/lib/compiler/src/cerl.erl index d1fd9d40e2..4b74d60e9f 100644 --- a/lib/compiler/src/cerl.erl +++ b/lib/compiler/src/cerl.erl @@ -973,7 +973,7 @@ atom_name(Node) -> %% TODO: replace the use of the unofficial 'write_string/2'. --spec atom_lit(cerl()) -> string(). +-spec atom_lit(cerl()) -> nonempty_string(). atom_lit(Node) -> io_lib:write_string(atom_name(Node), $'). %' stupid Emacs. @@ -1079,7 +1079,7 @@ char_val(Node) -> %% %% @see c_char/1 --spec char_lit(c_literal()) -> string(). +-spec char_lit(c_literal()) -> nonempty_string(). char_lit(Node) -> io_lib:write_char(char_val(Node)). @@ -1178,7 +1178,7 @@ string_val(Node) -> %% %% @see c_string/1 --spec string_lit(c_literal()) -> string(). +-spec string_lit(c_literal()) -> nonempty_string(). string_lit(Node) -> io_lib:write_string(string_val(Node)). diff --git a/lib/compiler/test/receive_SUITE.erl b/lib/compiler/test/receive_SUITE.erl index fca3f0387b..2a592dd669 100644 --- a/lib/compiler/test/receive_SUITE.erl +++ b/lib/compiler/test/receive_SUITE.erl @@ -21,7 +21,7 @@ -module(receive_SUITE). -export([all/1,init_per_testcase/2,fin_per_testcase/2, - recv/1,coverage/1,otp_7980/1,ref_opt/1]). + recv/1,coverage/1,otp_7980/1,ref_opt/1,export/1]). -include("test_server.hrl"). @@ -36,7 +36,7 @@ fin_per_testcase(_Case, Config) -> all(suite) -> test_lib:recompile(?MODULE), - [recv,coverage,otp_7980,ref_opt]. + [recv,coverage,otp_7980,ref_opt,export]. -record(state, {ena = true}). @@ -205,4 +205,25 @@ collect_recv_opt_instrs(Code) -> end] || {function,_,_,_,Is} <- Code], lists:append(L). +export(Config) when is_list(Config) -> + Ref = make_ref(), + ?line self() ! {result,Ref,42}, + ?line 42 = export_1(Ref), + ?line {error,timeout} = export_1(Ref), + ok. + +export_1(Reference) -> + id(Reference), + receive + {result,Reference,Result} -> + Result + after 1 -> + Result = {error,timeout} + end, + %% Result ({x,1}) is used, but not the return value ({x,0}) + %% of the receive. Used to be incorrectly optimized + %% by beam_block. + id({build,self()}), + Result. + id(I) -> I. diff --git a/lib/cosFileTransfer/test/Makefile b/lib/cosFileTransfer/test/Makefile new file mode 100644 index 0000000000..60f72644bd --- /dev/null +++ b/lib/cosFileTransfer/test/Makefile @@ -0,0 +1,132 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 2000-2010. All Rights Reserved. +# +# The contents of this file are subject to the Erlang Public License, +# Version 1.1, (the "License"); you may not use this file except in +# compliance with the License. You should have received a copy of the +# Erlang Public License along with this software. If not, it can be +# retrieved online at http://www.erlang.org/. +# +# Software distributed under the License is distributed on an "AS IS" +# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +# the License for the specific language governing rights and limitations +# under the License. +# +# %CopyrightEnd% +# +# +include $(ERL_TOP)/make/target.mk +include $(ERL_TOP)/make/$(TARGET)/otp.mk + +# ---------------------------------------------------- +# Application version +# ---------------------------------------------------- +include ../vsn.mk +VSN=$(COSFILETRANSFER_VSN) +# ---------------------------------------------------- +# Release directory specification +# ---------------------------------------------------- +RELSYSDIR = $(RELEASE_PATH)/cosFileTransfer_test + +# ---------------------------------------------------- +# Target Specs +# ---------------------------------------------------- +TEST_SPEC_FILE = cosFileTransfer.spec + + +IDL_FILES = + +IDLOUTDIR = idl_output + +MODULES = \ + fileTransfer_SUITE \ + +GEN_MODULES = \ + +GEN_HRL_FILES = \ + +ERL_FILES = $(MODULES:%=%.erl) + +HRL_FILES = + +GEN_FILES = \ + $(GEN_HRL_FILES:%=$(IDLOUTDIR)/%) \ + $(GEN_MODULES:%=$(IDLOUTDIR)/%.erl) + +GEN_TARGET_FILES = $(GEN_MODULES:%=$(IDLOUTDIR)/%.$(EMULATOR)) + +SUITE_TARGET_FILES = $(MODULES:%=%.$(EMULATOR)) + +TARGET_FILES = \ + $(GEN_TARGET_FILES) \ + $(SUITE_TARGET_FILES) + + +# ---------------------------------------------------- +# PROGRAMS +# ---------------------------------------------------- +LOCAL_CLASSPATH = $(ERL_TOP)lib/cosFileTransfer/priv:$(ERL_TOP)lib/cosFileTransfer/test +# ---------------------------------------------------- +# FLAGS +# ---------------------------------------------------- +ERL_IDL_FLAGS += -pa $(ERL_TOP)/lib/cosFileTransfer/ebin \ + -pa $(ERL_TOP)/lib/cosFileTransfer/src \ + -pa $(ERL_TOP)/lib/cosFileTransfer/include \ + -pa $(ERL_TOP)/lib/cosProperty/ebin \ + -pa $(ERL_TOP)/lib/cosProperty/include \ + -pa $(ERL_TOP)/lib/orber/ebin \ + -pa $(ERL_TOP)/lib/ic/ebin + +ERL_COMPILE_FLAGS += \ + $(ERL_IDL_FLAGS) \ + -pa $(ERL_TOP)/lib/orber/include \ + -pa $(ERL_TOP)/lib/cosProperty/include \ + -pa $(ERL_TOP)/internal_tools/test_server/ebin \ + -pa $(ERL_TOP)/lib/cosFileTransfer/ebin \ + -pa $(ERL_TOP)/lib/cosFileTransfer/include \ + -pa $(ERL_TOP)/lib/cosFileTransfer/test/idl_output \ + -I$(ERL_TOP)/lib/orber/include \ + -I$(ERL_TOP)/lib/cosProperty/include \ + -I$(ERL_TOP)/lib/cosFileTransfer/src \ + -I$(ERL_TOP)/lib/cosFileTransfer/include \ + -I$(ERL_TOP)/lib/cosFileTransfer \ + -I$(ERL_TOP)/lib/cosFileTransfer/test/$(IDLOUTDIR) \ + -I$(ERL_TOP)/lib/test_server/include + +# ---------------------------------------------------- +# Targets +# ---------------------------------------------------- + + +tests debug opt: $(TARGET_FILES) + +clean: + rm -f idl_output/* + rm -f $(TARGET_FILES) + rm -f errs core *~ + +docs: + +# ---------------------------------------------------- +# Special Targets +# ---------------------------------------------------- + +# ---------------------------------------------------- +# Release Targets +# ---------------------------------------------------- +# We don't copy generated intermediate erlang and hrl files + +include $(ERL_TOP)/make/otp_release_targets.mk + +release_spec: + +release_docs_spec: + +release_tests_spec: tests + $(INSTALL_DIR) $(RELSYSDIR) + $(INSTALL_DATA) $(IDL_FILES) $(TEST_SPEC_FILE) \ + $(ERL_FILES) $(RELSYSDIR) + $(INSTALL_DATA) $(SUITE_TARGET_FILES) $(RELSYSDIR) + chmod -f -R u+w $(RELSYSDIR) diff --git a/lib/cosFileTransfer/test/cosFileTransfer.spec b/lib/cosFileTransfer/test/cosFileTransfer.spec new file mode 100644 index 0000000000..80fe919f2a --- /dev/null +++ b/lib/cosFileTransfer/test/cosFileTransfer.spec @@ -0,0 +1 @@ +{topcase, {dir, "../cosFileTransfer_test"}}. diff --git a/lib/cosFileTransfer/test/fileTransfer_SUITE.erl b/lib/cosFileTransfer/test/fileTransfer_SUITE.erl new file mode 100644 index 0000000000..f877e3ceda --- /dev/null +++ b/lib/cosFileTransfer/test/fileTransfer_SUITE.erl @@ -0,0 +1,954 @@ +%%----------------------------------------------------------------------- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2000-2010. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% +%%---------------------------------------------------------------------- +%% File : fileTransfer_SUITE.erl +%% Purpose : +%%---------------------------------------------------------------------- + +-module(fileTransfer_SUITE). + + + +%%--------------- INCLUDES ----------------------------------- +-include_lib("cosFileTransfer/src/cosFileTransferApp.hrl"). + +-include("test_server.hrl"). + +%%--------------- DEFINES ------------------------------------ +-define(default_timeout, ?t:minutes(20)). +-define(match(ExpectedRes, Expr), + fun() -> + AcTuAlReS = (catch (Expr)), + case AcTuAlReS of + ExpectedRes -> + io:format("------ CORRECT RESULT ------~n~p~n", + [AcTuAlReS]), + AcTuAlReS; + _ -> + io:format("###### ERROR ERROR ######~n~p~n", + [AcTuAlReS]), + exit(AcTuAlReS) + end + end()). + +-define(matchnopr(ExpectedRes, Expr), + fun() -> + AcTuAlReS = (catch (Expr)), + case AcTuAlReS of + ExpectedRes -> + io:format("------ CORRECT RESULT (~p) ------~n", [?LINE]), + AcTuAlReS; + _ -> + io:format("###### ERROR ERROR ######~n~p~n", + [AcTuAlReS]), + exit(AcTuAlReS) + end + end()). + + + + + +%%----------------------------------------------------------------- +%% External exports +%%----------------------------------------------------------------- +-export([all/1, + cases/0, + init_all/1, + finish_all/1, + fileIterator_api/1, + fts_ftp_file_api/1, + fts_ftp_file_ssl_api/1, + fts_ftp_dir_api/1, + fts_native_file_api/1, + fts_native_file_ssl_api/1, + fts_native_dir_api/1, + init_per_testcase/2, + fin_per_testcase/2, + install_data/2, + uninstall_data/1, + slave_sup/0, + app_test/1]). + +%%----------------------------------------------------------------- +%% Func: all/1 +%% Args: +%% Returns: +%%----------------------------------------------------------------- +all(doc) -> ["API tests for the cosFileTransfer interfaces", ""]; +all(suite) -> {req, + [mnesia, orber], + {conf, init_all, cases(), finish_all}}. + +cases() -> + [fts_ftp_dir_api, fts_ftp_file_api, fts_ftp_file_ssl_api, + fts_native_dir_api, fts_native_file_api, fts_native_file_ssl_api, + fileIterator_api, app_test]. + +%%----------------------------------------------------------------- +%% Init and cleanup functions. +%%----------------------------------------------------------------- + +init_per_testcase(_Case, Config) -> + ?line Dog=test_server:timetrap(?default_timeout), + [{watchdog, Dog}|Config]. + + +fin_per_testcase(_Case, Config) -> + Dog = ?config(watchdog, Config), + test_server:timetrap_cancel(Dog), + ok. + +init_all(Config) -> + orber:jump_start(), + cosProperty:install(), + cosProperty:start(), + Dir = filename:join([code:lib_dir(ssl), "examples", "certs", "etc"]), + %% Client + cosFileTransferApp:configure(ssl_client_certfile, + filename:join([Dir, "client", "cert.pem"])), + cosFileTransferApp:configure(ssl_client_cacertfile, + filename:join([Dir, "client", "cacerts.pem"])), + cosFileTransferApp:configure(ssl_client_verify, 1), + cosFileTransferApp:configure(ssl_client_depth, 0), + %% Server + cosFileTransferApp:configure(ssl_server_certfile, + filename:join([Dir, "server", "cert.pem"])), + cosFileTransferApp:configure(ssl_server_cacertfile, + filename:join([Dir, "server", "cacerts.pem"])), + cosFileTransferApp:configure(ssl_server_verify, 1), + cosFileTransferApp:configure(ssl_server_depth, 0), + crypto:start(), + ssl:start(), + cosFileTransferApp:install(), + cosFileTransferApp:start(), + if + is_list(Config) -> + Config; + true -> + exit("Config not a list") + end. + +finish_all(Config) -> + ssl:stop(), + crypto:stop(), + cosFileTransferApp:stop(), + cosProperty:stop(), + cosProperty:uninstall(), + cosFileTransferApp:uninstall(), + orber:jump_stop(), + Config. + +%%----------------------------------------------------------------- +%% Local definitions +%%----------------------------------------------------------------- +-define(FTP_USER, "anonymous"). +-define(FTP_PASS, "fileTransfer_SUITE@localhost"). +-define(TEST_DIR,["/", "incoming"]). + + +-define(FTP_PORT, 21). +-define(FTP_ACC, "anonymous"). + +-define(BAD_HOST, "badhostname"). +-define(BAD_USER, "baduser"). +-define(BAD_DIR, "baddirectory"). + +-define(TEST_FILE_DATA, "If this file exists after a completed test an error occurred."). +-define(TEST_FILE_DATA2, "1234567890123"). + + +%%----------------------------------------------------------------- +%% aoo-file test +%%----------------------------------------------------------------- +app_test(doc) -> []; +app_test(suite) -> []; +app_test(_Config) -> + ?line ok=?t:app_test(cosFileTransfer), + ok. + +%%----------------------------------------------------------------- +%% FileIterator API tests +%%----------------------------------------------------------------- +fileIterator_api(doc) -> ["CosFileTransfer FileIterator API tests.", ""]; +fileIterator_api(suite) -> []; +fileIterator_api(Config) -> + case ftp_host(Config) of + {skipped, SkippedReason} -> + {skipped, SkippedReason}; + Host -> + + ?line {ok, Node} = create_node("fileIterator_api", 4008, normal), + ?line ?match(ok, remote_apply(Node, ?MODULE, install_data, + [tcp, {{'NATIVE', + 'cosFileTransferNATIVE_file'}, Host, + "fileIterator_api"}])), + + %% Create a Virtual File System. +%% ?line VFS = ?match({_,_,_,_,_,_}, +%% cosFileTransferApp:create_VFS({'NATIVE', +%% 'cosFileTransferNATIVE_file'}, +%% [], Host, ?FTP_PORT)), + ?line VFS = ?matchnopr({'IOP_IOR',"IDL:omg.org/CosFileTransfer/VirtualFileSystem:1.0",_}, + corba:string_to_object("corbaname::1.2@localhost:4008/NameService#fileIterator_api")), + + %% Start two File Transfer Sessions (Source and Target). + ?line {FS, Dir} = ?matchnopr({{_,_,_},{_,_,_}}, + 'CosFileTransfer_VirtualFileSystem':login(VFS, + ?FTP_USER, + ?FTP_PASS, + ?FTP_ACC)), + + %% Do some basic test on one of the Directories attributes. + ?line ?match([_H|_], 'CosFileTransfer_Directory':'_get_name'(Dir)), + ?line ?match([_H|_], 'CosFileTransfer_Directory':'_get_complete_file_name'(Dir)), + ?line ?match({'IOP_IOR',[],[]}, 'CosFileTransfer_Directory':'_get_parent'(Dir)), + ?line ?matchnopr(FS, 'CosFileTransfer_Directory':'_get_associated_session'(Dir)), + {ok,[],FileIter} = ?match({ok,[],_}, 'CosFileTransfer_Directory':list(Dir, 0)), + %% Usually the working directory for the test is not empty so no need for + %% creating files of our own?! + #any{value=Children} = ?match({any, _, _}, + 'CosPropertyService_PropertySet': + get_property_value(Dir, "num_children")), + + if + Children > 5 -> + ?line ?matchnopr({true, _}, 'CosFileTransfer_FileIterator':next_one(FileIter)), + ?line ?matchnopr({true, _}, 'CosFileTransfer_FileIterator':next_n(FileIter, 3)), + ?line ?matchnopr({true, _}, 'CosFileTransfer_FileIterator':next_n(FileIter, + Children)), + ?line ?matchnopr({false, _}, 'CosFileTransfer_FileIterator':next_one(FileIter)), + ?line ?match({false, []}, 'CosFileTransfer_FileIterator':next_n(FileIter, 1)), + ok; + true -> + ok + end, + ?line ?match(ok, 'CosFileTransfer_FileIterator':destroy(FileIter)), + ?line ?match(false, corba_object:non_existent(FS)), + ?line ?match(ok, 'CosFileTransfer_FileTransferSession':logout(FS)), + %% To make sure Orber can remove it from mnesia. + timer:sleep(1000), + ?line ?match(true, corba_object:non_existent(FS)), + ?line ?match(ok, remote_apply(Node, ?MODULE, uninstall_data, ["fileIterator_api"])), + stop_orber_remote(Node, normal), + ok + end. + + +%%----------------------------------------------------------------- +%% FileTransferSession API tests +%%----------------------------------------------------------------- +fts_ftp_file_api(doc) -> ["CosFileTransfer FTP FileTransferSession API tests.", ""]; +fts_ftp_file_api(suite) -> []; +fts_ftp_file_api(Config) -> + ?line {ok, Node} = create_node("ftp_file_api", 4004, normal), + file_helper(Config, 'FTP', ?TEST_DIR, Node, 4004, "ftp_file_api", tcp). + +fts_ftp_file_ssl_api(doc) -> ["CosFileTransfer FTP FileTransferSession API tests.", ""]; +fts_ftp_file_ssl_api(suite) -> []; +fts_ftp_file_ssl_api(Config) -> + case os:type() of + vxworks -> + {skipped, "No SSL-support for VxWorks."}; + _ -> + ?line {ok, Node} = create_node("ftp_file_api_ssl", {4005, 1}, ssl), + file_helper(Config, 'FTP', ?TEST_DIR, Node, 4005, "ftp_file_api_ssl", ssl) + end. + +fts_native_file_api(doc) -> ["CosFileTransfer NATIVE FileTransferSession API tests.", ""]; +fts_native_file_api(suite) -> []; +fts_native_file_api(Config) -> + ?line {ok, Node} = create_node("native_file_api", 4006, normal), + {ok, Pwd} = file:get_cwd(), + file_helper(Config,{'NATIVE', 'cosFileTransferNATIVE_file'},filename:split(Pwd), + Node, 4006, "native_file_api", tcp). + +fts_native_file_ssl_api(doc) -> ["CosFileTransfer NATIVE FileTransferSession API tests.", ""]; +fts_native_file_ssl_api(suite) -> []; +fts_native_file_ssl_api(Config) -> + case os:type() of + vxworks -> + {skipped, "No SSL-support for VxWorks."}; + _ -> + ?line {ok, Node} = create_node("native_file_ssl_api", {4007, 1}, ssl), + {ok, Pwd} = file:get_cwd(), + file_helper(Config,{'NATIVE', 'cosFileTransferNATIVE_file'},filename:split(Pwd), + Node, 4007, "native_file_ssl_api", ssl) + end. + + + +file_helper(Config, WhichType, TEST_DIR, Node, Port, Name, Type) -> + case ftp_host(Config) of + {skipped, SkippedReason} -> + {skipped, SkippedReason}; + Host -> + TEST_SOURCE = TEST_DIR ++ [create_name(remove_me_source)], + TEST_SOURCE2 = TEST_DIR ++ [create_name(remove_me_source)], + TEST_TARGET = TEST_DIR ++ [create_name(remove_me_target)], + + io:format("<<<<<< CosFileTransfer Testing Configuration >>>>>>~n",[]), + io:format("Source: ~p~nTarget: ~p~n", [TEST_SOURCE, TEST_TARGET]), + + ?line ?match(ok, remote_apply(Node, ?MODULE, install_data, + [Type, {WhichType, Host, Name}])), + + ?line VFST = ?match({'IOP_IOR',"IDL:omg.org/CosFileTransfer/VirtualFileSystem:1.0",_}, + corba:string_to_object("corbaname::1.2@localhost:"++integer_to_list(Port)++"/NameService#"++Name)), + + + %% Create a Virtual File System. + ?line VFS = ?match({_,_,_,_,_,_}, + cosFileTransferApp:create_VFS(WhichType, [], Host, ?FTP_PORT, + [{protocol, Type}])), + %% Start two File Transfer Sessions (Source and Target). + ?line {FST, _DirT} = ?match({{_,_,_},{_,_,_}}, + 'CosFileTransfer_VirtualFileSystem':login(VFST, + ?FTP_USER, + ?FTP_PASS, + ?FTP_ACC)), + ?line {FSS, DirS} = ?match({{_,_,_,_,_,_},{_,_,_,_,_,_}}, + 'CosFileTransfer_VirtualFileSystem':login(VFS, + ?FTP_USER, + ?FTP_PASS, + ?FTP_ACC)), + + %% Do some basic test on one of the Directories attributes. + ?line ?match([_H|_], 'CosFileTransfer_Directory':'_get_name'(DirS)), + ?line ?match([_H|_], 'CosFileTransfer_Directory':'_get_complete_file_name'(DirS)), + ?line ?match({'IOP_IOR',[],[]}, 'CosFileTransfer_Directory':'_get_parent'(DirS)), + ?line ?match(FSS, 'CosFileTransfer_Directory':'_get_associated_session'(DirS)), + + %% Get a FileList before we create any new Files + ?line #'CosFileTransfer_FileWrapper'{the_file = Dir} = + ?match({'CosFileTransfer_FileWrapper', _, ndirectory}, + 'CosFileTransfer_FileTransferSession':get_file(FSS, TEST_DIR)), + ?line {ok,FileList, Iter1} = ?match({ok,_,_}, 'CosFileTransfer_Directory':list(Dir, 10)), + ?line loop_files(FileList), + + case Iter1 of + {'IOP_IOR',[],[]} -> + ok; + _-> + ?line ?match(ok, 'CosFileTransfer_FileIterator':destroy(Iter1)) + end, + + #any{value=Count1} = ?match({any, _, _}, 'CosPropertyService_PropertySet': + get_property_value(Dir, "num_children")), + + %% Now we want to transfer a file from source to target. First, we'll create + %% a a file to work with. + ?line create_file_on_source_node(WhichType, Config, Host, + filename:join(TEST_SOURCE), TEST_DIR, + ?TEST_FILE_DATA), + ?line create_file_on_source_node(WhichType, Config, Host, + filename:join(TEST_SOURCE2), TEST_DIR, + ?TEST_FILE_DATA2), + + ?line #'CosFileTransfer_FileWrapper'{the_file = FileS} = + ?matchnopr({'CosFileTransfer_FileWrapper', _, nfile}, + 'CosFileTransfer_FileTransferSession':get_file(FSS, TEST_SOURCE)), + ?line #'CosFileTransfer_FileWrapper'{the_file = FileS2} = + ?matchnopr({'CosFileTransfer_FileWrapper', _, nfile}, + 'CosFileTransfer_FileTransferSession':get_file(FSS, TEST_SOURCE2)), + + #any{value=Count2} = ?match({any, _, _}, 'CosPropertyService_PropertySet': + get_property_value(Dir, "num_children")), + timer:sleep(2000), + ?match(true, (Count1+2 == Count2)), + + %% Create a target File + ?line FileT = ?matchnopr({_,_,_}, + 'CosFileTransfer_FileTransferSession':create_file(FST, TEST_TARGET)), + %% Try to delete the non-existing file. + ?line ?match({'EXCEPTION', _}, + 'CosFileTransfer_FileTransferSession':delete(FST, FileT)), + + ?line ?match(ok, 'CosFileTransfer_FileTransferSession':transfer(FSS, FileS, FileT)), + + %% Remove this test when ftp supports append. + case WhichType of + {'NATIVE', 'cosFileTransferNATIVE_file'} -> + ?line ?match(ok, 'CosFileTransfer_FileTransferSession':append(FSS, FileS, FileT)), + ?line ?match(ok, 'CosFileTransfer_FileTransferSession':insert(FSS, FileS2, FileT, 7)); + _-> + ok + end, + + %% Delete source and target files + ?line ?match(ok, 'CosFileTransfer_FileTransferSession':delete(FSS, FileS)), + ?line ?match(ok, 'CosFileTransfer_FileTransferSession':delete(FSS, FileS2)), + ?line ?match(ok, 'CosFileTransfer_FileTransferSession':delete(FST, FileT)), + + %% Should be back where we started. + timer:sleep(2000), + #any{value=Count3} = ?match({any, _, _}, 'CosPropertyService_PropertySet': + get_property_value(Dir, "num_children")), + ?match(true, (Count1 == Count3)), + + + ?line ?match(false, corba_object:non_existent(FSS)), + ?line ?match(false, corba_object:non_existent(FST)), + ?line ?match(ok, 'CosFileTransfer_FileTransferSession':logout(FSS)), + ?line ?match(ok, 'CosFileTransfer_FileTransferSession':logout(FST)), + %% To make sure Orber can remove it from mnesia. + timer:sleep(2000), + ?line ?match(true, corba_object:non_existent(FSS)), + ?line ?match(true, corba_object:non_existent(FST)), + ?line ?match(ok, remote_apply(Node, ?MODULE, uninstall_data, [Name])), + stop_orber_remote(Node, normal), + ok + end. + +%%----------------------------------------------------------------- +%% FileTransferSession API tests +%%----------------------------------------------------------------- +fts_ftp_dir_api(doc) -> ["CosFileTransfer FTP FileTransferSession API tests.", ""]; +fts_ftp_dir_api(suite) -> []; +fts_ftp_dir_api(Config) -> + ?line {ok, Node} = create_node("ftp_dir_api", 4009, normal), + dir_helper(Config, 'FTP', ?TEST_DIR, Node, 4009, "ftp_dir_api"). + + +fts_native_dir_api(doc) -> ["CosFileTransfer NATIVE FileTransferSession API tests.", ""]; +fts_native_dir_api(suite) -> []; +fts_native_dir_api(Config) -> + ?line {ok, Node} = create_node("native_dir_api", 4010, normal), + {ok, Pwd} = file:get_cwd(), + dir_helper(Config, {'NATIVE', 'cosFileTransferNATIVE_file'}, + filename:split(Pwd), Node, 4010, "native_dir_api"). + +dir_helper(Config, WhichType, TEST_DIR, Node, Port, Name) -> + case ftp_host(Config) of + {skipped, SkippedReason} -> + {skipped, SkippedReason}; + Host -> + TEST_DIR_LEVEL1 = TEST_DIR ++ [create_name(remove_me_dir1)], + TEST_DIR_LEVEL2 = TEST_DIR_LEVEL1 ++ [create_name(remove_me_dir2)], + + io:format("<<<<<< CosFileTransfer Testing Configuration >>>>>>~n",[]), + io:format("Top Dir: ~p~nLevel2 Dir: ~p~n", [TEST_DIR_LEVEL1, TEST_DIR_LEVEL2]), + + ?line ?match(ok, remote_apply(Node, ?MODULE, install_data, + [tcp, {WhichType, Host, Name}])), + + ?line VFS = ?matchnopr({'IOP_IOR',"IDL:omg.org/CosFileTransfer/VirtualFileSystem:1.0",_}, + corba:string_to_object("corbaname::1.2@localhost:"++integer_to_list(Port)++"/NameService#"++Name)), + + %% Start two File Transfer Sessions (Source and Target). + ?line {FS, DirS} = ?matchnopr({{'IOP_IOR',_,_}, _}, + 'CosFileTransfer_VirtualFileSystem':login(VFS, + ?FTP_USER, + ?FTP_PASS, + ?FTP_ACC)), + + %% Do some basic test on one of the Directories attributes. + ?line ?match([_H|_], 'CosFileTransfer_Directory':'_get_name'(DirS)), + ?line ?match([_H|_], 'CosFileTransfer_Directory':'_get_complete_file_name'(DirS)), + ?line ?match({'IOP_IOR',[],[]}, 'CosFileTransfer_Directory':'_get_parent'(DirS)), + ?line ?matchnopr(FS, 'CosFileTransfer_Directory':'_get_associated_session'(DirS)), + + %% Create a Root Directory. Currently we only need to create one but + %% later on, when supporting other protocols than FTP it's not enough. + ?line Dir1 = 'CosFileTransfer_FileTransferSession':create_directory(FS, + TEST_DIR_LEVEL1), + io:format("<<<<<< CosFileTransfer Testing Properties >>>>>>~n",[]), + ?line ?match({ok, [tk_long, tk_boolean]}, + 'CosFileTransfer_Directory':get_allowed_property_types(Dir1)), + ?line ?match({ok, [_,_]}, + 'CosFileTransfer_Directory':get_allowed_properties(Dir1)), + ?line ?match({'EXCEPTION', _}, + 'CosFileTransfer_Directory':define_property_with_mode(Dir1, + "num_children", + #any{typecode=tk_long, value=0}, + fixed_readonly)), + ?line ?match({'EXCEPTION', _}, + 'CosFileTransfer_Directory':define_property_with_mode(Dir1, + "wrong", + #any{typecode=tk_long, value=0}, + fixed_readonly)), + ?line ?match({'EXCEPTION', _}, + 'CosFileTransfer_Directory':define_property_with_mode(Dir1, + "num_children", + #any{typecode=tk_short, value=0}, + fixed_readonly)), + ?line ?match({'EXCEPTION', _}, + 'CosFileTransfer_Directory':define_property_with_mode(Dir1, + "num_children", + #any{typecode=tk_long, value=0}, + fixed_normal)), + ?line ?match({'EXCEPTION', _}, + 'CosFileTransfer_Directory':define_properties_with_modes(Dir1, + [#'CosPropertyService_PropertyDef' + {property_name = "num_children", + property_value = #any{typecode=tk_long, value=0}, + property_mode = fixed_readonly}])), + ?line ?match(fixed_readonly, + 'CosFileTransfer_Directory':get_property_mode(Dir1, "num_children")), + ?line ?match({true, + [#'CosPropertyService_PropertyMode'{property_name = "num_children", + property_mode = fixed_readonly}]}, + 'CosFileTransfer_Directory':get_property_modes(Dir1, ["num_children"])), + ?line ?match({'EXCEPTION', _}, + 'CosFileTransfer_Directory':set_property_mode(Dir1, "num_children", fixed_readonly)), + + ?line ?match({'EXCEPTION', _}, + 'CosFileTransfer_Directory': + set_property_modes(Dir1, + [#'CosPropertyService_PropertyMode' + {property_name = "num_children", + property_mode = fixed_readonly}])), + ?line ?match({'EXCEPTION', _}, + 'CosFileTransfer_Directory': + set_property_modes(Dir1, + [#'CosPropertyService_PropertyMode' + {property_name = "wrong", + property_mode = fixed_readonly}])), + ?line ?match({'EXCEPTION', _}, + 'CosFileTransfer_Directory': + set_property_modes(Dir1, + [#'CosPropertyService_PropertyMode' + {property_name = "num_children", + property_mode = fixed_normal}])), + ?line ?match({'EXCEPTION', _}, + 'CosFileTransfer_Directory':define_property(Dir1, + "num_children", + #any{typecode=tk_long, value=0})), + ?line ?match({'EXCEPTION', _}, + 'CosFileTransfer_Directory':define_property(Dir1, + "wrong", + #any{typecode=tk_long, value=0})), + ?line ?match({'EXCEPTION', _}, + 'CosFileTransfer_Directory':define_property(Dir1, + "num_children", + #any{typecode=tk_short, value=0})), + + ?line ?match({'EXCEPTION', _}, + 'CosFileTransfer_Directory':define_property(Dir1, + "num_children", + #any{typecode=tk_long, value=0})), + + ?line ?match({'EXCEPTION', _}, + 'CosFileTransfer_Directory': + define_properties(Dir1, + [#'CosPropertyService_Property' + {property_name = "num_children", + property_value = #any{typecode=tk_long, + value=0}}])), + ?line ?match({'EXCEPTION', _}, + 'CosFileTransfer_Directory': + define_properties(Dir1, + [#'CosPropertyService_Property' + {property_name = "wrong", + property_value = #any{typecode=tk_long, + value=0}}])), + ?line ?match({'EXCEPTION', _}, + 'CosFileTransfer_Directory': + define_properties(Dir1, + [#'CosPropertyService_Property' + {property_name = "num_children", + property_value = #any{typecode=tk_short, + value=0}}])), + ?line ?match(2, 'CosFileTransfer_Directory':get_number_of_properties(Dir1)), + + ?line ?match({ok, ["num_children", "is_directory"], {'IOP_IOR',[],[]}}, + 'CosFileTransfer_Directory':get_all_property_names(Dir1, 2)), + ?line ?match({ok, ["is_directory"], _}, + 'CosFileTransfer_Directory':get_all_property_names(Dir1, 1)), + + ?line ?match(#any{}, + 'CosFileTransfer_Directory':get_property_value(Dir1, "num_children")), + ?line ?match(#any{}, + 'CosFileTransfer_Directory':get_property_value(Dir1, "is_directory")), + ?line ?match({'EXCEPTION', _}, + 'CosFileTransfer_Directory':get_property_value(Dir1, "wrong")), + + ?line ?match({true, + [#'CosPropertyService_Property'{property_name = "num_children"}]}, + 'CosFileTransfer_Directory':get_properties(Dir1, ["num_children"])), + ?line ?match({false, + [#'CosPropertyService_Property'{property_name = "wrong"}]}, + 'CosFileTransfer_Directory':get_properties(Dir1, ["wrong"])), + + ?line ?match({ok, [_],_}, + 'CosFileTransfer_Directory':get_all_properties(Dir1, 1)), + ?line ?match({ok, [_,_], {'IOP_IOR',[],[]}}, + 'CosFileTransfer_Directory':get_all_properties(Dir1, 2)), + + ?line ?match({'EXCEPTION', _}, + 'CosFileTransfer_Directory':delete_property(Dir1, "num_children")), + ?line ?match({'EXCEPTION', _}, + 'CosFileTransfer_Directory':delete_property(Dir1, "wrong")), + + + ?line ?match({'EXCEPTION', _}, + 'CosFileTransfer_Directory':delete_properties(Dir1, ["num_children"])), + ?line ?match({'EXCEPTION', _}, + 'CosFileTransfer_Directory':delete_properties(Dir1, ["wrong"])), + ?line ?match(false, 'CosFileTransfer_Directory':delete_all_properties(Dir1)), + ?line ?match(true, + 'CosFileTransfer_Directory':is_property_defined(Dir1, "num_children")), + ?line ?match(false, + 'CosFileTransfer_Directory':is_property_defined(Dir1, "wrong")), + + %% The Top Dir should be empty and ... + ?line ?match({ok,[],_}, 'CosFileTransfer_Directory':list(Dir1, 1000)), + ?line ?match( #any{value=0}, + 'CosPropertyService_PropertySet':get_property_value(Dir1, "num_children")), + %% Create a sub-directory. + ?line Dir2 = 'CosFileTransfer_FileTransferSession':create_directory(FS, + TEST_DIR_LEVEL2), + ?line ?match( #any{value=1}, + 'CosPropertyService_PropertySet':get_property_value(Dir1, "num_children")), + + ?line ?match({ok, [_,_], {'IOP_IOR',[],[]}}, + 'CosFileTransfer_Directory':get_all_properties(Dir1, 2)), + ?line {_,_,Iterator1} = ?match({ok, [_], _}, + 'CosFileTransfer_Directory':get_all_properties(Dir1, 1)), + ?line ?match({false, [_]}, + 'CosPropertyService_PropertiesIterator':next_n(Iterator1,4)), + + ?line {_,_,Iterator0} = ?match({ok, [], _}, + 'CosFileTransfer_Directory':get_all_properties(Dir1, 0)), + + ?line ?match({false, [_, {'CosPropertyService_Property', + "num_children",{any,tk_long,1}}]}, + 'CosPropertyService_PropertiesIterator':next_n(Iterator0,4)), + + ?line ?match({true, + [#'CosPropertyService_Property'{property_name = "num_children"}]}, + 'CosFileTransfer_Directory':get_properties(Dir1, ["num_children"])), + + %% The Top Directory is not emtpy any more and ... + ?line {ok,[#'CosFileTransfer_FileWrapper'{the_file = DirRef}],_} = + ?matchnopr({ok,[{'CosFileTransfer_FileWrapper', _, ndirectory}],_}, + 'CosFileTransfer_Directory':list(Dir1, 1000)), + %% ... its name eq. to 'TEST_DIR_LEVEL2' + ?line ?match(TEST_DIR_LEVEL2, + 'CosFileTransfer_Directory':'_get_complete_file_name'(DirRef)), + + ?line #'CosFileTransfer_FileWrapper'{the_file = Dir3} = + ?matchnopr({'CosFileTransfer_FileWrapper', _, ndirectory}, + 'CosFileTransfer_FileTransferSession':get_file(FS, TEST_DIR_LEVEL1)), + + %% Must get the same result for the 'get_file' operation. + ?line {ok,[#'CosFileTransfer_FileWrapper'{the_file = DirRef2}],_} = + ?matchnopr({ok,[{'CosFileTransfer_FileWrapper', _, ndirectory}],_}, + 'CosFileTransfer_Directory':list(Dir3,1000)), + ?line ?match(TEST_DIR_LEVEL2, + 'CosFileTransfer_Directory':'_get_complete_file_name'(DirRef2)), + + %% Since the top directory isn't empty deleting it must fail. + ?line ?match({'EXCEPTION', _}, + 'CosFileTransfer_FileTransferSession':delete(FS, Dir1)), + + %% Delete the sub-directory and ... + ?line ?match(ok, 'CosFileTransfer_FileTransferSession':delete(FS, Dir2)), + %% ... see if the top directory realyy is empty. + ?line ?match({ok,[],_}, 'CosFileTransfer_Directory':list(Dir1, 1000)), + + ?line ?match(ok, 'CosFileTransfer_FileTransferSession':delete(FS, Dir1)), + %% Test if the top directory been removed as intended. + ?line ?match({'EXCEPTION', {'CosFileTransfer_FileNotFoundException', _, _}}, + 'CosFileTransfer_FileTransferSession':get_file(FS, TEST_DIR_LEVEL1)), + + ?line ?match(false, corba_object:non_existent(FS)), + ?line ?match(ok, 'CosFileTransfer_FileTransferSession':logout(FS)), + %% To make sure Orber can remove it from mnesia. + timer:sleep(1000), + ?line ?match(true, corba_object:non_existent(FS)), + ?line ?match(ok, remote_apply(Node, ?MODULE, uninstall_data, [Name])), + stop_orber_remote(Node, normal), + ok + end. + + +%%----------------------------------------------------------------- +%% Internal functions +%%----------------------------------------------------------------- +ftp_host(Config) -> + case ?config(ftp_remote_host, Config) of + undefined -> + {skipped, "The configuration parameter 'ftp_remote_host' not defined."}; + Host -> + Host + end. + +loop_files([]) -> + io:format("@@@ DONE @@@~n", []); +loop_files([#'CosFileTransfer_FileWrapper'{the_file = H}|T]) -> + FullName = 'CosFileTransfer_File':'_get_complete_file_name'(H), + Name = 'CosFileTransfer_File':'_get_name'(H), + io:format("FULL NAME: ~p SHORT NAME: ~p~n", [FullName, Name]), + loop_files(T). + + +create_file_on_source_node('FTP', _Config, Host, FileName, Path, Data) -> + io:format("<<<<<< CosFileTransfer Testing File >>>>>>~n",[]), + io:format("Host: ~p~nPath: ~p~nFile: ~p~n", [Host, Path, FileName]), + {ok, Pid} = ?match({ok, _}, inets:start(ftpc, [{host, Host}], stand_alone)), + ?match(ok, ftp:user(Pid, ?FTP_USER, ?FTP_PASS)), + ?match(ok, ftp:cd(Pid, Path)), + ?match(ok, ftp:send_bin(Pid, list_to_binary(Data), FileName)), + ?match(ok, inets:stop(ftpc, Pid)); +create_file_on_source_node({'NATIVE', _}, _Config, Host, FileName, Path, Data) -> + io:format("<<<<<< CosFileTransfer Testing File >>>>>>~n",[]), + io:format("Host: ~p~nPath: ~p~nFile: ~p~n", [Host, Path, FileName]), + ?match(ok, file:write_file(FileName, list_to_binary(Data))). + +create_name(Type) -> + {MSec, Sec, USec} = erlang:now(), + lists:concat([Type,'_',MSec, '_', Sec, '_', USec]). + + + + +%%------------------------------------------------------------ +%% function : create_node/4 +%% Arguments: Name - the name of the new node (atom()) +%% Port - which iiop_port (integer()) +%% Domain - which domain. +%% Type - if /4 used the types defines the extra arguments +%% to be used. +%% Returns : {ok, Node} | {error, _} +%% Effect : Starts a new slave-node with given (optinally) +%% extra arguments. If fails it retries 'Retries' times. +%%------------------------------------------------------------ +create_node(Name, Port, normal) -> + Args = basic_args(Name), + create_node(Name, Port, 10, normal, Args, []); +create_node(Name, {Port, _Depth}, ssl) -> + Dir = filename:join([code:lib_dir(ssl), "examples", "certs", "etc"]), + Args = basic_args(Name), + {ok, Node} = create_node(list_to_atom(Name), Port, 10, ssl, Args, []), + %% Client + rpc:call(Node, application, set_env, [cosFileTransfer, ssl_client_certfile, + filename:join([Dir, "client", "cert.pem"])]), + rpc:call(Node, application, set_env, [cosFileTransfer, ssl_client_cacertfile, + filename:join([Dir, "client", "cacerts.pem"])]), + rpc:call(Node, application, set_env, [cosFileTransfer, ssl_client_keyfile, + filename:join([Dir, "client", "key.pem"])]), + rpc:call(Node, application, set_env, [cosFileTransfer, ssl_client_verify, 1]), + rpc:call(Node, application, set_env, [cosFileTransfer, ssl_client_depth, 0]), + + %% Server + rpc:call(Node, application, set_env, [cosFileTransfer, ssl_server_certfile, + filename:join([Dir, "server", "cert.pem"])]), + rpc:call(Node, application, set_env, [cosFileTransfer, ssl_server_cacertfile, + filename:join([Dir, "server", "cacerts.pem"])]), + rpc:call(Node, application, set_env, [cosFileTransfer, ssl_server_keyfile, + filename:join([Dir, "server", "key.pem"])]), + rpc:call(Node, application, set_env, [cosFileTransfer, ssl_server_verify, 1]), + rpc:call(Node, application, set_env, [cosFileTransfer, ssl_server_depth, 0]), + {ok, Node}. + +%create_node(Name, {Port, Depth}, ssl) -> +% TestLibs = filename:join(filename:dirname(code:which(?MODULE)), "ssl_data"), +% Args = basic_args(Name), +% SArgs = basic_ssl_args(TestLibs, Args), +% LArgs = level_based_ssl(Depth, TestLibs, SArgs), +% create_node(list_to_atom(Name), Port, 10, ssl, LArgs, [{sslpath, TestLibs}]). + +create_node(Name, Port, Retries, Type, Args, Options) -> + [_, Host] = ?match([_,_],string:tokens(atom_to_list(node()), [$@])), + case starter(Host, Name, Args) of + {ok, NewNode} -> + ?line ?match(pong, net_adm:ping(NewNode)), + {ok, Cwd} = file:get_cwd(), + Path = code:get_path(), + ?line ?match(ok, rpc:call(NewNode, file, set_cwd, [Cwd])), + true = rpc:call(NewNode, code, set_path, [Path]), + ?match(ok, start_orber_remote(NewNode, Type, Options, Port)), + spawn_link(NewNode, ?MODULE, slave_sup, []), + rpc:multicall([node() | nodes()], global, sync, []), + {ok, NewNode}; + {error, Reason} when Retries == 0-> + {error, Reason}; + {error, Reason} -> + io:format("Could not start slavenode ~p ~p retrying~n", + [{Host, Name, Args}, Reason]), + timer:sleep(500), + create_node(Name, Port, Retries - 1, Type, Args, Options) + end. + +starter(Host, Name, Args) -> + case os:type() of + vxworks -> + test_server:start_node(Name, slave, [{args,Args}]); + _ -> + slave:start(Host, Name, Args) + end. + +slave_sup() -> + process_flag(trap_exit, true), + receive + {'EXIT', _, _} -> + case os:type() of + vxworks -> + erlang:halt(); + _ -> + ignore + end + end. + + +%%------------------------------------------------------------ +%% function : destroy_node +%% Arguments: Node - which node to destroy. +%% Type - normal | ssl +%% Returns : +%% Effect : +%%------------------------------------------------------------ +-ifdef(false). +destroy_node(Node, Type) -> + stopper(Node, Type). + +stopper(Node, Type) -> + catch stop_orber_remote(Node, Type), + case os:type() of + vxworks -> + test_server:stop_node(Node); + _ -> + slave:stop(Node) + end. +-endif. + +%%------------------------------------------------------------ +%% function : remote_apply +%% Arguments: N - Node, M - Module, +%% F - Function, A - Arguments (list) +%% Returns : +%% Effect : +%%------------------------------------------------------------ +remote_apply(N, M,F,A) -> + case rpc:call(N, M, F, A) of + {badrpc, Reason} -> + exit(Reason); + Other -> + Other + end. + +%%------------------------------------------------------------ +%% function : stop_orber_remote +%% Arguments: Node - which node to stop orber on. +%% Type - normal | ssl | light | ....... +%% Returns : ok +%% Effect : Stops orber on given node and, if specified, +%% other applications or programs. +%%------------------------------------------------------------ +stop_orber_remote(Node, ssl) -> + rpc:call(Node, ssl, stop, []), + rpc:call(Node, crypto, stop, []), + orb_rpc_blast(Node, ssl); +stop_orber_remote(Node, Type) -> + orb_rpc_blast(Node, Type). + +orb_rpc_blast(Node, _) -> + rpc:call(Node, cosFileTransferApp, stop, []), + rpc:call(Node, cosProperty, stop, []), + rpc:call(Node, cosFileTransferApp, uninstall, []), + rpc:call(Node, cosProperty, uninstall, []), + rpc:call(Node, orber, jump_stop, []). + +%%------------------------------------------------------------ +%% function : start_orber_remote +%% Arguments: Node - which node to start orber on. +%% Type - normal | ssl | light | ....... +%% Returns : ok +%% Effect : Starts orber on given node and, if specified, +%% other applications or programs. +%%------------------------------------------------------------ +start_orber_remote(Node, ssl, _Options, Port) -> + rpc:call(Node, ssl, start, []), + rpc:call(Node, crypto, start, []), + rpc:call(Node, ssl, seed, ["testing"]), + orb_rpc_setup(Node, ssl, Port); +start_orber_remote(Node, Type, _, Port) -> + orb_rpc_setup(Node, Type, Port). + +orb_rpc_setup(Node, _, Port) -> + rpc:call(Node, orber, jump_start, [Port]), + rpc:call(Node, cosProperty, install, []), + rpc:call(Node, cosProperty, start, []), + rpc:call(Node, cosFileTransferApp, install, []). + +%%--------------- MISC FUNCTIONS ----------------------------- +basic_args(_Name) -> + TestLibs = filename:dirname(code:which(?MODULE)), + " -orber orber_debug_level 10" ++ + " -pa " ++ + TestLibs ++ + " -pa " ++ + filename:join(TestLibs, "all_SUITE_data") ++ + " -pa " ++ + filename:dirname(code:which(cosFileTransferApp)). + +-ifdef(false). +basic_ssl_args(TestLibs, Args) -> +% Args ++ +% " -cosFileTransfer ssl_client_certfile \\\"" ++ +% filename:join(TestLibs, "ssl_client_cert.pem") ++ +% "\\\" -cosFileTransfer ssl_server_certfile \\\""++ +% filename:join(TestLibs, "ssl_server_cert.pem")++"\\\"". + + io:format("<<<<<< SSL LIBS ~p >>>>>>~n",[TestLibs]), + NewArgs = Args ++ + " -cosFileTransfer ssl_client_certfile \\\"" ++ + filename:join(TestLibs, "ssl_client_cert.pem") ++ + "\\\" -cosFileTransfer ssl_server_certfile \\\""++ + filename:join(TestLibs, "ssl_server_cert.pem")++"\\\"", + io:format("<<<<<< SSL LIBS ARGS ~p >>>>>>~n",[NewArgs]), + NewArgs. + +level_based_ssl(1, _TestLibs, Args) -> + Args; +level_based_ssl(2, _TestLibs, Args) -> + Args.% ++ +% " -cosFileTransfer ssl_server_depth 2 " ++ +% " -cosFileTransfer ssl_client_depth 2 " ++ +% " -cosFileTransfer ssl_server_verify " ++ +% " -cosFileTransfer ssl_client_verify " ++ +% " -cosFileTransfer ssl_server_cacertfile " ++ +% " -cosFileTransfer ssl_client_cacertfile " ++ + +-endif. + +install_data(Protocol, {WhichType, Host, Name}) -> + io:format("<<<<<< Starting ~p/~p VFS at ~p/~p>>>>>>~n", + [Protocol, WhichType, Host, Name]), + %% Create a Virtual File System. + ?line VFS = ?match({_,_,_,_,_,_}, + cosFileTransferApp:create_VFS(WhichType, [], Host, ?FTP_PORT, + [{protocol, Protocol}])), + NS = corba:resolve_initial_references("NameService"), + NC1 = lname_component:set_id(lname_component:create(), Name), + N = lname:insert_component(lname:create(), 1, NC1), + 'CosNaming_NamingContext':rebind(NS, N, VFS). + +uninstall_data(Name) -> + ?line VFS = ?match({_,_,_,_,_,_}, + corba:string_to_object("corbaname:rir:/NameService#"++Name)), + ?line ?match(ok, corba:dispose(VFS)), + ok. + + + +%%------------------- EOF MODULE----------------------------------- diff --git a/lib/cosNotification/doc/src/notes.xml b/lib/cosNotification/doc/src/notes.xml index de5a3e5f4c..04c0c2accd 100644 --- a/lib/cosNotification/doc/src/notes.xml +++ b/lib/cosNotification/doc/src/notes.xml @@ -31,6 +31,46 @@ <file>notes.xml</file> </header> + <section><title>cosNotification 1.1.15</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Switched from using the deprecated regexp to re instead.</p> + <p> + Own Id: OTP-8846</p> + </item> + </list> + </section> + +</section> + +<section> + <title>cosNotification 1.1.14</title> + <section> + <title>Improvements and New Features</title> + <list type="bulleted"> + <item> + <p> + Test suites published.</p> + <p> + Own Id: OTP-8543 Aux Id:</p> + </item> + </list> + </section> + + <section> + <title>Fixed Bugs and Malfunctions</title> + <list type="bulleted"> + <item> + <p>Added missing trailing bracket to define in hrl-file.</p> + <p>Own Id: OTP-8489 Aux Id:</p> + </item> + </list> + </section> + </section> + <section> <title>cosNotification 1.1.14</title> <section> @@ -64,15 +104,15 @@ <list type="bulleted"> <item> <p>Removed superfluous VT in the documentation.</p> - <p>Own id: OTP-8353 Aux Id:</p> + <p>Own Id: OTP-8353 Aux Id:</p> </item> <item> <p>Removed superfluous backslash in the documentation.</p> - <p>Own id: OTP-8354 Aux Id:</p> + <p>Own Id: OTP-8354 Aux Id:</p> </item> <item> <p>The documentation EIX file was not generated.</p> - <p>Own id: OTP-8355 Aux Id:</p> + <p>Own Id: OTP-8355 Aux Id:</p> </item> </list> </section> @@ -104,7 +144,7 @@ <item> <p>Obsolete guards, e.g. record vs is_record, has been changed to avoid compiler warnings.</p> - <p>Own id: OTP-7987</p> + <p>Own Id: OTP-7987</p> </item> </list> </section> @@ -118,7 +158,7 @@ <list type="bulleted"> <item> <p>Updated file headers.</p> - <p>Own id: OTP-7837 Aux Id:</p> + <p>Own Id: OTP-7837 Aux Id:</p> </item> </list> </section> @@ -132,7 +172,7 @@ <list type="bulleted"> <item> <p>Documentation source included in open source releases.</p> - <p>Own id: OTP-7595 Aux Id:</p> + <p>Own Id: OTP-7595 Aux Id:</p> </item> </list> </section> @@ -147,7 +187,7 @@ <item> <p>The CosNotification proxy objects ignored the gcLimit option, instead the gcTime value was used.</p> - <p>Own id: OTP-7553 Aux Id:</p> + <p>Own Id: OTP-7553 Aux Id:</p> </item> </list> </section> @@ -161,7 +201,7 @@ <list type="bulleted"> <item> <p>Updated file headers.</p> - <p>Own id: OTP-7011</p> + <p>Own Id: OTP-7011</p> </item> </list> </section> @@ -175,7 +215,7 @@ <list type="bulleted"> <item> <p>The documentation source has been converted from SGML to XML.</p> - <p>Own id: OTP-6754</p> + <p>Own Id: OTP-6754</p> </item> </list> </section> @@ -189,7 +229,7 @@ <list type="bulleted"> <item> <p>Minor Makefile changes.</p> - <p>Own id: OTP-6701</p> + <p>Own Id: OTP-6701</p> </item> </list> </section> @@ -203,7 +243,7 @@ <list type="bulleted"> <item> <p>Removed some unused code.</p> - <p>Own id: OTP-6527</p> + <p>Own Id: OTP-6527</p> </item> </list> </section> @@ -219,7 +259,7 @@ <p>A user can now define the QoS EventReliability to be Persistent. Note, this is only a lightweight version and events will be lost if a proxy is terminated.</p> - <p>Own id: OTP-5923</p> + <p>Own Id: OTP-5923</p> </item> </list> </section> @@ -235,7 +275,7 @@ <p>Possible to configure cosNotification not to type check, by invoking corba_object:is_a/2, supplied IOR:s. When a type check fails, the feedback has been improved.</p> - <p>Own id: OTP-5823 Aux Id: seq10143</p> + <p>Own Id: OTP-5823 Aux Id: seq10143</p> </item> </list> </section> @@ -249,7 +289,7 @@ <list type="bulleted"> <item> <p>The app-file contained duplicated modules.</p> - <p>Own id: OTP-4976</p> + <p>Own Id: OTP-4976</p> </item> </list> </section> @@ -268,7 +308,7 @@ Interface Repository. It is necessary to re-compile all IDL-files and use COS-applications, including Orber, compiled with IC-4.2.</p> - <p>Own id: OTP-4576</p> + <p>Own Id: OTP-4576</p> </item> </list> </section> diff --git a/lib/cosNotification/src/cosNotification_Filter.erl b/lib/cosNotification/src/cosNotification_Filter.erl index dd3b5beb93..7201f7d6e2 100644 --- a/lib/cosNotification/src/cosNotification_Filter.erl +++ b/lib/cosNotification/src/cosNotification_Filter.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% Copyright Ericsson AB 1999-2010. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -877,9 +877,9 @@ check_wildcard(Types, Which, WC, Domain, Type) -> end, check_types(Types, Which, NewWC). -%% Change '*' to '.*', see regexp:parse/2 documentation. +%% Change '*' to '.*', see re:compile/1 documentation. convert_wildcard([], Acc) -> - case regexp:parse(lists:reverse(Acc)) of + case re:compile(lists:reverse(Acc)) of {ok, Expr} -> Expr; _ -> @@ -900,37 +900,37 @@ match_types(_, _, []) -> false; match_types(Domain, Type, [{domain, WCDomain, Type}|T]) -> L=length(Domain), - case catch regexp:matches(Domain, WCDomain) of - {match, []} -> + case catch re:run(Domain, WCDomain) of + nomatch -> match_types(Domain, Type, T); - {match, [{1, L}]} -> + {match, [{0, L}]} -> true; _-> match_types(Domain, Type, T) end; match_types(Domain, Type, [{type, Domain, WCType}|T]) -> L=length(Type), - case catch regexp:matches(Type, WCType) of - {match, []} -> + case catch re:run(Type, WCType) of + nomatch -> match_types(Domain, Type, T); - {match, [{1, L}]} -> + {match, [{0, L}]} -> true; _-> match_types(Domain, Type, T) end; match_types(Domain, Type, [{both, WCDomain, WCType}|T]) -> L1=length(Domain), - case catch regexp:matches(Domain, WCDomain) of - {match, []} -> + case catch re:run(Domain, WCDomain) of + nomatch -> match_types(Domain, Type, T); - {match, [{1, L1}]} -> + {match, [{0, L1}]} -> L2=length(Type), - case catch regexp:matches(Type, WCType) of - {match, []} -> + case catch re:run(Type, WCType) of + nomatch -> match_types(Domain, Type, T); - {match, [{1, L2}]} -> + {match, [{0, L2}]} -> true; - _-> + _ -> match_types(Domain, Type, T) end; _-> diff --git a/lib/cosNotification/vsn.mk b/lib/cosNotification/vsn.mk index c03f0ef161..cfd5948dfc 100644 --- a/lib/cosNotification/vsn.mk +++ b/lib/cosNotification/vsn.mk @@ -1 +1 @@ -COSNOTIFICATION_VSN = 1.1.14 +COSNOTIFICATION_VSN = 1.1.15 diff --git a/lib/dialyzer/doc/src/dialyzer.xml b/lib/dialyzer/doc/src/dialyzer.xml index 1ec2ce830a..29308885fd 100644 --- a/lib/dialyzer/doc/src/dialyzer.xml +++ b/lib/dialyzer/doc/src/dialyzer.xml @@ -101,9 +101,9 @@ <tag><c><![CDATA[--output_plt file]]></c></tag> <item>Store the PLT at the specified location after building it.</item> <tag><c><![CDATA[--plt plt]]></c></tag> - <item>Use the specified plt as the initial persistent lookup table.</item> + <item>Use the specified PLT as the initial persistent lookup table.</item> <tag><c><![CDATA[-Wwarn]]></c></tag> - <item>a family of option which selectively turn on/off warnings. + <item>a family of options which selectively turn on/off warnings. (for help on the names of warnings use <c><![CDATA[dialyzer -Whelp]]></c>)</item> <tag><c><![CDATA[--shell]]></c></tag> <item>do not disable the Erlang shell while running the GUI</item> @@ -158,9 +158,16 @@ <tag><c><![CDATA[-Wno_match]]></c></tag> <item>Suppress warnings for patterns that are unused or cannot match.</item> + <tag><c><![CDATA[-Wno_opaque]]></c></tag> + <item>Suppress warnings for violations of opaqueness of data types.</item> <tag><c><![CDATA[-Werror_handling]]></c>***</tag> <item>Include warnings for functions that only return by means of an exception.</item> + <tag><c><![CDATA[-Wrace_conditions]]></c>***</tag> + <item>Include warnings for possible race conditions.</item> + <tag><c><![CDATA[-Wbehaviours]]></c>***</tag> + <item>Include warnings about behaviour callbacks which drift from the + published recommended interfaces.</item> <tag><c><![CDATA[-Wunmatched_returns]]></c>***</tag> <item>Include warnings for function calls which ignore a structured return value or do not match against one of many possible return value(s).</item> @@ -215,8 +222,11 @@ WarnOpts : no_return | no_improper_lists | no_fun_app | no_match + | no_opaque | no_fail_call | error_handling + | race_conditions + | behaviours | unmatched_returns | overspecs | underspecs diff --git a/lib/dialyzer/src/dialyzer_races.erl b/lib/dialyzer/src/dialyzer_races.erl index ec8d613b96..ee9d5e88a3 100644 --- a/lib/dialyzer/src/dialyzer_races.erl +++ b/lib/dialyzer/src/dialyzer_races.erl @@ -118,7 +118,7 @@ var_map :: dict()}). -type case_tags() :: 'beg_case' | #beg_clause{} | #end_clause{} | #end_case{}. --type code() :: [#dep_call{} | #warn_call{} | #fun_call{} | +-type code() :: [#dep_call{} | #fun_call{} | #warn_call{} | #curr_fun{} | #let_tag{} | case_tags() | race_tag()]. -type table_var() :: label() | ?no_label. @@ -479,23 +479,11 @@ fixup_race_forward(CurrFun, CurrFunLabel, Calls, Code, RaceList, _Other -> {RaceList, [], NestingLevel, false} end; - #dep_call{call_name = ets_lookup, args = DepCallArgs} -> + #dep_call{call_name = ets_lookup} -> case RaceWarnTag of ?WARN_ETS_LOOKUP_INSERT -> - [Tab, Names, _, _] = DepCallArgs, - case compare_var_list(Tab, - dialyzer_callgraph:get_public_tables(Callgraph), - RaceVarMap) - orelse - length(Names -- - dialyzer_callgraph:get_named_tables(Callgraph)) < - length(Names) of - true -> - {[Head#dep_call{var_map = RaceVarMap}|RaceList], - [], NestingLevel, false}; - false -> - {RaceList, [], NestingLevel, false} - end; + {[Head#dep_call{var_map = RaceVarMap}|RaceList], + [], NestingLevel, false}; _Other -> {RaceList, [], NestingLevel, false} end; @@ -517,23 +505,11 @@ fixup_race_forward(CurrFun, CurrFunLabel, Calls, Code, RaceList, _Other -> {RaceList, [], NestingLevel, false} end; - #warn_call{call_name = ets_insert, args = WarnCallArgs} -> + #warn_call{call_name = ets_insert} -> case RaceWarnTag of ?WARN_ETS_LOOKUP_INSERT -> - [Tab, Names, _, _] = WarnCallArgs, - case compare_var_list(Tab, - dialyzer_callgraph:get_public_tables(Callgraph), - RaceVarMap) - orelse - length(Names -- - dialyzer_callgraph:get_named_tables(Callgraph)) < - length(Names) of - true -> - {[Head#warn_call{var_map = RaceVarMap}|RaceList], - [], NestingLevel, false}; - false -> - {RaceList, [], NestingLevel, false} - end; + {[Head#warn_call{var_map = RaceVarMap}|RaceList], + [], NestingLevel, false}; _Other -> {RaceList, [], NestingLevel, false} end; diff --git a/lib/erl_interface/doc/src/notes.xml b/lib/erl_interface/doc/src/notes.xml index 8e379463ad..ff89802599 100644 --- a/lib/erl_interface/doc/src/notes.xml +++ b/lib/erl_interface/doc/src/notes.xml @@ -30,6 +30,20 @@ </header> <p>This document describes the changes made to the Erl_interface application.</p> +<section><title>Erl_Interface 3.7.1.1</title> + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + The <c>erl_interface</c> tracelevel for erlang messages was incorrect. This has now been fixed. + </p> + <p> + Own Id: OTP-8874</p> + </item> + </list> + </section> + +</section> <section><title>Erl_Interface 3.7.1</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/erl_interface/src/connect/eirecv.c b/lib/erl_interface/src/connect/eirecv.c index 51fc32d65c..7d72ddeeae 100644 --- a/lib/erl_interface/src/connect/eirecv.c +++ b/lib/erl_interface/src/connect/eirecv.c @@ -107,7 +107,7 @@ ei_recv_internal (int fd, switch (msg->msgtype) { case ERL_SEND: /* { SEND, Cookie, ToPid } */ - if (ei_tracelevel > 0) show_this_msg = 1; + if (ei_tracelevel >= 4) show_this_msg = 1; if (ei_decode_atom(header,&index,msg->cookie) || ei_decode_pid(header,&index,&msg->to)) { @@ -118,7 +118,7 @@ ei_recv_internal (int fd, break; case ERL_REG_SEND: /* { REG_SEND, From, Cookie, ToName } */ - if (ei_tracelevel > 0) show_this_msg = 1; + if (ei_tracelevel >= 4) show_this_msg = 1; if (ei_decode_pid(header,&index,&msg->from) || ei_decode_atom(header,&index,msg->cookie) || ei_decode_atom(header,&index,msg->toname)) @@ -133,7 +133,7 @@ ei_recv_internal (int fd, case ERL_LINK: /* { LINK, From, To } */ case ERL_UNLINK: /* { UNLINK, From, To } */ case ERL_GROUP_LEADER: /* { GROUP_LEADER, From, To } */ - if (ei_tracelevel > 1) show_this_msg = 1; + if (ei_tracelevel >= 4) show_this_msg = 1; if (ei_decode_pid(header,&index,&msg->from) || ei_decode_pid(header,&index,&msg->to)) { @@ -145,7 +145,7 @@ ei_recv_internal (int fd, case ERL_EXIT: /* { EXIT, From, To, Reason } */ case ERL_EXIT2: /* { EXIT2, From, To, Reason } */ - if (ei_tracelevel > 1) show_this_msg = 1; + if (ei_tracelevel >= 4) show_this_msg = 1; if (ei_decode_pid(header,&index,&msg->from) || ei_decode_pid(header,&index,&msg->to)) { @@ -156,7 +156,7 @@ ei_recv_internal (int fd, break; case ERL_SEND_TT: /* { SEND_TT, Cookie, ToPid, TraceToken } */ - if (ei_tracelevel > 0) show_this_msg = 1; + if (ei_tracelevel >= 4) show_this_msg = 1; if (ei_decode_atom(header,&index,msg->cookie) || ei_decode_pid(header,&index,&msg->to) || ei_decode_trace(header,&index,&msg->token)) @@ -169,7 +169,7 @@ ei_recv_internal (int fd, break; case ERL_REG_SEND_TT: /* { REG_SEND_TT, From, Cookie, ToName, TraceToken } */ - if (ei_tracelevel > 0) show_this_msg = 1; + if (ei_tracelevel >= 4) show_this_msg = 1; if (ei_decode_pid(header,&index,&msg->from) || ei_decode_atom(header,&index,msg->cookie) || ei_decode_atom(header,&index,msg->toname) @@ -184,7 +184,7 @@ ei_recv_internal (int fd, case ERL_EXIT_TT: /* { EXIT_TT, From, To, TraceToken, Reason } */ case ERL_EXIT2_TT: /* { EXIT2_TT, From, To, TraceToken, Reason } */ - if (ei_tracelevel > 1) show_this_msg = 1; + if (ei_tracelevel >= 4) show_this_msg = 1; if (ei_decode_pid(header,&index,&msg->from) || ei_decode_pid(header,&index,&msg->to) || ei_decode_trace(header,&index,&msg->token)) @@ -197,7 +197,7 @@ ei_recv_internal (int fd, break; case ERL_NODE_LINK: /* { NODE_LINK } */ - if (ei_tracelevel > 1) show_this_msg = 1; + if (ei_tracelevel >= 4) show_this_msg = 1; break; default: diff --git a/lib/erl_interface/src/connect/send.c b/lib/erl_interface/src/connect/send.c index cd832db4ea..57e32903cf 100644 --- a/lib/erl_interface/src/connect/send.c +++ b/lib/erl_interface/src/connect/send.c @@ -87,8 +87,7 @@ int ei_send_encoded_tmo(int fd, const erlang_pid *to, put8(s, ERL_PASS_THROUGH); /* 1 */ /*** sum: 1070 */ - /* FIXME incorrect level */ - if (ei_tracelevel > 0) + if (ei_tracelevel >= 4) ei_show_sendmsg(stderr,header,msg); #ifdef HAVE_WRITEV diff --git a/lib/erl_interface/src/connect/send_exit.c b/lib/erl_interface/src/connect/send_exit.c index 098797c96d..d4e6605a2c 100644 --- a/lib/erl_interface/src/connect/send_exit.c +++ b/lib/erl_interface/src/connect/send_exit.c @@ -88,8 +88,7 @@ int ei_send_exit_tmo(int fd, const erlang_pid *from, const erlang_pid *to, put32be(s, index - 4); /* 4 */ put8(s, ERL_PASS_THROUGH); /* 1 */ /*** sum: len + 1080 */ - /* FIXME incorrect level */ - if (ei_tracelevel > 1) + if (ei_tracelevel >= 4) ei_show_sendmsg(stderr,msgbuf,NULL); ei_write_fill_t(fd,msgbuf,index,ms); diff --git a/lib/erl_interface/src/connect/send_reg.c b/lib/erl_interface/src/connect/send_reg.c index 8f0e40309c..779b1b8359 100644 --- a/lib/erl_interface/src/connect/send_reg.c +++ b/lib/erl_interface/src/connect/send_reg.c @@ -82,8 +82,7 @@ int ei_send_reg_encoded_tmo(int fd, const erlang_pid *from, put32be(s, index + msglen - 4); /* 4 */ put8(s, ERL_PASS_THROUGH); /* 1 */ /*** sum: 1336 */ - /* FIXME incorrect level.... */ - if (ei_tracelevel > 0) + if (ei_tracelevel >= 4) ei_show_sendmsg(stderr,header,msg); #ifdef HAVE_WRITEV diff --git a/lib/erl_interface/src/misc/ei_format.c b/lib/erl_interface/src/misc/ei_format.c index 08235d0ebe..b35421d4b2 100644 --- a/lib/erl_interface/src/misc/ei_format.c +++ b/lib/erl_interface/src/misc/ei_format.c @@ -106,6 +106,8 @@ static int eiformat(const char** fmt, union arg** args, ei_x_buff* x) default: if (isdigit((int)*p)) res = pdigit(&p, x); + else if ((*p == '-' || *p == '+') && isdigit((int)*(p+1))) + res = pdigit(&p, x); else if (islower((int)*p)) res = patom(&p, x); else @@ -149,6 +151,8 @@ static int pdigit(const char** fmt, ei_x_buff* x) double d; long l; + if (**fmt == '-' || **fmt == '+') + (*fmt)++; for (;;) { c = *(*fmt)++; if (isdigit((int)c)) diff --git a/lib/erl_interface/src/prog/erl_call.c b/lib/erl_interface/src/prog/erl_call.c index 448de9aa23..33ff6da7c9 100644 --- a/lib/erl_interface/src/prog/erl_call.c +++ b/lib/erl_interface/src/prog/erl_call.c @@ -118,7 +118,6 @@ static void usage_arg(const char *progname, const char *switchname); static void usage_error(const char *progname, const char *switchname); static void usage(const char *progname); static int get_module(char **mbuf, char **mname); -static struct hostent* get_hostent(char *host); static int do_connect(ei_cnode *ec, char *nodename, struct call_flags *flags); static int read_stdin(char **buf); static void split_apply_string(char *str, char **mod, @@ -367,8 +366,8 @@ int erl_call(int argc, char **argv) * Expand name to a real name (may be ip-address) */ /* FIXME better error string */ - if ((hp = get_hostent(host)) == 0) { - fprintf(stderr,"erl_call: can't get_hostent(%s)\n", host); + if ((hp = ei_gethostbyname(host)) == 0) { + fprintf(stderr,"erl_call: can't ei_gethostbyname(%s)\n", host); exit(1); } /* If shortnames, cut off the name at first '.' */ @@ -604,32 +603,6 @@ int erl_call(int argc, char **argv) * ***************************************************************************/ -/* - * Get host entry (by address or name) - */ -/* FIXME: will fail on names like '2fun4you'. */ -static struct hostent* get_hostent(char *host) -{ - if (isdigit((int)*host)) { - struct in_addr ip_addr; - int b1, b2, b3, b4; - long addr; - - /* FIXME: Use inet_aton() (or inet_pton() and get v6 for free). */ - if (sscanf(host, "%d.%d.%d.%d", &b1, &b2, &b3, &b4) != 4) { - return NULL; - } - addr = inet_addr(host); - ip_addr.s_addr = htonl(addr); - - return ei_gethostbyaddr((char *)&ip_addr,sizeof(struct in_addr), AF_INET); - } - - return ei_gethostbyname(host); -} /* get_hostent */ - - - /* * This function does only return on success. diff --git a/lib/erl_interface/src/registry/reg_dump.c b/lib/erl_interface/src/registry/reg_dump.c index 50a6949177..dfec96b43c 100644 --- a/lib/erl_interface/src/registry/reg_dump.c +++ b/lib/erl_interface/src/registry/reg_dump.c @@ -157,7 +157,7 @@ static int mn_send_delete(int fd, erlang_pid *mnesia, const char *key) int len = strlen(key) + 32; /* 32 is a slight overestimate */ if (len > EISMALLBUF) - if (!(dbuf = malloc(index))) + if (!(dbuf = malloc(len))) return -1; msgbuf = (dbuf ? dbuf : sbuf); @@ -187,7 +187,7 @@ static int mn_send_write(int fd, erlang_pid *mnesia, const char *key, ei_reg_obj int len = 32 + keylen + obj->size; if (len > EISMALLBUF) - if (!(dbuf = malloc(index))) + if (!(dbuf = malloc(len))) return -1; msgbuf = (dbuf ? dbuf : sbuf); diff --git a/lib/erl_interface/src/registry/reg_restore.c b/lib/erl_interface/src/registry/reg_restore.c index 27918d2364..aeb33c784a 100644 --- a/lib/erl_interface/src/registry/reg_restore.c +++ b/lib/erl_interface/src/registry/reg_restore.c @@ -266,7 +266,7 @@ int ei_reg_restore(int fd, ei_reg *reg, const char *mntab) /* make sure receive buffer can handle largest expected message */ len = maxkey + maxobj + 512; if (len > EISMALLBUF) - if (!(dbuf = malloc(index))) { + if (!(dbuf = malloc(len))) { ei_send_exit(fd,&self,&mnesia,"cannot allocate space for incoming data"); return -1; } diff --git a/lib/erl_interface/test/ei_format_SUITE.erl b/lib/erl_interface/test/ei_format_SUITE.erl index 7871f07ae9..cbe9fa52d7 100644 --- a/lib/erl_interface/test/ei_format_SUITE.erl +++ b/lib/erl_interface/test/ei_format_SUITE.erl @@ -155,7 +155,7 @@ format_wo_ver(suite) -> []; format_wo_ver(Config) when is_list(Config) -> ?line P = runner:start(?format_wo_ver), - ?line {term, [{a, "b"}, {c, 10}]} = get_term(P), + ?line {term, [-1, 2, {a, "b"}, {c, 10}]} = get_term(P), ?line runner:recv_eot(P), ok. diff --git a/lib/erl_interface/test/ei_format_SUITE_data/ei_format_test.c b/lib/erl_interface/test/ei_format_SUITE_data/ei_format_test.c index a969ded3dc..ecdce402f5 100644 --- a/lib/erl_interface/test/ei_format_SUITE_data/ei_format_test.c +++ b/lib/erl_interface/test/ei_format_SUITE_data/ei_format_test.c @@ -176,7 +176,7 @@ TESTCASE(format_wo_ver) { ei_x_buff x; ei_x_new (&x); - ei_x_format(&x, "[{~a,~s},{~a,~i}]", "a", "b", "c", 10); + ei_x_format(&x, "[-1, +2, {~a,~s},{~a,~i}]", "a", "b", "c", 10); send_bin_term(&x); free(x.buff); diff --git a/lib/erl_interface/vsn.mk b/lib/erl_interface/vsn.mk index c642cc5002..6c664959a3 100644 --- a/lib/erl_interface/vsn.mk +++ b/lib/erl_interface/vsn.mk @@ -1 +1 @@ -EI_VSN = 3.7.1 +EI_VSN = 3.7.1.1 diff --git a/lib/hipe/cerl/erl_types.erl b/lib/hipe/cerl/erl_types.erl index 9bc56c99ff..4dd124a457 100644 --- a/lib/hipe/cerl/erl_types.erl +++ b/lib/hipe/cerl/erl_types.erl @@ -29,7 +29,7 @@ %% In late 2008, Manouk Manoukian and Kostis Sagonas added support for %% opaque types to the structure-based representation of types. %% During February and March 2009, Kostis Sagonas significantly -%% cleaned up the type representation added spec declarations. +%% cleaned up the type representation and added spec declarations. %% %% ====================================================================== @@ -714,12 +714,13 @@ t_solve_remote_type(#remote{mod = RemMod, name = Name, args = Args} = RemType, case lookup_type(Name, RemDict) of {type, {_Mod, Type, ArgNames}} when ArgsLen =:= length(ArgNames) -> {NewType, NewCycle, NewRR} = - case unfold(RemType, C) of + case can_unfold_more(RemType, C) of true -> List = lists:zip(ArgNames, Args), TmpVarDict = dict:from_list(List), {t_from_form(Type, RemDict, TmpVarDict), [RemType|C], []}; - false -> {t_any(), C, [RemType]} + false -> + {t_any(), C, [RemType]} end, {RT, RR} = t_solve_remote(NewType, ET, R, NewCycle), RetRR = NewRR ++ RR, @@ -733,9 +734,11 @@ t_solve_remote_type(#remote{mod = RemMod, name = Name, args = Args} = RemType, List = lists:zip(ArgNames, Args), TmpVarDict = dict:from_list(List), {Rep, NewCycle, NewRR} = - case unfold(RemType, C) of - true -> {t_from_form(Type, RemDict, TmpVarDict), [RemType|C], []}; - false -> {t_any(), C, [RemType]} + case can_unfold_more(RemType, C) of + true -> + {t_from_form(Type, RemDict, TmpVarDict), [RemType|C], []}; + false -> + {t_any(), C, [RemType]} end, {NewRep, RR} = t_solve_remote(Rep, ET, R, NewCycle), RetRR = NewRR ++ RR, @@ -2388,9 +2391,9 @@ inf_tuple_sets([{Arity, Tuples1}|Left1], [{Arity, Tuples2}|Left2], Acc, Mode) -> [] -> inf_tuple_sets(Left1, Left2, Acc, Mode); NewTuples -> inf_tuple_sets(Left1, Left2, [{Arity, NewTuples}|Acc], Mode) end; -inf_tuple_sets(L1 = [{Arity1, _}|Left1], L2 = [{Arity2, _}|Left2], Acc, Mode) -> - if Arity1 < Arity2 -> inf_tuple_sets(Left1, L2, Acc, Mode); - Arity1 > Arity2 -> inf_tuple_sets(L1, Left2, Acc, Mode) +inf_tuple_sets([{Arity1, _}|Ts1] = L1, [{Arity2, _}|Ts2] = L2, Acc, Mode) -> + if Arity1 < Arity2 -> inf_tuple_sets(Ts1, L2, Acc, Mode); + Arity1 > Arity2 -> inf_tuple_sets(L1, Ts2, Acc, Mode) end; inf_tuple_sets([], _, Acc, _Mode) -> lists:reverse(Acc); inf_tuple_sets(_, [], Acc, _Mode) -> lists:reverse(Acc). @@ -2406,17 +2409,17 @@ inf_tuples_in_sets(L1, [?tuple(Elements2, _, ?any)], Mode) -> inf_tuples_in_sets(L1, L2, Mode) -> inf_tuples_in_sets(L1, L2, [], Mode). -inf_tuples_in_sets([?tuple(Elements1, Arity, Tag)|Left1], - [?tuple(Elements2, Arity, Tag)|Left2], Acc, Mode) -> +inf_tuples_in_sets([?tuple(Elements1, Arity, Tag)|Ts1], + [?tuple(Elements2, Arity, Tag)|Ts2], Acc, Mode) -> case t_inf_lists_strict(Elements1, Elements2, Mode) of - bottom -> inf_tuples_in_sets(Left1, Left2, Acc, Mode); - NewElements -> - inf_tuples_in_sets(Left1, Left2, [?tuple(NewElements, Arity, Tag)|Acc], Mode) + bottom -> inf_tuples_in_sets(Ts1, Ts2, Acc, Mode); + NewElements -> + inf_tuples_in_sets(Ts1, Ts2, [?tuple(NewElements, Arity, Tag)|Acc], Mode) end; -inf_tuples_in_sets([?tuple(_, _, Tag1)|Left1] = L1, - [?tuple(_, _, Tag2)|Left2] = L2, Acc, Mode) -> - if Tag1 < Tag2 -> inf_tuples_in_sets(Left1, L2, Acc, Mode); - Tag1 > Tag2 -> inf_tuples_in_sets(L1, Left2, Acc, Mode) +inf_tuples_in_sets([?tuple(_, _, Tag1)|Ts1] = L1, + [?tuple(_, _, Tag2)|Ts2] = L2, Acc, Mode) -> + if Tag1 < Tag2 -> inf_tuples_in_sets(Ts1, L2, Acc, Mode); + Tag1 > Tag2 -> inf_tuples_in_sets(L1, Ts2, Acc, Mode) end; inf_tuples_in_sets([], _, Acc, _Mode) -> lists:reverse(Acc); inf_tuples_in_sets(_, [], Acc, _Mode) -> lists:reverse(Acc). @@ -2791,13 +2794,13 @@ t_subtract(?opaque(Set1), ?opaque(Set2)) -> Set -> ?opaque(Set) end; t_subtract(?matchstate(Pres1, Slots1), ?matchstate(Pres2, _Slots2)) -> - Pres = t_subtract(Pres1,Pres2), + Pres = t_subtract(Pres1, Pres2), case t_is_none(Pres) of true -> ?none; - false -> ?matchstate(Pres,Slots1) + false -> ?matchstate(Pres, Slots1) end; -t_subtract(?matchstate(Present,Slots),_) -> - ?matchstate(Present,Slots); +t_subtract(?matchstate(Present, Slots), _) -> + ?matchstate(Present, Slots); t_subtract(?nil, ?nil) -> ?none; t_subtract(?nil, ?nonempty_list(_, _)) -> @@ -3634,7 +3637,7 @@ t_from_form({type, _L, union, Args}, TypeNames, InOpaque, RecDict, VarDict) -> t_from_form({type, _L, Name, Args}, TypeNames, InOpaque, RecDict, VarDict) -> case lookup_type(Name, RecDict) of {type, {_Module, Type, ArgNames}} when length(Args) =:= length(ArgNames) -> - case unfold({type, Name}, TypeNames) of + case can_unfold_more({type, Name}, TypeNames) of true -> List = lists:zipwith( fun(ArgName, ArgType) -> @@ -3655,7 +3658,7 @@ t_from_form({type, _L, Name, Args}, TypeNames, InOpaque, RecDict, VarDict) -> end; {opaque, {Module, Type, ArgNames}} when length(Args) =:= length(ArgNames) -> {Rep, Rret} = - case unfold({opaque, Name}, TypeNames) of + case can_unfold_more({opaque, Name}, TypeNames) of true -> List = lists:zipwith( fun(ArgName, ArgType) -> @@ -3698,7 +3701,7 @@ t_from_form({opaque, _L, Name, {Mod, Args, Rep}}, _TypeNames, _InOpaque, record_from_form({atom, _, Name}, ModFields, TypeNames, InOpaque, RecDict, VarDict) -> - case unfold({record, Name}, TypeNames) of + case can_unfold_more({record, Name}, TypeNames) of true -> case lookup_record(Name, RecDict) of {ok, DeclFields} -> @@ -3716,7 +3719,7 @@ record_from_form({atom, _, Name}, ModFields, TypeNames, InOpaque, RecDict, RecDict, VarDict), case GetModRec of {error, FieldName} -> - throw({error, io_lib:format("Illegal declaration of ~w#{~w}\n", + throw({error, io_lib:format("Illegal declaration of #~w{~w}\n", [Name, FieldName])}); {ok, NewFields} -> {t_tuple( @@ -3724,8 +3727,7 @@ record_from_form({atom, _, Name}, ModFields, TypeNames, InOpaque, RecDict, R1 ++ R2} end; error -> - throw({error, erlang:error(io_lib:format("Unknown record #~w{}\n", - [Name]))}) + throw({error, io_lib:format("Unknown record #~w{}\n", [Name])}) end; false -> {t_any(), []} end. @@ -3946,8 +3948,9 @@ lookup_type(Name, RecDict) -> type_is_defined(TypeOrOpaque, Name, RecDict) -> dict:is_key({TypeOrOpaque, Name}, RecDict). -unfold(TypeName, TypeNames) -> - not lists:member(TypeName, TypeNames). +can_unfold_more(TypeName, TypeNames) -> + Fun = fun(E, Acc) -> case E of TypeName -> Acc + 1; _ -> Acc end end, + lists:foldl(Fun, 0, TypeNames) < ?REC_TYPE_LIMIT. %% ----------------------------------- %% Set diff --git a/lib/hipe/icode/hipe_beam_to_icode.erl b/lib/hipe/icode/hipe_beam_to_icode.erl index 1f8be4040e..920c94d85c 100644 --- a/lib/hipe/icode/hipe_beam_to_icode.erl +++ b/lib/hipe/icode/hipe_beam_to_icode.erl @@ -369,6 +369,10 @@ trans_fun([{bif,BifName,{f,Lbl},[_] = Args,Reg}|Instructions], Env) -> trans_fun([{bif,BifName,{f,Lbl},[_,_] = Args,Reg}|Instructions], Env) -> {BifInsts,Env1} = trans_bif(2,BifName,Lbl,Args,Reg,Env), [hipe_icode:mk_comment({bif2,BifName})|BifInsts] ++ trans_fun(Instructions,Env1); +%%--- bif3 --- +trans_fun([{bif,BifName,{f,Lbl},[_,_,_] = Args,Reg}|Instructions], Env) -> + {BifInsts,Env1} = trans_bif(3,BifName,Lbl,Args,Reg,Env), + [hipe_icode:mk_comment({bif3,BifName})|BifInsts] ++ trans_fun(Instructions,Env1); %%--- allocate trans_fun([{allocate,StackSlots,_}|Instructions], Env) -> trans_allocate(StackSlots) ++ trans_fun(Instructions,Env); diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml index 9859183390..edd6ea52b0 100644 --- a/lib/kernel/doc/src/notes.xml +++ b/lib/kernel/doc/src/notes.xml @@ -30,6 +30,29 @@ </header> <p>This document describes the changes made to the Kernel application.</p> +<section><title>Kernel 2.14.1.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p>In embedded mode, on_load handlers that called + <c>code:priv_dir/1</c> or other functions in <c>code</c> + would hang the system. Since the <c>crypto</c> + application now contains an on_loader handler that calls + <c>code:priv_dir/1</c>, including the <c>crypto</c> + application in the boot file would prevent the system + from starting.</p> + <p>Also extended the <c>-init_debug</c> option to print + information about on_load handlers being run to + facilitate debugging.</p> + <p> + Own Id: OTP-8902 Aux Id: seq11703 </p> + </item> + </list> + </section> + +</section> + <section><title>Kernel 2.14.1</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/kernel/src/kernel.erl b/lib/kernel/src/kernel.erl index 92ee7b441a..1e07620a3e 100644 --- a/lib/kernel/src/kernel.erl +++ b/lib/kernel/src/kernel.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2010. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -143,6 +143,13 @@ init(safe) -> Boot = start_boot_server(), DiskLog = start_disk_log(), Pg2 = start_pg2(), + + %% Run the on_load handlers for all modules that have been + %% loaded so far. Running them at this point means that + %% on_load handlers can safely call kernel processes + %% (and in particular call code:priv_dir/1 or code:lib_dir/1). + init:run_on_load_handlers(), + {ok, {SupFlags, Boot ++ DiskLog ++ Pg2}}. get_code_args() -> diff --git a/lib/kernel/test/code_SUITE_data/on_load_app-1.0/src/on_load_embedded.erl b/lib/kernel/test/code_SUITE_data/on_load_app-1.0/src/on_load_embedded.erl index a39332f81d..b7fdd4d9ae 100644 --- a/lib/kernel/test/code_SUITE_data/on_load_app-1.0/src/on_load_embedded.erl +++ b/lib/kernel/test/code_SUITE_data/on_load_app-1.0/src/on_load_embedded.erl @@ -3,6 +3,15 @@ -on_load(run_me/0). run_me() -> + %% An onload handler typically calls code:priv_dir/1 + %% or code:lib_dir/1, so make sure that it works. + LibDir = code:lib_dir(on_load_app), + PrivDir = code:priv_dir(on_load_app), + LibDir = filename:dirname(PrivDir), + ModPath = code:which(?MODULE), + LibDir = filename:dirname(filename:dirname(ModPath)), + + %% Start a process to remember that the on_load was called. spawn(fun() -> register(everything_is_fine, self()), receive Any -> diff --git a/lib/odbc/c_src/odbcserver.c b/lib/odbc/c_src/odbcserver.c index c9627e9d05..84b9a99192 100644 --- a/lib/odbc/c_src/odbcserver.c +++ b/lib/odbc/c_src/odbcserver.c @@ -1599,7 +1599,7 @@ static Boolean decode_params(db_state *state, byte *buffer, int *index, param_ar break; case SQL_C_TYPE_TIMESTAMP: ts = (TIMESTAMP_STRUCT*) param->values.string; - ei_decode_tuple_header(buffer, index, &val); + ei_decode_tuple_header(buffer, index, &size); ei_decode_long(buffer, index, &val); ts[j].year = (SQLUSMALLINT)val; ei_decode_long(buffer, index, &val); @@ -1727,74 +1727,48 @@ static byte * receive_erlang_port_msg(void) } /* ------------- Socket communication functions --------------------------*/ -#define USE_IPV4 -#ifdef UNIX -#define SOCKET int -#endif -#if defined WIN32 || defined USE_IPV4 -/* Currently only an old windows compiler is supported so we do not have ipv6 - capabilities */ +#if defined(WIN32) static SOCKET connect_to_erlang(const char *port) -{ - SOCKET sock; - struct sockaddr_in sin; - - sock = socket(AF_INET, SOCK_STREAM, 0); - - memset(&sin, 0, sizeof(sin)); - sin.sin_port = htons ((unsigned short)atoi(port)); - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = inet_addr("127.0.0.1"); - - if (connect(sock, (struct sockaddr*)&sin, sizeof(sin)) != 0) { - close_socket(sock); - DO_EXIT(EXIT_SOCKET_CONNECT); - } - return sock; -} #elif defined(UNIX) static int connect_to_erlang(const char *port) +#endif { - int sock; - - struct addrinfo hints; - struct addrinfo *erlang_ai, *first; - - memset(&hints, 0, sizeof(hints)); - hints.ai_family = PF_UNSPEC; /* PF_INET or PF_INET6 */ - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = IPPROTO_TCP; - - if (getaddrinfo("localhost", port, &hints, &first) != 0) { - DO_EXIT(EXIT_FAILURE); - } +#if defined(WIN32) + SOCKET sock; +#elif defined(UNIX) + int sock; +#endif + struct sockaddr_in sin; + +#if defined(HAVE_STRUCT_SOCKADDR_IN6_SIN6_ADDR) && defined(AF_INET6) + struct sockaddr_in6 sin6; + + sock = socket(AF_INET6, SOCK_STREAM, 0); + + memset(&sin6, 0, sizeof(sin6)); + sin6.sin6_port = htons ((unsigned short)atoi(port)); + sin6.sin6_family = AF_INET6; + sin6.sin6_addr = in6addr_loopback; - for (erlang_ai = first; erlang_ai; erlang_ai = erlang_ai->ai_next) { + if (connect(sock, (struct sockaddr*)&sin6, sizeof(sin6)) == 0) { + return sock; + } + close_socket(sock); +#endif + sock = socket(AF_INET, SOCK_STREAM, 0); + + memset(&sin, 0, sizeof(sin)); + sin.sin_port = htons ((unsigned short)atoi(port)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - sock = socket(erlang_ai->ai_family, erlang_ai->ai_socktype, - erlang_ai->ai_protocol); - if (sock < 0) - continue; - if (connect(sock, (struct sockaddr*)erlang_ai->ai_addr, - erlang_ai->ai_addrlen) < 0) { - close(sock); - sock = -1; - continue; - } else { - break; + if (connect(sock, (struct sockaddr*)&sin, sizeof(sin)) != 0) { + close_socket(sock); + DO_EXIT(EXIT_SOCKET_CONNECT); } - } - freeaddrinfo(first); - - if (sock < 0){ - close_socket(sock); - DO_EXIT(EXIT_SOCKET_CONNECT); - } - - return sock; + return sock; } -#endif #ifdef WIN32 static void close_socket(SOCKET socket) @@ -2177,9 +2151,9 @@ static void init_param_column(param_array *params, byte *buffer, int *index, params->type.sql = SQL_TYPE_TIMESTAMP; params->type.len = sizeof(TIMESTAMP_STRUCT); params->type.c = SQL_C_TYPE_TIMESTAMP; - params->type.col_size = (SQLUINTEGER)19;//;sizeof(TIMESTAMP_STRUCT); + params->type.col_size = (SQLUINTEGER)COL_SQL_TIMESTAMP; params->values.string = - (TIMESTAMP_STRUCT *)safe_malloc(num_param_values * params->type.len); + (byte *)safe_malloc(num_param_values * params->type.len); break; case USER_FLOAT: params->type.sql = SQL_FLOAT; diff --git a/lib/odbc/c_src/odbcserver.h b/lib/odbc/c_src/odbcserver.h index e6d8df1f58..3e2b22ab7d 100644 --- a/lib/odbc/c_src/odbcserver.h +++ b/lib/odbc/c_src/odbcserver.h @@ -98,6 +98,7 @@ #define COL_SQL_REAL 7 #define COL_SQL_DOUBLE 15 #define COL_SQL_TINYINT 4 +#define COL_SQL_TIMESTAMP 19 /* Types of parameters given to param_query*/ #define USER_SMALL_INT 1 diff --git a/lib/odbc/configure.in b/lib/odbc/configure.in index 94e8a214d4..c16d4f3880 100644 --- a/lib/odbc/configure.in +++ b/lib/odbc/configure.in @@ -118,11 +118,18 @@ AC_CHECK_FUNC(gethostbyname, , AC_CHECK_LIB(nsl, main, [LIBS="$LIBS -lnsl"])) dnl Checks for header files. AC_HEADER_STDC -AC_CHECK_HEADERS([fcntl.h netdb.h stdlib.h string.h sys/socket.h]) +AC_CHECK_HEADERS([fcntl.h netdb.h stdlib.h string.h sys/socket.h winsock2.h]) dnl Checks for typedefs, structures, and compiler characteristics. AC_C_CONST AC_TYPE_SIZE_T +AC_CHECK_MEMBERS([struct sockaddr_in6.sin6_addr], [], [], + [#if HAVE_WINSOCK2_H + #include <winsock2.h> + #include <windows.h> + #else + #include <netinet/in.h> + #endif]) dnl Checks for library functions. AC_CHECK_FUNCS([memset socket]) diff --git a/lib/odbc/src/odbc.erl b/lib/odbc/src/odbc.erl index eb27a471ec..83d9f33102 100644 --- a/lib/odbc/src/odbc.erl +++ b/lib/odbc/src/odbc.erl @@ -441,10 +441,12 @@ init(Args) -> {ok, ListenSocketSup} = gen_tcp:listen(0, [Inet, binary, {packet, ?LENGTH_INDICATOR_SIZE}, - {active, false}, {nodelay, true}]), + {active, false}, {nodelay, true}, + {ip, loopback}]), {ok, ListenSocketOdbc} = gen_tcp:listen(0, [Inet, binary, {packet, ?LENGTH_INDICATOR_SIZE}, - {active, false}, {nodelay, true}]), + {active, false}, {nodelay, true}, + {ip, loopback}]), %% Start the port program (a c program) that utilizes the odbc driver case os:find_executable(?SERVERPROG, ?SERVERDIR) of diff --git a/lib/orber/COSS/CosNaming/orber_cosnaming_utils.erl b/lib/orber/COSS/CosNaming/orber_cosnaming_utils.erl index 7792839e22..768653c898 100644 --- a/lib/orber/COSS/CosNaming/orber_cosnaming_utils.erl +++ b/lib/orber/COSS/CosNaming/orber_cosnaming_utils.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% Copyright Ericsson AB 1999-2010. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -536,8 +536,15 @@ lookup(_, _Ctx) -> receive_msg(Socket, Acc, Timeout) -> receive {tcp_closed, Socket} -> - [_Header, Body] = re:split(Acc,"\r\n\r\n",[{return,list}]), - Body; + case re:split(Acc,"\r\n\r\n",[{return,list}]) of + [_Header, Body] -> + Body; + What -> + orber:dbg("[~p] orber_cosnaming_utils:receive_msg();~n" + "HTTP server closed the connection before sending a complete reply: ~p.", + [?LINE, What], ?DEBUG_LEVEL), + corba:raise(#'COMM_FAILURE'{completion_status=?COMPLETED_NO}) + end; {tcp, Socket, Response} -> receive_msg(Socket, Acc ++ Response, Timeout); {tcp_error, Socket, Reason} -> diff --git a/lib/orber/doc/src/notes.xml b/lib/orber/doc/src/notes.xml index 17f7ac8270..6eda16a517 100644 --- a/lib/orber/doc/src/notes.xml +++ b/lib/orber/doc/src/notes.xml @@ -32,23 +32,41 @@ <file>notes.xml</file> </header> - <section><title>Orber 3.6.17</title> + <section> + <title>Orber 3.6.18</title> + <section> + <title>Fixed Bugs and Malfunctions</title> + <list type="bulleted"> + <item> + <p>A corbaloc http string could return an EXIT message, instead + of a system exception, if the HTTP server closed the socket + without returning a complete message. I.e. header and a body + containing a stringified IOR.</p> + <p>Own Id: OTP-8900 Aux Id: seq11704</p> + </item> + </list> + </section> + </section> - <section><title>Improvements and New Features</title> - <list> + <section> + <title>Orber 3.6.17</title> + + <section> + <title>Improvements and New Features</title> + <list type="bulleted"> <item> <p> - Eliminated warnings for auto-imported BIF clashes.</p> + Eliminated warnings for auto-imported BIF clashes.</p> <p> - Own Id: OTP-8840</p> + Own Id: OTP-8840</p> </item> </list> </section> + </section> -</section> - -<section> + <section> <title>Orber 3.6.16</title> + <section> <title>Improvements and New Features</title> <list type="bulleted"> @@ -56,16 +74,17 @@ <p> Test suites published.</p> <p> - Own Id: OTP-8543 Aux Id:</p> + Own Id: OTP-8543O Aux Id:</p> </item> </list> </section> + <section> <title>Fixed Bugs and Malfunctions</title> <list type="bulleted"> <item> <p>Added missing trailing bracket to define in hrl-file.</p> - <p>Own id: OTP-8489 Aux Id:</p> + <p>Own Id: OTP-8489 Aux Id:</p> </item> </list> </section> @@ -104,11 +123,11 @@ <list type="bulleted"> <item> <p>Removed superfluous VT in the documentation.</p> - <p>Own id: OTP-8353 Aux Id:</p> + <p>Own Id: OTP-8353 Aux Id:</p> </item> <item> <p>Removed superfluous backslash in the documentation.</p> - <p>Own id: OTP-8354 Aux Id:</p> + <p>Own Id: OTP-8354 Aux Id:</p> </item> </list> </section> @@ -140,7 +159,7 @@ <item> <p>Obsolete guards, e.g. record vs is_record, has been changed to avoid compiler warnings.</p> - <p>Own id: OTP-7987</p> + <p>Own Id: OTP-7987</p> </item> </list> </section> @@ -158,7 +177,7 @@ Naming Service (INS) instead. INS is a part of the OMG standard specification.</p> <p>*** POTENTIAL INCOMPATIBILITY ***</p> - <p>Own id: OTP-7906 Aux Id: seq11243</p> + <p>Own Id: OTP-7906 Aux Id: seq11243</p> </item> </list> </section> @@ -172,7 +191,7 @@ <list type="bulleted"> <item> <p>Updated file headers.</p> - <p>Own id: OTP-7837</p> + <p>Own Id: OTP-7837</p> </item> </list> </section> @@ -186,7 +205,7 @@ <list type="bulleted"> <item> <p>Documentation source included in open source releases.</p> - <p>Own id: OTP-7595</p> + <p>Own Id: OTP-7595</p> </item> </list> </section> @@ -200,11 +219,11 @@ <list type="bulleted"> <item> <p>Updated file headers.</p> - <p>Own id: OTP-7011</p> + <p>Own Id: OTP-7011</p> </item> <item> <p>Now compliant with the new behavior of stdlib.</p> - <p>Own id: OTP-7030 Aux Id: seq10827</p> + <p>Own Id: OTP-7030 Aux Id: seq10827</p> </item> </list> </section> diff --git a/lib/orber/vsn.mk b/lib/orber/vsn.mk index 681b82b51b..584a52ab84 100644 --- a/lib/orber/vsn.mk +++ b/lib/orber/vsn.mk @@ -1 +1 @@ -ORBER_VSN = 3.6.17 +ORBER_VSN = 3.6.18 diff --git a/lib/parsetools/include/yeccpre.hrl b/lib/parsetools/include/yeccpre.hrl index 39dea0552d..80a3afbdb6 100644 --- a/lib/parsetools/include/yeccpre.hrl +++ b/lib/parsetools/include/yeccpre.hrl @@ -167,7 +167,7 @@ yecctoken2string({char,_,C}) -> io_lib:write_char(C); yecctoken2string({var,_,V}) -> io_lib:format("~s", [V]); yecctoken2string({string,_,S}) -> io_lib:write_unicode_string(S); yecctoken2string({reserved_symbol, _, A}) -> io_lib:write(A); -yecctoken2string({_Cat, _, Val}) -> io_lib:write(Val); +yecctoken2string({_Cat, _, Val}) -> io_lib:format("~p",[Val]); yecctoken2string({dot, _}) -> "'.'"; yecctoken2string({'$end', _}) -> []; diff --git a/lib/parsetools/test/yecc_SUITE.erl b/lib/parsetools/test/yecc_SUITE.erl index 93949a074a..8153be7e61 100644 --- a/lib/parsetools/test/yecc_SUITE.erl +++ b/lib/parsetools/test/yecc_SUITE.erl @@ -46,7 +46,7 @@ bugs/1, otp_5369/1, otp_6362/1, otp_7945/1, otp_8483/1, otp_8486/1, improvements/1, - otp_7292/1, otp_7969/1]). + otp_7292/1, otp_7969/1, otp_8919/1]). % Default timetrap timeout (set in init_per_testcase). -define(default_timeout, ?t:minutes(1)). @@ -1541,7 +1541,7 @@ otp_8486(Config) when is_list(Config) -> ok. improvements(suite) -> - [otp_7292, otp_7969]. + [otp_7292, otp_7969, otp_8919]. otp_7292(doc) -> "OTP-7292. Header declarations for edoc."; @@ -1773,6 +1773,14 @@ otp_7969(Config) when is_list(Config) -> ?line {error,{{1,11},erl_parse,_}} = erl_parse:parse_and_scan({F6, []}), ok. +otp_8919(doc) -> + "OTP-8919. Improve formating of Yecc error messages."; +otp_8919(suite) -> []; +otp_8919(Config) when is_list(Config) -> + {error,{1,Mod,Mess}} = erl_parse:parse([{cat,1,"hello"}]), + "syntax error before: \"hello\"" = lists:flatten(Mod:format_error(Mess)), + ok. + yeccpre_size() -> yeccpre_size(default_yeccpre()). diff --git a/lib/public_key/src/pubkey_cert.erl b/lib/public_key/src/pubkey_cert.erl index c467e24741..c8953c6818 100644 --- a/lib/public_key/src/pubkey_cert.erl +++ b/lib/public_key/src/pubkey_cert.erl @@ -295,7 +295,7 @@ is_fixed_dh_cert(#'OTPCertificate'{tbsCertificate = %%-------------------------------------------------------------------- --spec verify_fun(#'OTPTBSCertificate'{}, {bad_cert, atom()} | {extension, #'Extension'{}}| +-spec verify_fun(#'OTPCertificate'{}, {bad_cert, atom()} | {extension, #'Extension'{}}| valid | valid_peer, term(), fun()) -> term(). %% %% Description: Gives the user application the opportunity handle path diff --git a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml index 950c249e72..9bedd446f4 100644 --- a/lib/ssh/doc/src/notes.xml +++ b/lib/ssh/doc/src/notes.xml @@ -29,8 +29,52 @@ <file>notes.xml</file> </header> - <section><title>Ssh 2.0.1</title> +<section><title>Ssh 2.0.3</title> + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + The fix regarding OTP-8849 was not included in the + previous version as stated.</p> + <p> + Own Id: OTP-8918</p> + </item> + </list> + </section> +</section> +<section><title>Ssh 2.0.2</title> + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + The ssh_system_sup did not catch noproc and shutdown + messages.</p> + <p> + Own Id: OTP-8863</p> + </item> + <item> + <p> + In some cases a crash report was generated when a + connection was closing down. This was caused by a race + condition between two processes.</p> + <p> + Own Id: OTP-8881 Aux Id: seq11656, seq11648 </p> + </item> + </list> + </section> + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + SSH no longer use deprecated public_key functions.</p> + <p> + Own Id: OTP-8849</p> + </item> + </list> + </section> + </section> + <section><title>Ssh 2.0.1</title> <section><title>Fixed Bugs and Malfunctions</title> <list> <item> diff --git a/lib/ssh/src/ssh.appup.src b/lib/ssh/src/ssh.appup.src index 21f7508555..9c806bcd03 100644 --- a/lib/ssh/src/ssh.appup.src +++ b/lib/ssh/src/ssh.appup.src @@ -19,9 +19,13 @@ {"%VSN%", [ + {"2.0.2", [{load_module, ssh_file, soft_purge, soft_purge, []}]}, + {"2.0.1", [{restart_application, ssh}]} ], [ - ] + {"2.0.2", [{load_module, ssh_file, soft_purge, soft_purge, []}]}, + {"2.0.1", [{restart_application, ssh}]} + ] }. diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl index d46002c494..0ba11b0a26 100644 --- a/lib/ssh/src/ssh_connection_handler.erl +++ b/lib/ssh/src/ssh_connection_handler.erl @@ -705,11 +705,19 @@ generate_event(<<?BYTE(Byte), _/binary>> = Msg, StateName, Byte == ?SSH_MSG_CHANNEL_REQUEST; Byte == ?SSH_MSG_CHANNEL_SUCCESS; Byte == ?SSH_MSG_CHANNEL_FAILURE -> - ssh_connection_manager:event(Pid, Msg), - State = generate_event_new_state(State0, EncData), - next_packet(State), - {next_state, StateName, State}; + try + ssh_connection_manager:event(Pid, Msg), + State = generate_event_new_state(State0, EncData), + next_packet(State), + {next_state, StateName, State} + catch + exit:{noproc, _Reason} -> + Report = io_lib:format("~p Connection Handler terminated: ~p~n", + [self(), Pid]), + error_logger:info_report(Report), + {stop, normal, State0} + end; generate_event(Msg, StateName, State0, EncData) -> Event = ssh_bits:decode(Msg), State = generate_event_new_state(State0, EncData), diff --git a/lib/ssh/src/ssh_file.erl b/lib/ssh/src/ssh_file.erl index 5572349fe7..13722656db 100755 --- a/lib/ssh/src/ssh_file.erl +++ b/lib/ssh/src/ssh_file.erl @@ -198,12 +198,17 @@ read_public_key_v1(File) -> %% pem_type("ssh-rsa") -> "RSA". read_private_key_v2(File, Type) -> - case catch (public_key:pem_to_der(File)) of - {ok, [{_, Bin, not_encrypted}]} -> - decode_private_key_v2(Bin, Type); - Error -> %% Note we do not handle password encrypted keys at the moment - {error, Error} - end. + case file:read_file(File) of + {ok, PemBin} -> + case catch (public_key:pem_decode(PemBin)) of + [{_, Bin, not_encrypted}] -> + decode_private_key_v2(Bin, Type); + Error -> %% Note we do not handle password encrypted keys at the moment + {error, Error} + end; + {error, Reason} -> + {error, Reason} + end. %% case file:read_file(File) of %% {ok,Bin} -> %% case read_pem(binary_to_list(Bin), pem_type(Type)) of diff --git a/lib/ssh/src/ssh_system_sup.erl b/lib/ssh/src/ssh_system_sup.erl index f4570b8a48..920baaadef 100644 --- a/lib/ssh/src/ssh_system_sup.erl +++ b/lib/ssh/src/ssh_system_sup.erl @@ -85,7 +85,7 @@ start_subsystem(SystemSup, Options) -> supervisor:start_child(SystemSup, Spec). stop_subsystem(SystemSup, SubSys) -> - case lists:keyfind(SubSys, 2, supervisor:which_children(SystemSup)) of + case catch lists:keyfind(SubSys, 2, supervisor:which_children(SystemSup)) of false -> {error, not_found}; {Id, _, _, _} -> diff --git a/lib/ssh/vsn.mk b/lib/ssh/vsn.mk index 79fd36cd83..db03168ad9 100644 --- a/lib/ssh/vsn.mk +++ b/lib/ssh/vsn.mk @@ -1,4 +1,5 @@ #-*-makefile-*- ; force emacs to enter makefile-mode -SSH_VSN = 2.0.1 +SSH_VSN = 2.0.3 APP_VSN = "ssh-$(SSH_VSN)" + diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml index 413703deca..511f1e0bb2 100644 --- a/lib/ssl/doc/src/ssl.xml +++ b/lib/ssl/doc/src/ssl.xml @@ -114,7 +114,7 @@ <p><c>ciphersuite() = {key_exchange(), cipher(), hash()}</c></p> - <p><c>key_exchange() = rsa | dhe_dss | dhe_rsa + <p><c>key_exchange() = rsa | dhe_dss | dhe_rsa | dh_anon </c></p> <p><c>cipher() = rc4_128 | des_cbc | '3des_ede_cbc' @@ -170,8 +170,13 @@ <tag>{ciphers, ciphers()}</tag> <item>The cipher suites that should be supported. The function - <c>ciphers_suites/0</c> can be used to find all available - ciphers. + <c>cipher_suites/0</c> can be used to find all available + ciphers. Additionally some anonymous cipher suites ({dh_anon, + rc4_128, md5}, {dh_anon, des_cbc, sha}, {dh_anon, + '3des_ede_cbc', sha}, {dh_anon, aes_128_cbc, sha}, {dh_anon, + aes_256_cbc, sha}) are supported for testing purposes and will + only work if explicitly enabled by this option and they are supported/enabled + by the peer also. </item> <tag>{ssl_imp, ssl_imp()}</tag> diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index ef94750d02..7e5929d708 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -747,7 +747,7 @@ validate_option(depth, Value) when is_integer(Value), validate_option(cert, Value) when Value == undefined; is_binary(Value) -> Value; -validate_option(certfile, Value) when is_list(Value) -> +validate_option(certfile, Value) when Value == undefined; is_list(Value) -> Value; validate_option(key, undefined) -> @@ -890,7 +890,7 @@ cipher_suites(Version, [{_,_,_}| _] = Ciphers0) -> Ciphers = [ssl_cipher:suite(C) || C <- Ciphers0], cipher_suites(Version, Ciphers); cipher_suites(Version, [Cipher0 | _] = Ciphers0) when is_binary(Cipher0) -> - Supported = ssl_cipher:suites(Version), + Supported = ssl_cipher:suites(Version) ++ ssl_cipher:anonymous_suites(), case [Cipher || Cipher <- Ciphers0, lists:member(Cipher, Supported)] of [] -> Supported; diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl index 8230149304..9824e17fcd 100644 --- a/lib/ssl/src/ssl_cipher.erl +++ b/lib/ssl/src/ssl_cipher.erl @@ -34,7 +34,7 @@ -export([security_parameters/2, suite_definition/1, decipher/5, cipher/4, - suite/1, suites/1, + suite/1, suites/1, anonymous_suites/0, openssl_suite/1, openssl_suite_name/1, filter/2]). -compile(inline). @@ -191,6 +191,19 @@ suites({3, N}) when N == 1; N == 2 -> ssl_tls1:suites(). %%-------------------------------------------------------------------- +-spec anonymous_suites() -> [cipher_suite()]. +%% +%% Description: Returns a list of the anonymous cipher suites, only supported +%% if explicitly set by user. Intended only for testing. +%%-------------------------------------------------------------------- +anonymous_suites() -> + [?TLS_DH_anon_WITH_RC4_128_MD5, + ?TLS_DH_anon_WITH_DES_CBC_SHA, + ?TLS_DH_anon_WITH_3DES_EDE_CBC_SHA, + ?TLS_DH_anon_WITH_AES_128_CBC_SHA, + ?TLS_DH_anon_WITH_AES_256_CBC_SHA]. + +%%-------------------------------------------------------------------- -spec suite_definition(cipher_suite()) -> erl_cipher_suite(). %% %% Description: Return erlang cipher suite definition. @@ -235,7 +248,20 @@ suite_definition(?TLS_RSA_WITH_AES_256_CBC_SHA) -> suite_definition(?TLS_DHE_DSS_WITH_AES_256_CBC_SHA) -> {dhe_dss, aes_256_cbc, sha}; suite_definition(?TLS_DHE_RSA_WITH_AES_256_CBC_SHA) -> - {dhe_rsa, aes_256_cbc, sha}. + {dhe_rsa, aes_256_cbc, sha}; + +%%% DH-ANON deprecated by TLS spec and not available +%%% by default, but good for testing purposes. +suite_definition(?TLS_DH_anon_WITH_RC4_128_MD5) -> + {dh_anon, rc4_128, md5}; +suite_definition(?TLS_DH_anon_WITH_DES_CBC_SHA) -> + {dh_anon, des_cbc, sha}; +suite_definition(?TLS_DH_anon_WITH_3DES_EDE_CBC_SHA) -> + {dh_anon, '3des_ede_cbc', sha}; +suite_definition(?TLS_DH_anon_WITH_AES_128_CBC_SHA) -> + {dh_anon, aes_128_cbc, sha}; +suite_definition(?TLS_DH_anon_WITH_AES_256_CBC_SHA) -> + {dh_anon, aes_256_cbc, sha}. %%-------------------------------------------------------------------- -spec suite(erl_cipher_suite()) -> cipher_suite(). @@ -266,12 +292,12 @@ suite({dhe_rsa, des_cbc, sha}) -> ?TLS_DHE_RSA_WITH_DES_CBC_SHA; suite({dhe_rsa, '3des_ede_cbc', sha}) -> ?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA; -%% suite({dh_anon, rc4_128, md5}) -> -%% ?TLS_DH_anon_WITH_RC4_128_MD5; -%% suite({dh_anon, des40_cbc, sha}) -> -%% ?TLS_DH_anon_WITH_DES_CBC_SHA; -%% suite({dh_anon, '3des_ede_cbc', sha}) -> -%% ?TLS_DH_anon_WITH_3DES_EDE_CBC_SHA; +suite({dh_anon, rc4_128, md5}) -> + ?TLS_DH_anon_WITH_RC4_128_MD5; +suite({dh_anon, des_cbc, sha}) -> + ?TLS_DH_anon_WITH_DES_CBC_SHA; +suite({dh_anon, '3des_ede_cbc', sha}) -> + ?TLS_DH_anon_WITH_3DES_EDE_CBC_SHA; %%% TSL V1.1 AES suites suite({rsa, aes_128_cbc, sha}) -> @@ -280,16 +306,16 @@ suite({dhe_dss, aes_128_cbc, sha}) -> ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA; suite({dhe_rsa, aes_128_cbc, sha}) -> ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA; -%% suite({dh_anon, aes_128_cbc, sha}) -> -%% ?TLS_DH_anon_WITH_AES_128_CBC_SHA; +suite({dh_anon, aes_128_cbc, sha}) -> + ?TLS_DH_anon_WITH_AES_128_CBC_SHA; suite({rsa, aes_256_cbc, sha}) -> ?TLS_RSA_WITH_AES_256_CBC_SHA; suite({dhe_dss, aes_256_cbc, sha}) -> ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA; suite({dhe_rsa, aes_256_cbc, sha}) -> - ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA. -%% suite({dh_anon, aes_256_cbc, sha}) -> -%% ?TLS_DH_anon_WITH_AES_256_CBC_SHA. + ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA; +suite({dh_anon, aes_256_cbc, sha}) -> + ?TLS_DH_anon_WITH_AES_256_CBC_SHA. %%-------------------------------------------------------------------- -spec openssl_suite(openssl_cipher_suite()) -> cipher_suite(). @@ -580,5 +606,3 @@ filter_rsa_suites(Use, KeyUse, CipherSuits, RsaSuites) -> false -> CipherSuits -- RsaSuites end. - - diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index bd1ba6978a..3a9cada81e 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -374,7 +374,7 @@ hello(#server_hello{cipher_suite = CipherSuite, case ssl_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation) of {Version, NewId, ConnectionStates} -> - {KeyAlgorithm, _, _} = + {KeyAlgorithm, _, _} = ssl_cipher:suite_definition(CipherSuite), PremasterSecret = make_premaster_secret(ReqVersion, KeyAlgorithm), @@ -512,7 +512,7 @@ certify(#certificate{} = Cert, certify(#server_key_exchange{} = KeyExchangeMsg, #state{role = client, negotiated_version = Version, key_algorithm = Alg} = State0) - when Alg == dhe_dss; Alg == dhe_rsa -> + when Alg == dhe_dss; Alg == dhe_rsa; Alg == dh_anon -> case handle_server_key(KeyExchangeMsg, State0) of #state{} = State1 -> {Record, State} = next_record(State1), @@ -613,25 +613,10 @@ certify_client_key_exchange(#client_diffie_hellman_public{dh_public = ClientPubl #state{negotiated_version = Version, diffie_hellman_params = #'DHParameter'{prime = P, base = G}, - diffie_hellman_keys = {_, ServerDhPrivateKey}, - role = Role, - session = Session, - connection_states = ConnectionStates0} = State0) -> - - PMpint = crypto:mpint(P), - GMpint = crypto:mpint(G), - PremasterSecret = crypto:dh_compute_key(mpint_binary(ClientPublicDhKey), - ServerDhPrivateKey, - [PMpint, GMpint]), - - case ssl_handshake:master_secret(Version, PremasterSecret, - ConnectionStates0, Role) of - {MasterSecret, ConnectionStates} -> - State1 = State0#state{session = - Session#session{master_secret - = MasterSecret}, - connection_states = ConnectionStates}, + diffie_hellman_keys = {_, ServerDhPrivateKey}} = State0) -> + case dh_master_secret(crypto:mpint(P), crypto:mpint(G), ClientPublicDhKey, ServerDhPrivateKey, State0) of + #state{} = State1 -> {Record, State} = next_record(State1), next_state(cipher, Record, State); #alert{} = Alert -> @@ -653,12 +638,10 @@ cipher(#certificate_verify{signature = Signature}, public_key_info = PublicKeyInfo, negotiated_version = Version, session = #session{master_secret = MasterSecret}, - key_algorithm = Algorithm, tls_handshake_hashes = Hashes } = State0) -> case ssl_handshake:certificate_verify(Signature, PublicKeyInfo, - Version, MasterSecret, - Algorithm, Hashes) of + Version, MasterSecret, Hashes) of valid -> {Record, State} = next_record(State0), next_state(cipher, Record, State); @@ -1058,6 +1041,8 @@ init_certificates(#ssl_options{cacerts = CaCerts, end, init_certificates(Cert, CertDbRef, CacheRef, CertFile, Role). +init_certificates(undefined, CertDbRef, CacheRef, "", _) -> + {ok, CertDbRef, CacheRef, undefined}; init_certificates(undefined, CertDbRef, CacheRef, CertFile, client) -> try @@ -1068,18 +1053,18 @@ init_certificates(undefined, CertDbRef, CacheRef, CertFile, client) -> end; init_certificates(undefined, CertDbRef, CacheRef, CertFile, server) -> - try + try [OwnCert] = ssl_certificate:file_to_certificats(CertFile), {ok, CertDbRef, CacheRef, OwnCert} - catch - Error:Reason -> - handle_file_error(?LINE, Error, Reason, CertFile, ecertfile, - erlang:get_stacktrace()) - end; + catch + Error:Reason -> + handle_file_error(?LINE, Error, Reason, CertFile, ecertfile, + erlang:get_stacktrace()) + end; init_certificates(Cert, CertDbRef, CacheRef, _, _) -> {ok, CertDbRef, CacheRef, Cert}. -init_private_key(undefined, "", _Password, client) -> +init_private_key(undefined, "", _Password, _Client) -> undefined; init_private_key(undefined, KeyFile, Password, _) -> try @@ -1182,16 +1167,15 @@ verify_client_cert(#state{client_certificate_requested = true, role = client, negotiated_version = Version, own_cert = OwnCert, socket = Socket, - key_algorithm = KeyAlg, private_key = PrivateKey, session = #session{master_secret = MasterSecret}, tls_handshake_hashes = Hashes0} = State) -> + case ssl_handshake:client_certificate_verify(OwnCert, MasterSecret, - Version, KeyAlg, - PrivateKey, Hashes0) of + Version, PrivateKey, Hashes0) of #certificate_verify{} = Verified -> {BinVerified, ConnectionStates1, Hashes1} = - encode_handshake(Verified, KeyAlg, Version, + encode_handshake(Verified, Version, ConnectionStates0, Hashes0), Transport:send(Socket, BinVerified), State#state{connection_states = ConnectionStates1, @@ -1340,15 +1324,17 @@ server_hello_done(#state{transport_cb = Transport, Transport:send(Socket, BinHelloDone), State#state{connection_states = NewConnectionStates, tls_handshake_hashes = NewHashes}. - -certify_server(#state{transport_cb = Transport, - socket = Socket, - negotiated_version = Version, - connection_states = ConnectionStates, - tls_handshake_hashes = Hashes, - cert_db_ref = CertDbRef, - own_cert = OwnCert} = State) -> +certify_server(#state{key_algorithm = dh_anon} = State) -> + State; + +certify_server(#state{transport_cb = Transport, + socket = Socket, + negotiated_version = Version, + connection_states = ConnectionStates, + tls_handshake_hashes = Hashes, + cert_db_ref = CertDbRef, + own_cert = OwnCert} = State) -> case ssl_handshake:certificate(OwnCert, CertDbRef, server) of CertMsg = #certificate{} -> {BinCertMsg, NewConnectionStates, NewHashes} = @@ -1373,7 +1359,8 @@ key_exchange(#state{role = server, key_algorithm = Algo, transport_cb = Transport } = State) when Algo == dhe_dss; - Algo == dhe_rsa -> + Algo == dhe_rsa; + Algo == dh_anon -> Keys = crypto:dh_generate_key([crypto:mpint(P), crypto:mpint(G)]), ConnectionState = @@ -1392,11 +1379,6 @@ key_exchange(#state{role = server, key_algorithm = Algo, diffie_hellman_keys = Keys, tls_handshake_hashes = Hashes1}; - -%% key_algorithm = dh_anon is not supported. Should be by default disabled -%% if support is implemented and then we need a key_exchange clause for it -%% here. - key_exchange(#state{role = client, connection_states = ConnectionStates0, key_algorithm = rsa, @@ -1419,7 +1401,8 @@ key_exchange(#state{role = client, socket = Socket, transport_cb = Transport, tls_handshake_hashes = Hashes0} = State) when Algorithm == dhe_dss; - Algorithm == dhe_rsa -> + Algorithm == dhe_rsa; + Algorithm == dh_anon -> Msg = ssl_handshake:key_exchange(client, {dh, DhPubKey}), {BinMsg, ConnectionStates1, Hashes1} = encode_handshake(Msg, Version, ConnectionStates0, Hashes0), @@ -1497,23 +1480,30 @@ save_verify_data(client, #finished{verify_data = Data}, ConnectionStates, abbrev save_verify_data(server, #finished{verify_data = Data}, ConnectionStates, abbreviated) -> ssl_record:set_server_verify_data(current_write, Data, ConnectionStates). +handle_server_key(#server_key_exchange{params = + #server_dh_params{dh_p = P, + dh_g = G, + dh_y = ServerPublicDhKey}, + signed_params = <<>>}, + #state{key_algorithm = dh_anon} = State) -> + dh_master_secret(P, G, ServerPublicDhKey, undefined, State); + handle_server_key( #server_key_exchange{params = #server_dh_params{dh_p = P, dh_g = G, dh_y = ServerPublicDhKey}, signed_params = Signed}, - #state{session = Session, negotiated_version = Version, role = Role, - public_key_info = PubKeyInfo, + #state{public_key_info = PubKeyInfo, key_algorithm = KeyAlgo, - connection_states = ConnectionStates0} = State) -> + connection_states = ConnectionStates} = State) -> PLen = size(P), GLen = size(G), YLen = size(ServerPublicDhKey), ConnectionState = - ssl_record:pending_connection_state(ConnectionStates0, read), + ssl_record:pending_connection_state(ConnectionStates, read), SecParams = ConnectionState#connection_state.security_parameters, #security_parameters{client_random = ClientRandom, server_random = ServerRandom} = SecParams, @@ -1527,29 +1517,11 @@ handle_server_key( case verify_dh_params(Signed, Hash, PubKeyInfo) of true -> - PMpint = mpint_binary(P), - GMpint = mpint_binary(G), - Keys = {_, ClientDhPrivateKey} = - crypto:dh_generate_key([PMpint,GMpint]), - PremasterSecret = - crypto:dh_compute_key(mpint_binary(ServerPublicDhKey), - ClientDhPrivateKey, [PMpint, GMpint]), - case ssl_handshake:master_secret(Version, PremasterSecret, - ConnectionStates0, Role) of - {MasterSecret, ConnectionStates} -> - State#state{diffie_hellman_keys = Keys, - session = - Session#session{master_secret - = MasterSecret}, - connection_states = ConnectionStates}; - #alert{} = Alert -> - Alert - end; + dh_master_secret(P, G, ServerPublicDhKey, undefined, State); false -> ?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE) end. - verify_dh_params(Signed, Hashes, {?rsaEncryption, PubKey, _PubKeyParams}) -> case public_key:decrypt_public(Signed, PubKey, [{rsa_pad, rsa_pkcs1_padding}]) of @@ -1561,6 +1533,30 @@ verify_dh_params(Signed, Hashes, {?rsaEncryption, PubKey, _PubKeyParams}) -> verify_dh_params(Signed, Hash, {?'id-dsa', PublicKey, PublicKeyParams}) -> public_key:verify(Hash, none, Signed, {PublicKey, PublicKeyParams}). +dh_master_secret(Prime, Base, PublicDhKey, undefined, State) -> + PMpint = mpint_binary(Prime), + GMpint = mpint_binary(Base), + Keys = {_, PrivateDhKey} = + crypto:dh_generate_key([PMpint,GMpint]), + dh_master_secret(PMpint, GMpint, PublicDhKey, PrivateDhKey, State#state{diffie_hellman_keys = Keys}); + +dh_master_secret(PMpint, GMpint, PublicDhKey, PrivateDhKey, + #state{session = Session, + negotiated_version = Version, role = Role, + connection_states = ConnectionStates0} = State) -> + PremasterSecret = + crypto:dh_compute_key(mpint_binary(PublicDhKey), PrivateDhKey, + [PMpint, GMpint]), + case ssl_handshake:master_secret(Version, PremasterSecret, + ConnectionStates0, Role) of + {MasterSecret, ConnectionStates} -> + State#state{ + session = + Session#session{master_secret = MasterSecret}, + connection_states = ConnectionStates}; + #alert{} = Alert -> + Alert + end. cipher_role(client, Data, Session, #state{connection_states = ConnectionStates0} = State) -> ConnectionStates = ssl_record:set_server_verify_data(current_both, Data, ConnectionStates0), @@ -1585,13 +1581,9 @@ encode_change_cipher(#change_cipher_spec{}, Version, ConnectionStates) -> ?DBG_TERM(#change_cipher_spec{}), ssl_record:encode_change_cipher_spec(Version, ConnectionStates). -encode_handshake(HandshakeRec, Version, ConnectionStates, Hashes) -> - encode_handshake(HandshakeRec, null, Version, - ConnectionStates, Hashes). - -encode_handshake(HandshakeRec, SigAlg, Version, ConnectionStates0, Hashes0) -> +encode_handshake(HandshakeRec, Version, ConnectionStates0, Hashes0) -> ?DBG_TERM(HandshakeRec), - Frag = ssl_handshake:encode_handshake(HandshakeRec, Version, SigAlg), + Frag = ssl_handshake:encode_handshake(HandshakeRec, Version), Hashes1 = ssl_handshake:update_hashes(Hashes0, Frag), {E, ConnectionStates1} = ssl_record:encode_handshake(Frag, Version, ConnectionStates0), @@ -2179,7 +2171,7 @@ renegotiate(#state{role = server, negotiated_version = Version, connection_states = ConnectionStates0} = State0) -> HelloRequest = ssl_handshake:hello_request(), - Frag = ssl_handshake:encode_handshake(HelloRequest, Version, null), + Frag = ssl_handshake:encode_handshake(HelloRequest, Version), Hs0 = ssl_handshake:init_hashes(), {BinMsg, ConnectionStates} = ssl_record:encode_handshake(Frag, Version, ConnectionStates0), diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index 5b1a510034..f8e5d585e7 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -33,11 +33,11 @@ -export([master_secret/4, client_hello/5, server_hello/4, hello/4, hello_request/0, certify/6, certificate/3, - client_certificate_verify/6, certificate_verify/6, + client_certificate_verify/5, certificate_verify/5, certificate_request/2, key_exchange/2, server_key_exchange_hash/2, finished/4, verify_connection/5, get_tls_handshake/2, - decode_client_key/3, server_hello_done/0, sig_alg/1, - encode_handshake/3, init_hashes/0, update_hashes/2, + decode_client_key/3, server_hello_done/0, + encode_handshake/2, init_hashes/0, update_hashes/2, decrypt_premaster_secret/2]). -type tls_handshake() :: #client_hello{} | #server_hello{} | @@ -237,7 +237,7 @@ certificate(OwnCert, CertDbRef, client) -> {error, _} -> %% If no suitable certificate is available, the client %% SHOULD send a certificate message containing no - %% certificates. (chapter 7.4.6. rfc 4346) + %% certificates. (chapter 7.4.6. RFC 4346) [] end, #certificate{asn1_certificates = Chain}; @@ -252,17 +252,17 @@ certificate(OwnCert, CertDbRef, server) -> %%-------------------------------------------------------------------- -spec client_certificate_verify(undefined | der_cert(), binary(), - tls_version(), key_algo(), private_key(), + tls_version(), private_key(), {{binary(), binary()},{binary(), binary()}}) -> #certificate_verify{} | ignore | #alert{}. %% %% Description: Creates a certificate_verify message, called by the client. %%-------------------------------------------------------------------- -client_certificate_verify(undefined, _, _, _, _, _) -> +client_certificate_verify(undefined, _, _, _, _) -> ignore; -client_certificate_verify(_, _, _, _, undefined, _) -> +client_certificate_verify(_, _, _, undefined, _) -> ignore; -client_certificate_verify(OwnCert, MasterSecret, Version, Algorithm, +client_certificate_verify(OwnCert, MasterSecret, Version, PrivateKey, {Hashes0, _}) -> case public_key:pkix_is_fixed_dh_cert(OwnCert) of true -> @@ -270,33 +270,30 @@ client_certificate_verify(OwnCert, MasterSecret, Version, Algorithm, false -> Hashes = calc_certificate_verify(Version, MasterSecret, - Algorithm, Hashes0), + alg_oid(PrivateKey), Hashes0), Signed = digitally_signed(Hashes, PrivateKey), #certificate_verify{signature = Signed} end. %%-------------------------------------------------------------------- -spec certificate_verify(binary(), public_key_info(), tls_version(), - binary(), key_algo(), - {_, {binary(), binary()}}) -> valid | #alert{}. + binary(), {_, {binary(), binary()}}) -> valid | #alert{}. %% %% Description: Checks that the certificate_verify message is valid. %%-------------------------------------------------------------------- -certificate_verify(Signature, {_, PublicKey, _}, Version, - MasterSecret, Algorithm, {_, Hashes0}) - when Algorithm == rsa; - Algorithm == dhe_rsa -> +certificate_verify(Signature, {?'rsaEncryption'= Algorithm, PublicKey, _}, Version, + MasterSecret, {_, Hashes0}) -> Hashes = calc_certificate_verify(Version, MasterSecret, Algorithm, Hashes0), - case public_key:decrypt_public(Signature, PublicKey, + case public_key:decrypt_public(Signature, PublicKey, [{rsa_pad, rsa_pkcs1_padding}]) of Hashes -> valid; _ -> ?ALERT_REC(?FATAL, ?BAD_CERTIFICATE) end; -certificate_verify(Signature, {_, PublicKey, PublicKeyParams}, Version, - MasterSecret, dhe_dss = Algorithm, {_, Hashes0}) -> +certificate_verify(Signature, {?'id-dsa' = Algorithm, PublicKey, PublicKeyParams}, Version, + MasterSecret, {_, Hashes0}) -> Hashes = calc_certificate_verify(Version, MasterSecret, Algorithm, Hashes0), case public_key:verify(Hashes, none, Signature, {PublicKey, PublicKeyParams}) of @@ -355,15 +352,22 @@ key_exchange(server, {dh, {<<?UINT32(Len), PublicKey:Len/binary>>, _}, YLen = byte_size(PublicKey), ServerDHParams = #server_dh_params{dh_p = PBin, dh_g = GBin, dh_y = PublicKey}, - Hash = - server_key_exchange_hash(KeyAlgo, <<ClientRandom/binary, - ServerRandom/binary, - ?UINT16(PLen), PBin/binary, - ?UINT16(GLen), GBin/binary, - ?UINT16(YLen), PublicKey/binary>>), - Signed = digitally_signed(Hash, PrivateKey), - #server_key_exchange{params = ServerDHParams, - signed_params = Signed}. + + case KeyAlgo of + dh_anon -> + #server_key_exchange{params = ServerDHParams, + signed_params = <<>>}; + _ -> + Hash = + server_key_exchange_hash(KeyAlgo, <<ClientRandom/binary, + ServerRandom/binary, + ?UINT16(PLen), PBin/binary, + ?UINT16(GLen), GBin/binary, + ?UINT16(YLen), PublicKey/binary>>), + Signed = digitally_signed(Hash, PrivateKey), + #server_key_exchange{params = ServerDHParams, + signed_params = Signed} + end. %%-------------------------------------------------------------------- -spec master_secret(tls_version(), #session{} | binary(), #connection_states{}, @@ -441,13 +445,12 @@ server_hello_done() -> #server_hello_done{}. %%-------------------------------------------------------------------- --spec encode_handshake(tls_handshake(), tls_version(), key_algo()) -> iolist(). +-spec encode_handshake(tls_handshake(), tls_version()) -> iolist(). %% %% Description: Encode a handshake packet to binary %%-------------------------------------------------------------------- -encode_handshake(Package, Version, KeyAlg) -> - SigAlg = sig_alg(KeyAlg), - {MsgType, Bin} = enc_hs(Package, Version, SigAlg), +encode_handshake(Package, Version) -> + {MsgType, Bin} = enc_hs(Package, Version), Len = byte_size(Bin), [MsgType, ?uint24(Len), Bin]. @@ -526,7 +529,7 @@ decrypt_premaster_secret(Secret, RSAPrivateKey) -> end. %%-------------------------------------------------------------------- --spec server_key_exchange_hash(rsa | dhe_rsa| dhe_dss, binary()) -> binary(). +-spec server_key_exchange_hash(rsa | dhe_rsa| dhe_dss | dh_anon, binary()) -> binary(). %% %% Description: Calculate server key exchange hash @@ -541,21 +544,6 @@ server_key_exchange_hash(dhe_dss, Value) -> crypto:sha(Value). %%-------------------------------------------------------------------- --spec sig_alg(atom()) -> integer(). - -%% -%% Description: Translate atom representation to enum representation. -%%-------------------------------------------------------------------- -sig_alg(dh_anon) -> - ?SIGNATURE_ANONYMOUS; -sig_alg(Alg) when Alg == dhe_rsa; Alg == rsa -> - ?SIGNATURE_RSA; -sig_alg(dhe_dss) -> - ?SIGNATURE_DSA; -sig_alg(_) -> - ?NULL. - -%%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- get_tls_handshake_aux(<<?BYTE(Type), ?UINT24(Length), @@ -876,6 +864,13 @@ dec_hs(?CERTIFICATE, <<?UINT24(ACLen), ASN1Certs:ACLen/binary>>) -> dec_hs(?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P:PLen/binary, ?UINT16(GLen), G:GLen/binary, ?UINT16(YLen), Y:YLen/binary, + ?UINT16(0)>>) -> %% May happen if key_algorithm is dh_anon + #server_key_exchange{params = #server_dh_params{dh_p = P,dh_g = G, + dh_y = Y}, + signed_params = <<>>}; +dec_hs(?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P:PLen/binary, + ?UINT16(GLen), G:GLen/binary, + ?UINT16(YLen), Y:YLen/binary, ?UINT16(Len), Sig:Len/binary>>) -> #server_key_exchange{params = #server_dh_params{dh_p = P,dh_g = G, dh_y = Y}, @@ -958,14 +953,14 @@ certs_from_list(ACList) -> <<?UINT24(CertLen), Cert/binary>> end || Cert <- ACList]). -enc_hs(#hello_request{}, _Version, _) -> +enc_hs(#hello_request{}, _Version) -> {?HELLO_REQUEST, <<>>}; enc_hs(#client_hello{client_version = {Major, Minor}, random = Random, session_id = SessionID, cipher_suites = CipherSuites, compression_methods = CompMethods, - renegotiation_info = RenegotiationInfo}, _Version, _) -> + renegotiation_info = RenegotiationInfo}, _Version) -> SIDLength = byte_size(SessionID), BinCompMethods = list_to_binary(CompMethods), CmLength = byte_size(BinCompMethods), @@ -983,20 +978,20 @@ enc_hs(#server_hello{server_version = {Major, Minor}, session_id = Session_ID, cipher_suite = Cipher_suite, compression_method = Comp_method, - renegotiation_info = RenegotiationInfo}, _Version, _) -> + renegotiation_info = RenegotiationInfo}, _Version) -> SID_length = byte_size(Session_ID), Extensions = hello_extensions(RenegotiationInfo), ExtensionsBin = enc_hello_extensions(Extensions), {?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, ?BYTE(SID_length), Session_ID/binary, Cipher_suite/binary, ?BYTE(Comp_method), ExtensionsBin/binary>>}; -enc_hs(#certificate{asn1_certificates = ASN1CertList}, _Version, _) -> +enc_hs(#certificate{asn1_certificates = ASN1CertList}, _Version) -> ASN1Certs = certs_from_list(ASN1CertList), ACLen = erlang:iolist_size(ASN1Certs), {?CERTIFICATE, <<?UINT24(ACLen), ASN1Certs:ACLen/binary>>}; enc_hs(#server_key_exchange{params = #server_dh_params{ dh_p = P, dh_g = G, dh_y = Y}, - signed_params = SignedParams}, _Version, _) -> + signed_params = SignedParams}, _Version) -> PLen = byte_size(P), GLen = byte_size(G), YLen = byte_size(Y), @@ -1008,21 +1003,21 @@ enc_hs(#server_key_exchange{params = #server_dh_params{ }; enc_hs(#certificate_request{certificate_types = CertTypes, certificate_authorities = CertAuths}, - _Version, _) -> + _Version) -> CertTypesLen = byte_size(CertTypes), CertAuthsLen = byte_size(CertAuths), {?CERTIFICATE_REQUEST, <<?BYTE(CertTypesLen), CertTypes/binary, ?UINT16(CertAuthsLen), CertAuths/binary>> }; -enc_hs(#server_hello_done{}, _Version, _) -> +enc_hs(#server_hello_done{}, _Version) -> {?SERVER_HELLO_DONE, <<>>}; -enc_hs(#client_key_exchange{exchange_keys = ExchangeKeys}, Version, _) -> +enc_hs(#client_key_exchange{exchange_keys = ExchangeKeys}, Version) -> {?CLIENT_KEY_EXCHANGE, enc_cke(ExchangeKeys, Version)}; -enc_hs(#certificate_verify{signature = BinSig}, _, _) -> +enc_hs(#certificate_verify{signature = BinSig}, _) -> EncSig = enc_bin_sig(BinSig), {?CERTIFICATE_VERIFY, EncSig}; -enc_hs(#finished{verify_data = VerifyData}, _Version, _) -> +enc_hs(#finished{verify_data = VerifyData}, _Version) -> {?FINISHED, VerifyData}. enc_cke(#encrypted_premaster_secret{premaster_secret = PKEPMS},{3, 0}) -> @@ -1152,7 +1147,7 @@ calc_certificate_verify({3, N}, _, Algorithm, Hashes) key_exchange_alg(rsa) -> ?KEY_EXCHANGE_RSA; key_exchange_alg(Alg) when Alg == dhe_rsa; Alg == dhe_dss; - Alg == dh_dss; Alg == dh_rsa -> + Alg == dh_dss; Alg == dh_rsa; Alg == dh_anon -> ?KEY_EXCHANGE_DIFFIE_HELLMAN; key_exchange_alg(_) -> ?NULL. @@ -1166,3 +1161,8 @@ apply_user_fun(Fun, OtpCert, ExtensionOrError, UserState0, SslState) -> {unknown, UserState} -> {unknown, {SslState, UserState}} end. + +alg_oid(#'RSAPrivateKey'{}) -> + ?'rsaEncryption'; +alg_oid(#'DSAPrivateKey'{}) -> + ?'id-dsa'. diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl index ddb05e70f6..d2dee4d861 100644 --- a/lib/ssl/src/ssl_internal.hrl +++ b/lib/ssl/src/ssl_internal.hrl @@ -104,7 +104,7 @@ -type tls_atom_version() :: sslv3 | tlsv1. -type cache_ref() :: term(). -type certdb_ref() :: term(). --type key_algo() :: null | rsa | dhe_rsa | dhe_dss. +-type key_algo() :: null | rsa | dhe_rsa | dhe_dss | dh_anon. -type enum_algo() :: integer(). -type public_key() :: #'RSAPublicKey'{} | integer(). -type public_key_params() :: #'Dss-Parms'{} | term(). diff --git a/lib/ssl/src/ssl_ssl3.erl b/lib/ssl/src/ssl_ssl3.erl index 1add203fb0..f3cb6ad66e 100644 --- a/lib/ssl/src/ssl_ssl3.erl +++ b/lib/ssl/src/ssl_ssl3.erl @@ -79,10 +79,9 @@ finished(Role, MasterSecret, {MD5Hash, SHAHash}) -> SHA = handshake_hash(?SHA, MasterSecret, Sender, SHAHash), <<MD5/binary, SHA/binary>>. --spec certificate_verify(key_algo(), binary(), {binary(), binary()}) -> binary(). +-spec certificate_verify(OID::tuple(), binary(), {binary(), binary()}) -> binary(). -certificate_verify(Algorithm, MasterSecret, {MD5Hash, SHAHash}) - when Algorithm == rsa; Algorithm == dhe_rsa -> +certificate_verify(?'rsaEncryption', MasterSecret, {MD5Hash, SHAHash}) -> %% md5_hash %% MD5(master_secret + pad_2 + %% MD5(handshake_messages + master_secret + pad_1)); @@ -94,7 +93,7 @@ certificate_verify(Algorithm, MasterSecret, {MD5Hash, SHAHash}) SHA = handshake_hash(?SHA, MasterSecret, undefined, SHAHash), <<MD5/binary, SHA/binary>>; -certificate_verify(dhe_dss, MasterSecret, {_, SHAHash}) -> +certificate_verify(?'id-dsa', MasterSecret, {_, SHAHash}) -> %% sha_hash %% SHA(master_secret + pad_2 + %% SHA(handshake_messages + master_secret + pad_1)); diff --git a/lib/ssl/src/ssl_tls1.erl b/lib/ssl/src/ssl_tls1.erl index d1bc0730ba..dd66418dd8 100644 --- a/lib/ssl/src/ssl_tls1.erl +++ b/lib/ssl/src/ssl_tls1.erl @@ -60,15 +60,14 @@ finished(Role, MasterSecret, {MD5Hash, SHAHash}) -> SHA = hash_final(?SHA, SHAHash), prf(MasterSecret, finished_label(Role), [MD5, SHA], 12). --spec certificate_verify(key_algo(), {binary(), binary()}) -> binary(). +-spec certificate_verify(OID::tuple(), {binary(), binary()}) -> binary(). -certificate_verify(Algorithm, {MD5Hash, SHAHash}) when Algorithm == rsa; - Algorithm == dhe_rsa -> +certificate_verify(?'rsaEncryption', {MD5Hash, SHAHash}) -> MD5 = hash_final(?MD5, MD5Hash), SHA = hash_final(?SHA, SHAHash), <<MD5/binary, SHA/binary>>; -certificate_verify(dhe_dss, {_, SHAHash}) -> +certificate_verify(?'id-dsa', {_, SHAHash}) -> hash_final(?SHA, SHAHash). -spec setup_keys(binary(), binary(), binary(), integer(), diff --git a/lib/ssl/test/Makefile b/lib/ssl/test/Makefile index 9e4aecac45..c0a7f8d257 100644 --- a/lib/ssl/test/Makefile +++ b/lib/ssl/test/Makefile @@ -40,6 +40,7 @@ MODULES = \ ssl_packet_SUITE \ ssl_payload_SUITE \ ssl_to_openssl_SUITE \ + ssl_session_cache_SUITE \ ssl_test_MACHINE \ old_ssl_active_SUITE \ old_ssl_active_once_SUITE \ diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl index fade67f3ba..ea84b3c9d1 100644 --- a/lib/ssl/test/ssl_basic_SUITE.erl +++ b/lib/ssl/test/ssl_basic_SUITE.erl @@ -34,12 +34,6 @@ -define(EXPIRE, 10). -define(SLEEP, 500). --behaviour(ssl_session_cache_api). - -%% For the session cache tests --export([init/1, terminate/1, lookup/2, update/3, - delete/2, foldl/3, select_session/2]). - %% Test server callback functions %%-------------------------------------------------------------------- %% Function: init_per_suite(Config) -> Config @@ -89,13 +83,6 @@ end_per_suite(_Config) -> %% variable, but should NOT alter/remove any existing entries. %% Description: Initialization before each test case %%-------------------------------------------------------------------- -init_per_testcase(session_cache_process_list, Config) -> - init_customized_session_cache(list, Config); - -init_per_testcase(session_cache_process_mnesia, Config) -> - mnesia:start(), - init_customized_session_cache(mnesia, Config); - init_per_testcase(reuse_session_expired, Config0) -> Config = lists:keydelete(watchdog, 1, Config0), Dog = ssl_test_lib:timetrap(?EXPIRE * 1000 * 5), @@ -142,16 +129,6 @@ init_per_testcase(_TestCase, Config0) -> Dog = test_server:timetrap(?TIMEOUT), [{watchdog, Dog} | Config]. -init_customized_session_cache(Type, Config0) -> - Config = lists:keydelete(watchdog, 1, Config0), - Dog = test_server:timetrap(?TIMEOUT), - ssl:stop(), - application:load(ssl), - application:set_env(ssl, session_cb, ?MODULE), - application:set_env(ssl, session_cb_init_args, [Type]), - ssl:start(), - [{watchdog, Dog} | Config]. - %%-------------------------------------------------------------------- %% Function: end_per_testcase(TestCase, Config) -> _ %% Case - atom() @@ -160,16 +137,6 @@ init_customized_session_cache(Type, Config0) -> %% A list of key/value pairs, holding the test case configuration. %% Description: Cleanup after each test case %%-------------------------------------------------------------------- -end_per_testcase(session_cache_process_list, Config) -> - application:unset_env(ssl, session_cb), - end_per_testcase(default_action, Config); -end_per_testcase(session_cache_process_mnesia, Config) -> - application:unset_env(ssl, session_cb), - application:unset_env(ssl, session_cb_init_args), - mnesia:stop(), - ssl:stop(), - ssl:start(), - end_per_testcase(default_action, Config); end_per_testcase(reuse_session_expired, Config) -> application:unset_env(ssl, session_lifetime), end_per_testcase(default_action, Config); @@ -216,6 +183,8 @@ all(suite) -> ciphers_dsa_signed_certs_ssl3, ciphers_dsa_signed_certs_openssl_names, ciphers_dsa_signed_certs_openssl_names_ssl3, + anonymous_cipher_suites, + default_reject_anonymous, send_close, close_transport_accept, dh_params, server_verify_peer_passive, server_verify_peer_active, server_verify_peer_active_once, @@ -226,7 +195,6 @@ all(suite) -> server_verify_client_once_active, server_verify_client_once_active_once, client_verify_none_passive, client_verify_none_active, client_verify_none_active_once, - session_cache_process_list, session_cache_process_mnesia, reuse_session, reuse_session_expired, server_does_not_want_to_reuse_session, client_renegotiate, server_renegotiate, client_renegotiate_reused_session, @@ -1165,13 +1133,13 @@ ecertfile(Config) when is_list(Config) -> Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, {from, self()}, - {options, ServerBadOpts}]), + {options, ServerBadOpts}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client_error([{node, ClientNode}, - {port, Port}, {host, Hostname}, + {port, Port}, {host, Hostname}, {from, self()}, {options, ClientOpts}]), @@ -1522,6 +1490,14 @@ ciphers_dsa_signed_certs_openssl_names_ssl3(Config) when is_list(Config) -> Ciphers = ssl_test_lib:openssl_dsa_suites(), run_suites(Ciphers, Version, Config, dsa). +anonymous_cipher_suites(doc)-> + ["Test the anonymous ciphersuites"]; +anonymous_cipher_suites(suite) -> + []; +anonymous_cipher_suites(Config) when is_list(Config) -> + Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + Ciphers = ssl_test_lib:anonymous_suites(), + run_suites(Ciphers, Version, Config, anonymous). run_suites(Ciphers, Version, Config, Type) -> {ClientOpts, ServerOpts} = @@ -1531,8 +1507,12 @@ run_suites(Ciphers, Version, Config, Type) -> ?config(server_opts, Config)}; dsa -> {?config(client_opts, Config), - ?config(server_dsa_opts, Config)} - end, + ?config(server_dsa_opts, Config)}; + anonymous -> + %% No certs in opts! + {?config(client_opts, Config), + ?config(server_anon, Config)} + end, Result = lists:map(fun(Cipher) -> cipher(Cipher, Version, Config, ClientOpts, ServerOpts) end, @@ -1593,6 +1573,32 @@ cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) -> end. %%-------------------------------------------------------------------- +default_reject_anonymous(doc)-> + ["Test that by default anonymous cipher suites are rejected "]; +default_reject_anonymous(suite) -> + []; +default_reject_anonymous(Config) when is_list(Config) -> + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + + [Cipher | _] = ssl_test_lib:anonymous_suites(), + + Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, + {from, self()}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {options, + [{ciphers,[Cipher]} | + ClientOpts]}]), + + ssl_test_lib:check_result(Server, {error, "insufficient security"}, + Client, {error, "insufficient security"}). + +%%-------------------------------------------------------------------- reuse_session(doc) -> ["Test reuse of sessions (short handshake)"]; @@ -2952,7 +2958,7 @@ unknown_server_ca_accept_verify_peer(Config) when is_list(Config) -> %%-------------------------------------------------------------------- unknown_server_ca_accept_backwardscompatibilty(doc) -> - ["Test that the client succeds if the ca is unknown in verify_none mode"]; + ["Test that old style verify_funs will work"]; unknown_server_ca_accept_backwardscompatibilty(suite) -> []; unknown_server_ca_accept_backwardscompatibilty(Config) when is_list(Config) -> @@ -3047,6 +3053,17 @@ der_input_opts(Opts) -> %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- +erlang_ssl_receive(Socket, Data) -> + receive + {ssl, Socket, Data} -> + io:format("Received ~p~n",[Data]), + ok; + Other -> + test_server:fail({unexpected_message, Other}) + after ?SLEEP * 3 -> + test_server:fail({did_not_get, Data}) + end. + send_recv_result(Socket) -> ssl:send(Socket, "Hello world"), {ok,"Hello world"} = ssl:recv(Socket, 11), @@ -3086,162 +3103,3 @@ renegotiate_reuse_session(Socket, Data) -> %% Make sure session is registerd test_server:sleep(?SLEEP), renegotiate(Socket, Data). - -session_cache_process_list(doc) -> - ["Test reuse of sessions (short handshake)"]; - -session_cache_process_list(suite) -> - []; -session_cache_process_list(Config) when is_list(Config) -> - session_cache_process(list,Config). - -session_cache_process_mnesia(doc) -> - ["Test reuse of sessions (short handshake)"]; - -session_cache_process_mnesia(suite) -> - []; -session_cache_process_mnesia(Config) when is_list(Config) -> - session_cache_process(mnesia,Config). - -session_cache_process(_Type,Config) when is_list(Config) -> - reuse_session(Config). - -init([Type]) -> - ets:new(ssl_test, [named_table, public, set]), - ets:insert(ssl_test, {type, Type}), - case Type of - list -> - spawn(fun() -> session_loop([]) end); - mnesia -> - mnesia:start(), - {atomic,ok} = mnesia:create_table(sess_cache, []), - sess_cache - end. - -session_cb() -> - [{type, Type}] = ets:lookup(ssl_test, type), - Type. - -terminate(Cache) -> - case session_cb() of - list -> - Cache ! terminate; - mnesia -> - catch {atomic,ok} = - mnesia:delete_table(sess_cache) - end. - -lookup(Cache, Key) -> - case session_cb() of - list -> - Cache ! {self(), lookup, Key}, - receive {Cache, Res} -> Res end; - mnesia -> - case mnesia:transaction(fun() -> - mnesia:read(sess_cache, - Key, read) - end) of - {atomic, [{sess_cache, Key, Value}]} -> - Value; - _ -> - undefined - end - end. - -update(Cache, Key, Value) -> - case session_cb() of - list -> - Cache ! {update, Key, Value}; - mnesia -> - {atomic, ok} = - mnesia:transaction(fun() -> - mnesia:write(sess_cache, - {sess_cache, Key, Value}, write) - end) - end. - -delete(Cache, Key) -> - case session_cb() of - list -> - Cache ! {delete, Key}; - mnesia -> - {atomic, ok} = - mnesia:transaction(fun() -> - mnesia:delete(sess_cache, Key) - end) - end. - -foldl(Fun, Acc, Cache) -> - case session_cb() of - list -> - Cache ! {self(),foldl,Fun,Acc}, - receive {Cache, Res} -> Res end; - mnesia -> - Foldl = fun() -> - mnesia:foldl(Fun, Acc, sess_cache) - end, - {atomic, Res} = mnesia:transaction(Foldl), - Res - end. - -select_session(Cache, PartialKey) -> - case session_cb() of - list -> - Cache ! {self(),select_session, PartialKey}, - receive - {Cache, Res} -> - Res - end; - mnesia -> - Sel = fun() -> - mnesia:select(Cache, - [{{sess_cache,{PartialKey,'$1'}, '$2'}, - [],['$$']}]) - end, - {atomic, Res} = mnesia:transaction(Sel), - Res - end. - -session_loop(Sess) -> - receive - terminate -> - ok; - {Pid, lookup, Key} -> - case lists:keysearch(Key,1,Sess) of - {value, {Key,Value}} -> - Pid ! {self(), Value}; - _ -> - Pid ! {self(), undefined} - end, - session_loop(Sess); - {update, Key, Value} -> - NewSess = [{Key,Value}| lists:keydelete(Key,1,Sess)], - session_loop(NewSess); - {delete, Key} -> - session_loop(lists:keydelete(Key,1,Sess)); - {Pid,foldl,Fun,Acc} -> - Res = lists:foldl(Fun, Acc,Sess), - Pid ! {self(), Res}, - session_loop(Sess); - {Pid,select_session,PKey} -> - Sel = fun({{PKey0, Id},Session}, Acc) when PKey == PKey0 -> - [[Id, Session]|Acc]; - (_,Acc) -> - Acc - end, - Sessions = lists:foldl(Sel, [], Sess), - Pid ! {self(), Sessions}, - session_loop(Sess) - end. - - -erlang_ssl_receive(Socket, Data) -> - receive - {ssl, Socket, Data} -> - io:format("Received ~p~n",[Data]), - ok; - Other -> - test_server:fail({unexpected_message, Other}) - after ?SLEEP * 3 -> - test_server:fail({did_not_get, Data}) - end. diff --git a/lib/ssl/test/ssl_session_cache_SUITE.erl b/lib/ssl/test/ssl_session_cache_SUITE.erl new file mode 100644 index 0000000000..7a441e4599 --- /dev/null +++ b/lib/ssl/test/ssl_session_cache_SUITE.erl @@ -0,0 +1,305 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2010-2010. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/.2 +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%% + +-module(ssl_session_cache_SUITE). + +%% Note: This directive should only be used in test suites. +-compile(export_all). + +-include("test_server.hrl"). + +-define(SLEEP, 500). +-define(TIMEOUT, 60000). +-behaviour(ssl_session_cache_api). + +%% For the session cache tests +-export([init/1, terminate/1, lookup/2, update/3, + delete/2, foldl/3, select_session/2]). + +%% Test server callback functions +%%-------------------------------------------------------------------- +%% Function: init_per_suite(Config) -> Config +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Description: Initialization before the whole suite +%% +%% Note: This function is free to add any key/value pairs to the Config +%% variable, but should NOT alter/remove any existing entries. +%%-------------------------------------------------------------------- +init_per_suite(Config0) -> + Dog = ssl_test_lib:timetrap(?TIMEOUT *2), + crypto:start(), + application:start(public_key), + ssl:start(), + + %% make rsa certs using oppenssl + Result = + (catch make_certs:all(?config(data_dir, Config0), + ?config(priv_dir, Config0))), + test_server:format("Make certs ~p~n", [Result]), + + Config1 = ssl_test_lib:make_dsa_cert(Config0), + Config = ssl_test_lib:cert_options(Config1), + [{watchdog, Dog} | Config]. + +%%-------------------------------------------------------------------- +%% Function: end_per_suite(Config) -> _ +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Description: Cleanup after the whole suite +%%-------------------------------------------------------------------- +end_per_suite(_Config) -> + ssl:stop(), + crypto:stop(). + +%%-------------------------------------------------------------------- +%% Function: init_per_testcase(TestCase, Config) -> Config +%% Case - atom() +%% Name of the test case that is about to be run. +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% +%% Description: Initialization before each test case +%% +%% Note: This function is free to add any key/value pairs to the Config +%% variable, but should NOT alter/remove any existing entries. +%% Description: Initialization before each test case +%%-------------------------------------------------------------------- +init_per_testcase(session_cache_process_list, Config) -> + init_customized_session_cache(list, Config); + +init_per_testcase(session_cache_process_mnesia, Config) -> + mnesia:start(), + init_customized_session_cache(mnesia, Config); + +init_per_testcase(_TestCase, Config0) -> + Config = lists:keydelete(watchdog, 1, Config0), + Dog = test_server:timetrap(?TIMEOUT), + [{watchdog, Dog} | Config]. + +init_customized_session_cache(Type, Config0) -> + Config = lists:keydelete(watchdog, 1, Config0), + Dog = test_server:timetrap(?TIMEOUT), + ssl:stop(), + application:load(ssl), + application:set_env(ssl, session_cb, ?MODULE), + application:set_env(ssl, session_cb_init_args, [Type]), + ssl:start(), + [{watchdog, Dog} | Config]. + +%%-------------------------------------------------------------------- +%% Function: end_per_testcase(TestCase, Config) -> _ +%% Case - atom() +%% Name of the test case that is about to be run. +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Description: Cleanup after each test case +%%-------------------------------------------------------------------- +end_per_testcase(session_cache_process_list, Config) -> + application:unset_env(ssl, session_cb), + end_per_testcase(default_action, Config); +end_per_testcase(session_cache_process_mnesia, Config) -> + application:unset_env(ssl, session_cb), + application:unset_env(ssl, session_cb_init_args), + mnesia:stop(), + ssl:stop(), + ssl:start(), + end_per_testcase(default_action, Config); +end_per_testcase(_TestCase, Config) -> + Dog = ?config(watchdog, Config), + case Dog of + undefined -> + ok; + _ -> + test_server:timetrap_cancel(Dog) + end. + +%%-------------------------------------------------------------------- +%% Function: all(Clause) -> TestCases +%% Clause - atom() - suite | doc +%% TestCases - [Case] +%% Case - atom() +%% Name of a test case. +%% Description: Returns a list of all test cases in this test suite +%%-------------------------------------------------------------------- +all(doc) -> + ["Test session cach API"]; + +all(suite) -> + [ + session_cache_process_list, session_cache_process_mnesia + ]. + +session_cache_process_list(doc) -> + ["Test reuse of sessions (short handshake)"]; + +session_cache_process_list(suite) -> + []; +session_cache_process_list(Config) when is_list(Config) -> + session_cache_process(list,Config). +%%-------------------------------------------------------------------- +session_cache_process_mnesia(doc) -> + ["Test reuse of sessions (short handshake)"]; + +session_cache_process_mnesia(suite) -> + []; +session_cache_process_mnesia(Config) when is_list(Config) -> + session_cache_process(mnesia,Config). + + +%%-------------------------------------------------------------------- +%%% Session cache API callbacks +%%-------------------------------------------------------------------- + +init([Type]) -> + ets:new(ssl_test, [named_table, public, set]), + ets:insert(ssl_test, {type, Type}), + case Type of + list -> + spawn(fun() -> session_loop([]) end); + mnesia -> + mnesia:start(), + {atomic,ok} = mnesia:create_table(sess_cache, []), + sess_cache + end. + +session_cb() -> + [{type, Type}] = ets:lookup(ssl_test, type), + Type. + +terminate(Cache) -> + case session_cb() of + list -> + Cache ! terminate; + mnesia -> + catch {atomic,ok} = + mnesia:delete_table(sess_cache) + end. + +lookup(Cache, Key) -> + case session_cb() of + list -> + Cache ! {self(), lookup, Key}, + receive {Cache, Res} -> Res end; + mnesia -> + case mnesia:transaction(fun() -> + mnesia:read(sess_cache, + Key, read) + end) of + {atomic, [{sess_cache, Key, Value}]} -> + Value; + _ -> + undefined + end + end. + +update(Cache, Key, Value) -> + case session_cb() of + list -> + Cache ! {update, Key, Value}; + mnesia -> + {atomic, ok} = + mnesia:transaction(fun() -> + mnesia:write(sess_cache, + {sess_cache, Key, Value}, write) + end) + end. + +delete(Cache, Key) -> + case session_cb() of + list -> + Cache ! {delete, Key}; + mnesia -> + {atomic, ok} = + mnesia:transaction(fun() -> + mnesia:delete(sess_cache, Key) + end) + end. + +foldl(Fun, Acc, Cache) -> + case session_cb() of + list -> + Cache ! {self(),foldl,Fun,Acc}, + receive {Cache, Res} -> Res end; + mnesia -> + Foldl = fun() -> + mnesia:foldl(Fun, Acc, sess_cache) + end, + {atomic, Res} = mnesia:transaction(Foldl), + Res + end. + +select_session(Cache, PartialKey) -> + case session_cb() of + list -> + Cache ! {self(),select_session, PartialKey}, + receive + {Cache, Res} -> + Res + end; + mnesia -> + Sel = fun() -> + mnesia:select(Cache, + [{{sess_cache,{PartialKey,'$1'}, '$2'}, + [],['$$']}]) + end, + {atomic, Res} = mnesia:transaction(Sel), + Res + end. + +session_loop(Sess) -> + receive + terminate -> + ok; + {Pid, lookup, Key} -> + case lists:keysearch(Key,1,Sess) of + {value, {Key,Value}} -> + Pid ! {self(), Value}; + _ -> + Pid ! {self(), undefined} + end, + session_loop(Sess); + {update, Key, Value} -> + NewSess = [{Key,Value}| lists:keydelete(Key,1,Sess)], + session_loop(NewSess); + {delete, Key} -> + session_loop(lists:keydelete(Key,1,Sess)); + {Pid,foldl,Fun,Acc} -> + Res = lists:foldl(Fun, Acc,Sess), + Pid ! {self(), Res}, + session_loop(Sess); + {Pid,select_session,PKey} -> + Sel = fun({{PKey0, Id},Session}, Acc) when PKey == PKey0 -> + [[Id, Session]|Acc]; + (_,Acc) -> + Acc + end, + Sessions = lists:foldl(Sel, [], Sess), + Pid ! {self(), Sessions}, + session_loop(Sess) + end. + +%%-------------------------------------------------------------------- +%%% Internal functions +%%-------------------------------------------------------------------- + +session_cache_process(_Type,Config) when is_list(Config) -> + ssl_basic_SUITE:reuse_session(Config). diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl index ce164f7e4c..e1e8214ed6 100644 --- a/lib/ssl/test/ssl_test_lib.erl +++ b/lib/ssl/test/ssl_test_lib.erl @@ -300,6 +300,7 @@ cert_options(Config) -> {ssl_imp, new}]}, {server_opts, [{ssl_imp, new},{reuseaddr, true}, {certfile, ServerCertFile}, {keyfile, ServerKeyFile}]}, + {server_anon, [{ssl_imp, new},{reuseaddr, true}, {ciphers, anonymous_suites()}]}, {server_verification_opts, [{ssl_imp, new},{reuseaddr, true}, {cacertfile, ServerCaCertFile}, {certfile, ServerCertFile}, {keyfile, ServerKeyFile}]}, @@ -616,6 +617,13 @@ openssl_dsa_suites() -> end end, Ciphers). +anonymous_suites() -> + [{dh_anon, rc4_128, md5}, + {dh_anon, des_cbc, sha}, + {dh_anon, '3des_ede_cbc', sha}, + {dh_anon, aes_128_cbc, sha}, + {dh_anon, aes_256_cbc, sha}]. + pem_to_der(File) -> {ok, PemBin} = file:read_file(File), public_key:pem_decode(PemBin). @@ -633,7 +641,7 @@ cipher_result(Socket, Result) -> receive {ssl, Socket, "Hello\n"} -> ssl:send(Socket, " world\n"), - receive + receive {ssl, Socket, " world\n"} -> ok end; diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl index 7f512f2ab9..55a0100b1e 100644 --- a/lib/ssl/test/ssl_to_openssl_SUITE.erl +++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl @@ -1116,8 +1116,6 @@ run_suites(Ciphers, Version, Config, Type) -> test_server:fail(cipher_suite_failed_see_test_case_log) end. - - cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) -> process_flag(trap_exit, true), test_server:format("Testing CipherSuite ~p~n", [CipherSuite]), @@ -1128,8 +1126,8 @@ cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) -> KeyFile = proplists:get_value(keyfile, ServerOpts), Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ - " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ "", - + " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ "", + test_server:format("openssl cmd: ~p~n", [Cmd]), OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), @@ -1140,11 +1138,11 @@ cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) -> Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, cipher_result, [ConnectionInfo]}}, - {options, - [{ciphers,[CipherSuite]} | - ClientOpts]}]), + {from, self()}, + {mfa, {ssl_test_lib, cipher_result, [ConnectionInfo]}}, + {options, + [{ciphers,[CipherSuite]} | + ClientOpts]}]), port_command(OpenSslPort, "Hello\n"), diff --git a/lib/stdlib/doc/src/ets.xml b/lib/stdlib/doc/src/ets.xml index dd4a289c61..702e1b928e 100644 --- a/lib/stdlib/doc/src/ets.xml +++ b/lib/stdlib/doc/src/ets.xml @@ -56,8 +56,8 @@ Even if there are no references to a table from any process, it will not automatically be destroyed unless the owner process terminates. It can be destroyed explicitly by using - <c>delete/1</c>.</p> - <p>Since R13B01, table ownership can be transferred at process termination + <c>delete/1</c>. The default owner is the process that created the + table. Table ownership can be transferred at process termination by using the <seealso marker="#heir">heir</seealso> option or explicitly by calling <seealso marker="#give_away/3">give_away/3</seealso>.</p> <p>Some implementation details:</p> @@ -82,11 +82,15 @@ <c>float()</c> that extends to the same value, hence the key <c>1</c> and the key <c>1.0</c> are regarded as equal in an <c>ordered_set</c> table.</p> - <p>In general, the functions below will exit with reason - <c>badarg</c> if any argument is of the wrong format, or if the - table identifier is invalid.</p> </description> - + <section> + <title>Failure</title> + <p>In general, the functions below will exit with reason + <c>badarg</c> if any argument is of the wrong format, if the + table identifier is invalid or if the operation is denied due to + table access rights (<seealso marker="#protected">protected</seealso> + or <seealso marker="#private">private</seealso>).</p> + </section> <section><marker id="concurrency"></marker> <title>Concurrency</title> <p>This module provides some limited support for concurrent access. @@ -947,7 +951,7 @@ ets:select(Table,MatchSpec),</code> <type> <v>Name = atom()</v> <v>Options = [Option]</v> - <v> Option = Type | Access | named_table | {keypos,Pos} | {heir,pid(),HeirData} | {heir,none} | {write_concurrency,bool()}</v> + <v> Option = Type | Access | named_table | {keypos,Pos} | {heir,pid(),HeirData} | {heir,none} | {write_concurrency,bool()} | {read_concurrency,bool()}</v> <v> Type = set | ordered_set | bag | duplicate_bag</v> <v> Access = public | protected | private</v> <v> Pos = int()</v> @@ -963,7 +967,7 @@ ets:select(Table,MatchSpec),</code> table is named or not. If one or more options are left out, the default values are used. This means that not specifying any options (<c>[]</c>) is the same as specifying - <c>[set,protected,{keypos,1},{heir,none},{write_concurrency,false}]</c>.</p> + <c>[set,protected,{keypos,1},{heir,none},{write_concurrency,false},{read_concurrency,false}]</c>.</p> <list type="bulleted"> <item> <p><c>set</c> @@ -1002,12 +1006,14 @@ ets:select(Table,MatchSpec),</code> Any process may read or write to the table.</p> </item> <item> + <marker id="protected"></marker> <p><c>protected</c> The owner process can read and write to the table. Other processes can only read the table. This is the default setting for the access rights.</p> </item> <item> + <marker id="private"></marker> <p><c>private</c> Only the owner process can read or write to the table.</p> </item> diff --git a/lib/stdlib/src/dets.erl b/lib/stdlib/src/dets.erl index 4584b8184f..6c91f1efb7 100644 --- a/lib/stdlib/src/dets.erl +++ b/lib/stdlib/src/dets.erl @@ -147,6 +147,7 @@ bin, % small chunk not consumed, or 'eof' at end-of-file alloc, % the part of the file not yet scanned, mostly a binary tab, + proc, % the pid of the Dets process match_program % true | compiled_match_spec() | undefined }). @@ -208,8 +209,6 @@ all() -> bchunk(Tab, start) -> badarg(treq(Tab, {bchunk_init, Tab}), [Tab, start]); -bchunk(Tab, #dets_cont{bin = eof, tab = Tab}) -> - '$end_of_table'; bchunk(Tab, #dets_cont{what = bchunk, tab = Tab} = State) -> badarg(treq(Tab, {bchunk, State}), [Tab, State]); bchunk(Tab, Term) -> @@ -722,11 +721,14 @@ init_chunk_match(Tab, Pat, What, N) when is_integer(N), N >= 0; N =:= default -> case compile_match_spec(What, Pat) of {Spec, MP} -> - case req(dets_server:get_pid(Tab), {match, MP, Spec, N}) of + Proc = dets_server:get_pid(Tab), + case req(Proc, {match, MP, Spec, N}) of {done, L} -> - {L, #dets_cont{tab = Tab, what = What, bin = eof}}; + {L, #dets_cont{tab = Tab, proc = Proc, what = What, + bin = eof}}; {cont, State} -> - chunk_match(State#dets_cont{what = What, tab = Tab}); + chunk_match(State#dets_cont{what = What, tab = Tab, + proc = Proc}); Error -> Error end; @@ -736,34 +738,28 @@ init_chunk_match(Tab, Pat, What, N) when is_integer(N), N >= 0; init_chunk_match(_Tab, _Pat, _What, _) -> badarg. -chunk_match(State) -> - case catch dets_server:get_pid(State#dets_cont.tab) of - {'EXIT', _Reason} -> - badarg; - _Proc when State#dets_cont.bin =:= eof -> - '$end_of_table'; - Proc -> - case req(Proc, {match_init, State}) of - {cont, {Bins, NewState}} -> - MP = NewState#dets_cont.match_program, - case catch do_foldl_bins(Bins, MP) of - {'EXIT', _} -> - case ets:is_compiled_ms(MP) of - true -> - Bad = dets_utils:bad_object(chunk_match, - Bins), - req(Proc, {corrupt, Bad}); - false -> - badarg - end; - [] -> - chunk_match(NewState); - Terms -> - {Terms, NewState} - end; - Error -> - Error - end +chunk_match(#dets_cont{proc = Proc}=State) -> + case req(Proc, {match_init, State}) of + '$end_of_table'=Reply -> + Reply; + {cont, {Bins, NewState}} -> + MP = NewState#dets_cont.match_program, + case catch do_foldl_bins(Bins, MP) of + {'EXIT', _} -> + case ets:is_compiled_ms(MP) of + true -> + Bad = dets_utils:bad_object(chunk_match, Bins), + req(Proc, {corrupt, Bad}); + false -> + badarg + end; + [] -> + chunk_match(NewState); + Terms -> + {Terms, NewState} + end; + Error -> + Error end. do_foldl_bins(Bins, true) -> @@ -1094,7 +1090,9 @@ do_apply_op(Op, From, Head, N) -> {N2, H2} when is_record(H2, head), is_integer(N2) -> open_file_loop(H2, N2); H2 when is_record(H2, head) -> - open_file_loop(H2, N) + open_file_loop(H2, N); + {{more,From1,Op1,N1}, NewHead} -> + do_apply_op(Op1, From1, NewHead, N1) catch exit:normal -> exit(normal); @@ -1363,37 +1361,35 @@ start_auto_save_timer(Head) -> %% lookup requests in parallel. Evalute delete_object, delete and %% insert as well. stream_op(Op, Pid, Pids, Head, N) -> - stream_op(Head, Pids, [], N, Pid, Op, Head#head.fixed). + #head{fixed = Fxd, update_mode = M} = Head, + stream_op(Head, Pids, [], N, Pid, Op, Fxd, M). -stream_loop(Head, Pids, C, N, false = Fxd) -> +stream_loop(Head, Pids, C, N, false = Fxd, M) -> receive ?DETS_CALL(From, Message) -> - stream_op(Head, Pids, C, N, From, Message, Fxd) + stream_op(Head, Pids, C, N, From, Message, Fxd, M) after 0 -> stream_end(Head, Pids, C, N, no_more) end; -stream_loop(Head, Pids, C, N, _Fxd) -> +stream_loop(Head, Pids, C, N, _Fxd, _M) -> stream_end(Head, Pids, C, N, no_more). -stream_op(Head, Pids, C, N, Pid, {lookup_keys,Keys}, Fxd) -> +stream_op(Head, Pids, C, N, Pid, {lookup_keys,Keys}, Fxd, M) -> NC = [{{lookup,Pid},Keys} | C], - stream_loop(Head, Pids, NC, N, Fxd); -stream_op(Head, Pids, C, N, Pid, {insert, _Objects} = Op, Fxd) -> - NC = [Op | C], - stream_loop(Head, [Pid | Pids], NC, N, Fxd); -stream_op(Head, Pids, C, N, Pid, {insert_new, _Objects} = Op, Fxd) -> + stream_loop(Head, Pids, NC, N, Fxd, M); +stream_op(Head, Pids, C, N, Pid, {insert, _Objects} = Op, Fxd, dirty = M) -> NC = [Op | C], - stream_loop(Head, [Pid | Pids], NC, N, Fxd); -stream_op(Head, Pids, C, N, Pid, {delete_key, _Keys} = Op, Fxd) -> + stream_loop(Head, [Pid | Pids], NC, N, Fxd, M); +stream_op(Head, Pids, C, N, Pid, {delete_key, _Keys} = Op, Fxd, dirty = M) -> NC = [Op | C], - stream_loop(Head, [Pid | Pids], NC, N, Fxd); -stream_op(Head, Pids, C, N, Pid, {delete_object, _Objects} = Op, Fxd) -> + stream_loop(Head, [Pid | Pids], NC, N, Fxd, M); +stream_op(Head, Pids, C, N, Pid, {delete_object, _Os} = Op, Fxd, dirty = M) -> NC = [Op | C], - stream_loop(Head, [Pid | Pids], NC, N, Fxd); -stream_op(Head, Pids, C, N, Pid, {member, Key}, Fxd) -> + stream_loop(Head, [Pid | Pids], NC, N, Fxd, M); +stream_op(Head, Pids, C, N, Pid, {member, Key}, Fxd, M) -> NC = [{{lookup,[Pid]},[Key]} | C], - stream_loop(Head, Pids, NC, N, Fxd); -stream_op(Head, Pids, C, N, Pid, Op, _Fxd) -> + stream_loop(Head, Pids, NC, N, Fxd, M); +stream_op(Head, Pids, C, N, Pid, Op, _Fxd, _M) -> stream_end(Head, Pids, C, N, {Pid,Op}). stream_end(Head, Pids0, C, N, Next) -> @@ -1438,7 +1434,7 @@ stream_end2([], Ps, no_more, N, C, Head, _Reply) -> penalty(Head, Ps, C), {N, Head}; stream_end2([], _Ps, {From, Op}, N, _C, Head, _Reply) -> - apply_op(Op, From, Head, N). + {{more,From,Op,N},Head}. penalty(H, _Ps, _C) when H#head.fixed =:= false -> ok; @@ -1578,13 +1574,18 @@ do_bchunk_init(Head, Tab) -> L = dets_utils:all_allocated(H2), C0 = #dets_cont{no_objs = default, bin = <<>>, alloc = L}, BinParms = term_to_binary(Parms), - {H2, {C0#dets_cont{tab = Tab, what = bchunk}, [BinParms]}} + {H2, {C0#dets_cont{tab = Tab, proc = self(),what = bchunk}, + [BinParms]}} end; {NewHead, _} = HeadError when is_record(NewHead, head) -> HeadError end. %% -> {NewHead, {cont(), [binary()]}} | {NewHead, Error} +do_bchunk(Head, #dets_cont{proc = Proc}) when Proc =/= self() -> + {Head, badarg}; +do_bchunk(Head, #dets_cont{bin = eof}) -> + {Head, '$end_of_table'}; do_bchunk(Head, State) -> case dets_v9:read_bchunks(Head, State#dets_cont.alloc) of {error, Reason} -> @@ -1954,6 +1955,8 @@ flookup_keys(Head, Keys) -> end. %% -> {NewHead, Result} +fmatch_init(Head, #dets_cont{bin = eof}) -> + {Head, '$end_of_table'}; fmatch_init(Head, C) -> case scan(Head, C) of {scan_error, Reason} -> diff --git a/lib/stdlib/src/dets_v8.erl b/lib/stdlib/src/dets_v8.erl index 1f9f84cd27..af36958c1c 100644 --- a/lib/stdlib/src/dets_v8.erl +++ b/lib/stdlib/src/dets_v8.erl @@ -1074,6 +1074,8 @@ wl([], _Type, Del, Lookup, I, Objs) -> [{Del, Lookup, Objs} | I]. %% -> {NewHead, ok} | {NewHead, Error} +may_grow(Head, 0, once) -> + {Head, ok}; may_grow(Head, _N, _How) when Head#head.fixed =/= false -> {Head, ok}; may_grow(#head{access = read}=Head, _N, _How) -> diff --git a/lib/stdlib/src/dets_v9.erl b/lib/stdlib/src/dets_v9.erl index 53238e962f..132af01f79 100644 --- a/lib/stdlib/src/dets_v9.erl +++ b/lib/stdlib/src/dets_v9.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2009. All Rights Reserved. +%% Copyright Ericsson AB 2001-2010. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -1908,6 +1908,9 @@ write_cache(Head) -> end. %% -> {NewHead, ok} | {NewHead, Error} +may_grow(Head, 0, once) -> + %% Do not re-hash if there is a chance that the file is not dirty. + {Head, ok}; may_grow(Head, _N, _How) when Head#head.fixed =/= false -> {Head, ok}; may_grow(#head{access = read}=Head, _N, _How) -> diff --git a/lib/stdlib/src/epp.erl b/lib/stdlib/src/epp.erl index 81b2431f40..e5ccaddbb4 100644 --- a/lib/stdlib/src/epp.erl +++ b/lib/stdlib/src/epp.erl @@ -33,7 +33,9 @@ %% Epp state record. -record(epp, {file, %Current file location, %Current location + delta, %Offset from Location (-file) name="", %Current file name + name2="", %-"-, modified by -file istk=[], %Ifdef stack sstk=[], %State stack path=[], %Include-path @@ -234,8 +236,8 @@ init_server(Pid, Name, File, AtLocation, Path, Pdm, Pre) -> case user_predef(Pdm, Ms0) of {ok,Ms1} -> epp_reply(Pid, {ok,self()}), - St = #epp{file=File, location=AtLocation, name=Name, - path=Path, macs=Ms1, pre_opened = Pre}, + St = #epp{file=File, location=AtLocation, delta=0, name=Name, + name2=Name, path=Path, macs=Ms1, pre_opened = Pre}, From = wait_request(St), enter_file_reply(From, Name, AtLocation, AtLocation), wait_req_scan(St); @@ -358,8 +360,8 @@ enter_file2(NewF, Pname, From, St, AtLocation, ExtraPath) -> enter_file_reply(From, Pname, Loc, AtLocation), Ms = dict:store({atom,'FILE'}, {none,[{string,Loc,Pname}]}, St#epp.macs), Path = St#epp.path ++ ExtraPath, - #epp{location=Loc,file=NewF, - name=Pname,sstk=[St|St#epp.sstk],path=Path,macs=Ms}. + #epp{file=NewF,location=Loc,name=Pname,delta=0, + sstk=[St|St#epp.sstk],path=Path,macs=Ms}. enter_file_reply(From, Name, Location, AtLocation) -> Attr = loc_attr(AtLocation), @@ -391,14 +393,23 @@ leave_file(From, St) -> case St#epp.sstk of [OldSt|Sts] -> close_file(St), - enter_file_reply(From, OldSt#epp.name, - OldSt#epp.location, OldSt#epp.location), + #epp{location=OldLoc, delta=Delta, name=OldName, + name2=OldName2} = OldSt, + CurrLoc = add_line(OldLoc, Delta), Ms = dict:store({atom,'FILE'}, - {none, - [{string,OldSt#epp.location, - OldSt#epp.name}]}, + {none,[{string,CurrLoc,OldName2}]}, St#epp.macs), - wait_req_scan(OldSt#epp{sstk=Sts,macs=Ms}); + NextSt = OldSt#epp{sstk=Sts,macs=Ms}, + enter_file_reply(From, OldName, CurrLoc, CurrLoc), + case OldName2 =:= OldName of + true -> + From; + false -> + NFrom = wait_request(NextSt), + enter_file_reply(NFrom, OldName2, OldLoc, + neg_line(CurrLoc)) + end, + wait_req_scan(NextSt); [] -> epp_reply(From, {eof,St#epp.location}), wait_req_scan(St) @@ -768,7 +779,8 @@ scan_file([{'(',_Llp},{string,_Ls,Name},{',',_Lc},{integer,_Li,Ln},{')',_Lrp}, Ms = dict:store({atom,'FILE'}, {none,[{string,1,Name}]}, St#epp.macs), Locf = loc(Tf), NewLoc = new_location(Ln, St#epp.location, Locf), - wait_req_scan(St#epp{name=Name,location=NewLoc,macs=Ms}); + Delta = abs(get_line(element(2, Tf)))-Ln + St#epp.delta, + wait_req_scan(St#epp{name2=Name,location=NewLoc,delta=Delta,macs=Ms}); scan_file(_Toks, Tf, From, St) -> epp_reply(From, {error,{loc(Tf),epp,{bad,file}}}), wait_req_scan(St). @@ -1132,6 +1144,9 @@ neg_line(L) -> abs_line(L) -> erl_scan:set_attribute(line, L, fun(Line) -> abs(Line) end). +add_line(L, Offset) -> + erl_scan:set_attribute(line, L, fun(Line) -> Line+Offset end). + start_loc(Line) when is_integer(Line) -> 1; start_loc({_Line, _Column}) -> @@ -1191,10 +1206,10 @@ interpret_file_attr([{attribute,Loc,file,{File,Line}}=Form | Forms], %% -include or -include_lib % true = L =:= Line, case Fs of - [_, Delta1, File | Fs1] -> % end of included file - [Form | interpret_file_attr(Forms, Delta1, [File | Fs1])]; + [_, File | Fs1] -> % end of included file + [Form | interpret_file_attr(Forms, 0, [File | Fs1])]; _ -> % start of included file - [Form | interpret_file_attr(Forms, 0, [File, Delta | Fs])] + [Form | interpret_file_attr(Forms, 0, [File | Fs])] end end; interpret_file_attr([Form0 | Forms], Delta, Fs) -> diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl index 077621ac91..0c2d3db8ec 100644 --- a/lib/stdlib/src/erl_lint.erl +++ b/lib/stdlib/src/erl_lint.erl @@ -311,6 +311,8 @@ format_error({ill_defined_behaviour_callbacks,Behaviour}) -> %% --- types and specs --- format_error({singleton_typevar, Name}) -> io_lib:format("type variable ~w is only used once (is unbound)", [Name]); +format_error({bad_export_type, _ETs}) -> + io_lib:format("bad export_type declaration", []); format_error({duplicated_export_type, {T, A}}) -> io_lib:format("type ~w/~w already exported", [T, A]); format_error({undefined_type, {TypeName, Arity}}) -> @@ -1128,8 +1130,7 @@ export(Line, Es, #lint{exports = Es0, called = Called} = St0) -> export_type(Line, ETs, #lint{usage = Usage, exp_types = ETs0} = St0) -> UTs0 = Usage#usage.used_types, - {ETs1,UTs1,St1} = - foldl(fun (TA, {E,U,St2}) -> + try foldl(fun ({T,A}=TA, {E,U,St2}) when is_atom(T), is_integer(A) -> St = case gb_sets:is_element(TA, E) of true -> Warn = {duplicated_export_type,TA}, @@ -1139,8 +1140,13 @@ export_type(Line, ETs, #lint{usage = Usage, exp_types = ETs0} = St0) -> end, {gb_sets:add_element(TA, E), dict:store(TA, Line, U), St} end, - {ETs0,UTs0,St0}, ETs), - St1#lint{usage = Usage#usage{used_types = UTs1}, exp_types = ETs1}. + {ETs0,UTs0,St0}, ETs) of + {ETs1,UTs1,St1} -> + St1#lint{usage = Usage#usage{used_types = UTs1}, exp_types = ETs1} + catch + error:_ -> + add_error(Line, {bad_export_type, ETs}, St0) + end. -type import() :: {module(), [fa()]} | module(). -spec import(line(), import(), lint_state()) -> lint_state(). diff --git a/lib/stdlib/src/lists.erl b/lib/stdlib/src/lists.erl index 431e5b114e..c669c1f7c1 100644 --- a/lib/stdlib/src/lists.erl +++ b/lib/stdlib/src/lists.erl @@ -358,7 +358,7 @@ merge(L) -> %% merge3(X, Y, Z) -> L %% merges three sorted lists X, Y and Z --spec merge3([_], [_], [_]) -> [_]. +-spec merge3([X], [Y], [Z]) -> [(X | Y | Z)]. merge3(L1, [], L3) -> merge(L1, L3); @@ -370,7 +370,7 @@ merge3(L1, [H2 | T2], [H3 | T3]) -> %% rmerge3(X, Y, Z) -> L %% merges three reversed sorted lists X, Y and Z --spec rmerge3([_], [_], [_]) -> [_]. +-spec rmerge3([X], [Y], [Z]) -> [(X | Y | Z)]. rmerge3(L1, [], L3) -> rmerge(L1, L3); @@ -382,7 +382,7 @@ rmerge3(L1, [H2 | T2], [H3 | T3]) -> %% merge(X, Y) -> L %% merges two sorted lists X and Y --spec merge([_], [_]) -> [_]. +-spec merge([X], [Y]) -> [(X | Y)]. merge(T1, []) -> T1; @@ -394,7 +394,7 @@ merge(T1, [H2 | T2]) -> %% reverse(rmerge(reverse(A),reverse(B))) is equal to merge(I,A,B). --spec rmerge([_], [_]) -> [_]. +-spec rmerge([X], [Y]) -> [(X | Y)]. rmerge(T1, []) -> T1; @@ -420,12 +420,12 @@ thing_to_list(X) when is_list(X) -> X. %Assumed to be a string %% flatten(List, Tail) %% Flatten a list, adding optional tail. --spec flatten([_]) -> [_]. +-spec flatten([term()]) -> [term()]. flatten(List) when is_list(List) -> do_flatten(List, []). --spec flatten([_], [_]) -> [_]. +-spec flatten([term()], [term()]) -> [term()]. flatten(List, Tail) when is_list(List), is_list(Tail) -> do_flatten(List, Tail). @@ -440,7 +440,7 @@ do_flatten([], Tail) -> %% flatlength(List) %% Calculate the length of a list of lists. --spec flatlength([_]) -> non_neg_integer(). +-spec flatlength([term()]) -> non_neg_integer(). flatlength(List) -> flatlength(List, 0). @@ -481,7 +481,7 @@ flatlength([], L) -> L. % keysearch3(Key, N, T); %keysearch3(Key, N, []) -> false. --spec keydelete(_, pos_integer(), [T]) -> [T]. +-spec keydelete(term(), pos_integer(), [T]) -> [T] when T :: tuple(). keydelete(K, N, L) when is_integer(N), N > 0 -> keydelete3(K, N, L). @@ -491,7 +491,7 @@ keydelete3(Key, N, [H|T]) -> [H|keydelete3(Key, N, T)]; keydelete3(_, _, []) -> []. --spec keyreplace(_, pos_integer(), [_], tuple()) -> [_]. +-spec keyreplace(term(), pos_integer(), [tuple()], tuple()) -> [tuple()]. keyreplace(K, N, L, New) when is_integer(N), N > 0, is_tuple(New) -> keyreplace3(K, N, L, New). @@ -502,7 +502,8 @@ keyreplace3(Key, Pos, [H|T], New) -> [H|keyreplace3(Key, Pos, T, New)]; keyreplace3(_, _, [], _) -> []. --spec keytake(_, pos_integer(), [_]) -> {'value', tuple(), [_]} | 'false'. +-spec keytake(term(), pos_integer(), [tuple()]) -> + {'value', tuple(), [tuple()]} | 'false'. keytake(Key, N, L) when is_integer(N), N > 0 -> keytake(Key, N, L, []). @@ -513,7 +514,8 @@ keytake(Key, N, [H|T], L) -> keytake(Key, N, T, [H|L]); keytake(_K, _N, [], _L) -> false. --spec keystore(_, pos_integer(), [_], tuple()) -> [_]. +-spec keystore(term(), pos_integer(), [tuple()], tuple()) -> [tuple(),...]. + keystore(K, N, L, New) when is_integer(N), N > 0, is_tuple(New) -> keystore2(K, N, L, New). @@ -524,7 +526,7 @@ keystore2(Key, N, [H|T], New) -> keystore2(_Key, _N, [], New) -> [New]. --spec keysort(pos_integer(), [T]) -> [T] when is_subtype(T, tuple()). +-spec keysort(pos_integer(), [T]) -> [T] when T :: tuple(). keysort(I, L) when is_integer(I), I > 0 -> case L of @@ -582,7 +584,7 @@ keysort_1(_I, X, _EX, [], R) -> lists:reverse(R, [X]). -spec keymerge(pos_integer(), [X], [Y]) -> - [R] when is_subtype(X, tuple()), is_subtype(Y, tuple()), is_subtype(R, tuple()). + [R] when X :: tuple(), Y :: tuple(), R :: tuple(). keymerge(Index, T1, L2) when is_integer(Index), Index > 0 -> case L2 of @@ -597,7 +599,7 @@ keymerge(Index, T1, L2) when is_integer(Index), Index > 0 -> %% reverse(rkeymerge(I,reverse(A),reverse(B))) is equal to keymerge(I,A,B). -spec rkeymerge(pos_integer(), [X], [Y]) -> - [R] when is_subtype(X, tuple()), is_subtype(Y, tuple()), is_subtype(R, tuple()). + [R] when X :: tuple(), Y :: tuple(), R :: tuple(). rkeymerge(Index, T1, L2) when is_integer(Index), Index > 0 -> case L2 of @@ -609,7 +611,7 @@ rkeymerge(Index, T1, L2) when is_integer(Index), Index > 0 -> lists:reverse(M, []) end. --spec ukeysort(pos_integer(), [T]) -> [T] when is_subtype(T, tuple()). +-spec ukeysort(pos_integer(), [T]) -> [T] when T :: tuple(). ukeysort(I, L) when is_integer(I), I > 0 -> case L of @@ -675,7 +677,7 @@ ukeysort_1(_I, X, _EX, []) -> [X]. -spec ukeymerge(pos_integer(), [X], [Y]) -> - [(X | Y)] when is_subtype(X, tuple()), is_subtype(Y, tuple()). + [(X | Y)] when X :: tuple(), Y :: tuple(). ukeymerge(Index, L1, T2) when is_integer(Index), Index > 0 -> case L1 of @@ -690,7 +692,7 @@ ukeymerge(Index, L1, T2) when is_integer(Index), Index > 0 -> %% reverse(rukeymerge(I,reverse(A),reverse(B))) is equal to ukeymerge(I,A,B). -spec rukeymerge(pos_integer(), [X], [Y]) -> - [(X | Y)] when is_subtype(X, tuple()), is_subtype(Y, tuple()). + [(X | Y)] when X :: tuple(), Y :: tuple(). rukeymerge(Index, T1, L2) when is_integer(Index), Index > 0 -> case L2 of @@ -702,7 +704,7 @@ rukeymerge(Index, T1, L2) when is_integer(Index), Index > 0 -> lists:reverse(M, []) end. --spec keymap(fun((_) -> _), pos_integer(), [tuple()]) -> [tuple()]. +-spec keymap(fun((term()) -> term()), pos_integer(), [tuple()]) -> [tuple()]. keymap(Fun, Index, [Tup|Tail]) -> [setelement(Index, Tup, Fun(element(Index, Tup)))|keymap(Fun, Index, Tail)]; @@ -725,7 +727,7 @@ sort(Fun, [X, Y | T]) -> fsplit_2(Y, X, Fun, T, [], []) end. --spec merge(fun((X, Y) -> boolean()), [X], [Y]) -> [_]. +-spec merge(fun((X, Y) -> boolean()), [X], [Y]) -> [(X | Y)]. merge(Fun, T1, [H2 | T2]) when is_function(Fun, 2) -> lists:reverse(fmerge2_1(T1, H2, Fun, T2, []), []); @@ -734,7 +736,7 @@ merge(Fun, T1, []) when is_function(Fun, 2) -> %% reverse(rmerge(F,reverse(A),reverse(B))) is equal to merge(F,A,B). --spec rmerge(fun((X, Y) -> boolean()), [X], [Y]) -> [_]. +-spec rmerge(fun((X, Y) -> boolean()), [X], [Y]) -> [(X | Y)]. rmerge(Fun, T1, [H2 | T2]) when is_function(Fun, 2) -> lists:reverse(rfmerge2_1(T1, H2, Fun, T2, []), []); @@ -768,7 +770,7 @@ usort_1(Fun, X, [Y | L]) -> ufsplit_2(Y, L, Fun, [X]) end. --spec umerge(fun((X, Y) -> boolean()), [X], [Y]) -> [_]. +-spec umerge(fun((X, Y) -> boolean()), [X], [Y]) -> [(X | Y)]. umerge(Fun, [], T2) when is_function(Fun, 2) -> T2; @@ -777,7 +779,7 @@ umerge(Fun, [H1 | T1], T2) when is_function(Fun, 2) -> %% reverse(rumerge(F,reverse(A),reverse(B))) is equal to umerge(F,A,B). --spec rumerge(fun((X, Y) -> boolean()), [X], [Y]) -> [_]. +-spec rumerge(fun((X, Y) -> boolean()), [X], [Y]) -> [(X | Y)]. rumerge(Fun, T1, []) when is_function(Fun, 2) -> T1; @@ -851,7 +853,7 @@ umerge(L) -> %% merges three sorted lists X, Y and Z without duplicates, %% removes duplicates --spec umerge3([_], [_], [_]) -> [_]. +-spec umerge3([X], [Y], [Z]) -> [(X | Y | Z)]. umerge3(L1, [], L3) -> umerge(L1, L3); @@ -864,7 +866,7 @@ umerge3(L1, [H2 | T2], [H3 | T3]) -> %% merges three reversed sorted lists X, Y and Z without duplicates, %% removes duplicates --spec rumerge3([_], [_], [_]) -> [_]. +-spec rumerge3([X], [Y], [Z]) -> [(X | Y | Z)]. rumerge3(L1, [], L3) -> rumerge(L1, L3); @@ -876,7 +878,7 @@ rumerge3(L1, [H2 | T2], [H3 | T3]) -> %% umerge(X, Y) -> L %% merges two sorted lists X and Y without duplicates, removes duplicates --spec umerge([_], [_]) -> [_]. +-spec umerge([X], [Y]) -> [(X | Y)]. umerge([], T2) -> T2; @@ -889,7 +891,7 @@ umerge([H1 | T1], T2) -> %% reverse(rumerge(reverse(A),reverse(B))) is equal to umerge(I,A,B). --spec rumerge([_], [_]) -> [_]. +-spec rumerge([X], [Y]) -> [(X | Y)]. rumerge(T1, []) -> T1; @@ -952,13 +954,13 @@ flatmap(F, [Hd|Tail]) -> F(Hd) ++ flatmap(F, Tail); flatmap(F, []) when is_function(F, 1) -> []. --spec foldl(fun((T, _) -> _), _, [T]) -> _. +-spec foldl(fun((T, term()) -> term()), term(), [T]) -> term(). foldl(F, Accu, [Hd|Tail]) -> foldl(F, F(Hd, Accu), Tail); foldl(F, Accu, []) when is_function(F, 2) -> Accu. --spec foldr(fun((T, _) -> _), _, [T]) -> _. +-spec foldr(fun((T, term()) -> term()), term(), [T]) -> term(). foldr(F, Accu, [Hd|Tail]) -> F(Hd, foldr(F, Accu, Tail)); @@ -998,14 +1000,14 @@ zf(F, [Hd|Tail]) -> end; zf(F, []) when is_function(F, 1) -> []. --spec foreach(F :: fun((T) -> _), List :: [T]) -> 'ok'. +-spec foreach(F :: fun((T) -> term()), List :: [T]) -> 'ok'. foreach(F, [Hd|Tail]) -> F(Hd), foreach(F, Tail); foreach(F, []) when is_function(F, 1) -> ok. --spec mapfoldl(fun((T, _) -> {_, _}), _, [T]) -> {[_], _}. +-spec mapfoldl(fun((A, term()) -> {B, term()}), term(), [A]) -> {[B], term()}. mapfoldl(F, Accu0, [Hd|Tail]) -> {R,Accu1} = F(Hd, Accu0), @@ -1013,7 +1015,7 @@ mapfoldl(F, Accu0, [Hd|Tail]) -> {[R|Rs],Accu2}; mapfoldl(F, Accu, []) when is_function(F, 2) -> {[],Accu}. --spec mapfoldr(fun((T, _) -> {_, _}), _, [T]) -> {[_], _}. +-spec mapfoldr(fun((A, term()) -> {B, term()}), term(), [A]) -> {[B], term()}. mapfoldr(F, Accu0, [Hd|Tail]) -> {Rs,Accu1} = mapfoldr(F, Accu0, Tail), diff --git a/lib/stdlib/src/ordsets.erl b/lib/stdlib/src/ordsets.erl index e992b66714..4c72e351d0 100644 --- a/lib/stdlib/src/ordsets.erl +++ b/lib/stdlib/src/ordsets.erl @@ -26,12 +26,14 @@ -export([subtract/2,is_subset/2]). -export([fold/3,filter/2]). +-export_type([ordset/1]). + -type ordset(T) :: [T]. %% new() -> Set. %% Return a new empty ordered set. --spec new() -> ordset(term()). +-spec new() -> []. new() -> []. @@ -84,7 +86,7 @@ is_element(_, []) -> false. %% add_element(Element, OrdSet) -> OrdSet. %% Return OrdSet with Element inserted in it. --spec add_element(term(), ordset(_)) -> ordset(_). +-spec add_element(E, ordset(T)) -> [T | E,...]. add_element(E, [H|Es]) when E > H -> [H|add_element(E, Es)]; add_element(E, [H|_]=Set) when E < H -> [E|Set]; @@ -94,7 +96,7 @@ add_element(E, []) -> [E]. %% del_element(Element, OrdSet) -> OrdSet. %% Return OrdSet but with Element removed. --spec del_element(term(), ordset(_)) -> ordset(_). +-spec del_element(term(), ordset(T)) -> ordset(T). del_element(E, [H|Es]) when E > H -> [H|del_element(E, Es)]; del_element(E, [H|_]=Set) when E < H -> Set; @@ -104,7 +106,7 @@ del_element(_, []) -> []. %% union(OrdSet1, OrdSet2) -> OrdSet %% Return the union of OrdSet1 and OrdSet2. --spec union(ordset(_), ordset(_)) -> ordset(_). +-spec union(ordset(T1), ordset(T2)) -> ordset(T1 | T2). union([E1|Es1], [E2|_]=Set2) when E1 < E2 -> [E1|union(Es1, Set2)]; @@ -118,7 +120,7 @@ union(Es1, []) -> Es1. %% union([OrdSet]) -> OrdSet %% Return the union of the list of ordered sets. --spec union([ordset(_)]) -> ordset(_). +-spec union([ordset(T)]) -> ordset(T). union([S1,S2|Ss]) -> union1(union(S1, S2), Ss); @@ -206,7 +208,7 @@ is_subset(_, []) -> false. %% fold(Fun, Accumulator, OrdSet) -> Accumulator. %% Fold function Fun over all elements in OrdSet and return Accumulator. --spec fold(fun((_, _) -> _), _, ordset(_)) -> _. +-spec fold(fun((T, term()) -> term()), term(), ordset(T)) -> term(). fold(F, Acc, Set) -> lists:foldl(F, Acc, Set). @@ -214,7 +216,7 @@ fold(F, Acc, Set) -> %% filter(Fun, OrdSet) -> OrdSet. %% Filter OrdSet with Fun. --spec filter(fun((_) -> boolean()), ordset(_)) -> ordset(_). +-spec filter(fun((T) -> boolean()), ordset(T)) -> ordset(T). filter(F, Set) -> lists:filter(F, Set). diff --git a/lib/stdlib/src/string.erl b/lib/stdlib/src/string.erl index 6636a03f06..c987c224db 100644 --- a/lib/stdlib/src/string.erl +++ b/lib/stdlib/src/string.erl @@ -201,7 +201,7 @@ chars(C, 0, Tail) when is_integer(C) -> -spec copies(string(), non_neg_integer()) -> string(). -copies(CharList, Num) when is_list(CharList), Num >= 0 -> +copies(CharList, Num) when is_list(CharList), is_integer(Num), Num >= 0 -> copies(CharList, Num, []). copies(_CharList, 0, R) -> diff --git a/lib/stdlib/src/supervisor.erl b/lib/stdlib/src/supervisor.erl index f5d5441184..5bdd1a8672 100644 --- a/lib/stdlib/src/supervisor.erl +++ b/lib/stdlib/src/supervisor.erl @@ -33,7 +33,9 @@ -export([init/1, handle_call/3, handle_info/2, terminate/2, code_change/3]). -export([handle_cast/2]). --export_type([child_spec/0, strategy/0]). +%%-------------------------------------------------------------------------- + +-export_type([child_spec/0, del_err/0, startchild_ret/0, strategy/0]). %%-------------------------------------------------------------------------- @@ -77,6 +79,10 @@ -define(is_simple(State), State#state.strategy =:= simple_one_for_one). +%%-------------------------------------------------------------------------- + +-spec behaviour_info(atom()) -> 'undefined' | [{atom(), arity()}]. + behaviour_info(callbacks) -> [{init,1}]; behaviour_info(_Other) -> @@ -160,11 +166,13 @@ check_childspecs(X) -> {error, {badarg, X}}. %%% %%% --------------------------------------------------- +-type init_sup_name() :: sup_name() | 'self'. + -type stop_rsn() :: 'shutdown' | {'bad_return', {module(),'init', term()}} | {'bad_start_spec', term()} | {'start_spec', term()} | {'supervisor_data', term()}. --spec init({sup_name(), module(), [term()]}) -> +-spec init({init_sup_name(), module(), [term()]}) -> {'ok', state()} | 'ignore' | {'stop', stop_rsn()}. init({SupName, Mod, Args}) -> @@ -184,7 +192,7 @@ init({SupName, Mod, Args}) -> Error -> {stop, {bad_return, {Mod, init, Error}}} end. - + init_children(State, StartSpec) -> SupName = State#state.name, case check_startspec(StartSpec) of diff --git a/lib/stdlib/test/dets_SUITE.erl b/lib/stdlib/test/dets_SUITE.erl index 760e610e00..8b18ef5664 100644 --- a/lib/stdlib/test/dets_SUITE.erl +++ b/lib/stdlib/test/dets_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2010. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -50,7 +50,8 @@ otp_4208/1, otp_4989/1, many_clients/1, otp_4906/1, otp_5402/1, simultaneous_open/1, insert_new/1, repair_continuation/1, otp_5487/1, otp_6206/1, otp_6359/1, otp_4738/1, otp_7146/1, - otp_8070/1]). + otp_8070/1, otp_8856/1, otp_8898/1, otp_8899/1, otp_8903/1, + otp_8923/1]). -export([dets_dirty_loop/0]). @@ -108,7 +109,8 @@ all(suite) -> cache_duplicate_bags_v9, otp_4208, otp_4989, many_clients, otp_4906, otp_5402, simultaneous_open, insert_new, repair_continuation, otp_5487, otp_6206, otp_6359, otp_4738, - otp_7146, otp_8070]} + otp_7146, otp_8070, otp_8856, otp_8898, otp_8899, otp_8903, + otp_8923]} end. not_run(suite) -> []; @@ -2935,6 +2937,57 @@ ets_init(Tab, N) -> ets:insert(Tab, {N,N}), ets_init(Tab, N - 1). +otp_8898(doc) -> + ["OTP-8898. Truncated Dets file."]; +otp_8898(suite) -> + []; +otp_8898(Config) when is_list(Config) -> + Tab = otp_8898, + ?line FName = filename(Tab, Config), + + Server = self(), + + ?line file:delete(FName), + ?line {ok, _} = dets:open_file(Tab,[{file, FName}]), + ?line [P1,P2,P3] = new_clients(3, Tab), + + Seq = [{P1,[sync]},{P2,[{lookup,1,[]}]},{P3,[{insert,{1,b}}]}], + ?line atomic_requests(Server, Tab, [[]], Seq), + ?line true = get_replies([{P1,ok},{P2,ok},{P3,ok}]), + ?line ok = dets:close(Tab), + ?line {ok, _} = dets:open_file(Tab,[{file, FName}]), + ?line file:delete(FName), + + ok. + +otp_8899(doc) -> + ["OTP-8899. Several clients. Updated Head was ignored."]; +otp_8899(suite) -> + []; +otp_8899(Config) when is_list(Config) -> + Tab = many_clients, + ?line FName = filename(Tab, Config), + + Server = self(), + + ?line file:delete(FName), + ?line {ok, _} = dets:open_file(Tab,[{file, FName},{version,9}]), + ?line [P1,P2,P3,P4] = new_clients(4, Tab), + + MC = [Tab], + Seq6a = [{P1,[{insert,[{used_to_be_skipped_by,match}]}, + {lookup,1,[{1,a}]}]}, + {P2,[{verbose,true,MC}]}, + {P3,[{lookup,1,[{1,a}]}]}, {P4,[{verbose,true,MC}]}], + ?line atomic_requests(Server, Tab, [[{1,a},{2,b},{3,c}]], Seq6a), + ?line true = get_replies([{P1,ok}, {P2,ok}, {P3,ok}, {P4,ok}]), + ?line [{1,a},{2,b},{3,c},{used_to_be_skipped_by,match}] = + lists:sort(dets:match_object(Tab, '_')), + ?line _ = dets:close(Tab), + ?line file:delete(FName), + + ok. + many_clients(doc) -> ["Several clients accessing a table simultaneously."]; many_clients(suite) -> @@ -3071,6 +3124,11 @@ client(S, Tab) -> eval([], _Tab) -> ok; +eval([{verbose,Bool,Expected} | L], Tab) -> + ?line case dets:verbose(Bool) of + Expected -> eval(L, Tab); + Error -> {error, {verbose,Error}} + end; eval([sync | L], Tab) -> ?line case dets:sync(Tab) of ok -> eval(L, Tab); @@ -3701,6 +3759,87 @@ otp_8070(Config) when is_list(Config) -> file:delete(File), ok. +otp_8856(doc) -> + ["OTP-8856. insert_new() bug."]; +otp_8856(suite) -> + []; +otp_8856(Config) when is_list(Config) -> + Tab = otp_8856, + File = filename(Tab, Config), + file:delete(File), + Me = self(), + ?line {ok, _} = dets:open_file(Tab, [{type, bag}, {file, File}]), + spawn(fun()-> Me ! {1, dets:insert(Tab, [])} end), + spawn(fun()-> Me ! {2, dets:insert_new(Tab, [])} end), + ?line ok = dets:close(Tab), + ?line receive {1, ok} -> ok end, + ?line receive {2, true} -> ok end, + file:delete(File), + + ?line {ok, _} = dets:open_file(Tab, [{type, set}, {file, File}]), + spawn(fun() -> dets:delete(Tab, 0) end), + spawn(fun() -> Me ! {3, dets:insert_new(Tab, {0,0})} end), + ?line ok = dets:close(Tab), + ?line receive {3, true} -> ok end, + file:delete(File), + ok. + +otp_8903(doc) -> + ["OTP-8903. bchunk/match/select bug."]; +otp_8903(suite) -> + []; +otp_8903(Config) when is_list(Config) -> + Tab = otp_8903, + File = filename(Tab, Config), + ?line {ok,T} = dets:open_file(bug, [{file,File}]), + ?line ok = dets:insert(T, [{1,a},{2,b},{3,c}]), + ?line dets:safe_fixtable(T, true), + ?line {[_],C1} = dets:match_object(T, '_', 1), + ?line {BC1,_D} = dets:bchunk(T, start), + ?line ok = dets:close(T), + ?line {'EXIT', {badarg, _}} = (catch {foo,dets:match_object(C1)}), + ?line {'EXIT', {badarg, _}} = (catch {foo,dets:bchunk(T, BC1)}), + ?line {ok,T} = dets:open_file(bug, [{file,File}]), + ?line false = dets:info(T, safe_fixed), + ?line {'EXIT', {badarg, _}} = (catch {foo,dets:match_object(C1)}), + ?line {'EXIT', {badarg, _}} = (catch {foo,dets:bchunk(T, BC1)}), + ?line ok = dets:close(T), + file:delete(File), + ok. + +otp_8923(doc) -> + ["OTP-8923. rehash due to lookup after initialization."]; +otp_8923(suite) -> + []; +otp_8923(Config) when is_list(Config) -> + Tab = otp_8923, + File = filename(Tab, Config), + %% Create a file with more than 256 keys: + file:delete(File), + Bin = list_to_binary([ 0 || _ <- lists:seq(1, 400) ]), + BigBin = list_to_binary([ 0 ||_ <- lists:seq(1, 4000)]), + Ets = ets:new(temp, [{keypos,1}]), + ?line [ true = ets:insert(Ets, {C,Bin}) || C <- lists:seq(1, 700) ], + ?line true = ets:insert(Ets, {helper_data,BigBin}), + ?line true = ets:insert(Ets, {prim_btree,BigBin}), + ?line true = ets:insert(Ets, {sec_btree,BigBin}), + %% Note: too few slots; re-hash will take place + ?line {ok, Tab} = dets:open_file(Tab, [{file,File}]), + ?line Tab = ets:to_dets(Ets, Tab), + ?line ok = dets:close(Tab), + ?line true = ets:delete(Ets), + + ?line {ok,Ref} = dets:open_file(File), + ?line [{1,_}] = dets:lookup(Ref, 1), + ?line ok = dets:close(Ref), + + ?line {ok,Ref2} = dets:open_file(File), + ?line [{helper_data,_}] = dets:lookup(Ref2, helper_data), + ?line ok = dets:close(Ref2), + + file:delete(File), + ok. + %% %% Parts common to several test cases %% diff --git a/lib/stdlib/test/epp_SUITE.erl b/lib/stdlib/test/epp_SUITE.erl index e31dfdd764..e9fb932632 100644 --- a/lib/stdlib/test/epp_SUITE.erl +++ b/lib/stdlib/test/epp_SUITE.erl @@ -24,7 +24,7 @@ variable/1, variable_1/1, otp_4870/1, otp_4871/1, otp_5362/1, pmod/1, not_circular/1, skip_header/1, otp_6277/1, otp_7702/1, otp_8130/1, overload_mac/1, otp_8388/1, otp_8470/1, otp_8503/1, - otp_8562/1, otp_8665/1]). + otp_8562/1, otp_8665/1, otp_8911/1]). -export([epp_parse_erl_form/2]). @@ -64,7 +64,7 @@ all(doc) -> all(suite) -> [rec_1, upcase_mac, predef_mac, variable, otp_4870, otp_4871, otp_5362, pmod, not_circular, skip_header, otp_6277, otp_7702, otp_8130, - overload_mac, otp_8388, otp_8470, otp_8503, otp_8562, otp_8665]. + overload_mac, otp_8388, otp_8470, otp_8503, otp_8562, otp_8665, otp_8911]. rec_1(doc) -> ["Recursive macros hang or crash epp (OTP-1398)."]; @@ -1197,6 +1197,40 @@ otp_8562(Config) when is_list(Config) -> ?line [] = compile(Config, Cs), ok. +otp_8911(doc) -> + ["OTP-8911. -file and file inclusion bug"]; +otp_8911(suite) -> + []; +otp_8911(Config) when is_list(Config) -> + ?line {ok, CWD} = file:get_cwd(), + ?line ok = file:set_cwd(?config(priv_dir, Config)), + + File = "i.erl", + Cont = <<"-module(i). + -compile(export_all). + -file(\"fil1\", 100). + -include(\"i1.erl\"). + t() -> + a. + ">>, + ?line ok = file:write_file(File, Cont), + Incl = <<"-file(\"fil2\", 35). + t1() -> + b. + ">>, + File1 = "i1.erl", + ?line ok = file:write_file(File1, Incl), + + ?line {ok, i} = cover:compile(File), + ?line a = i:t(), + ?line {ok,[{{i,6},1}]} = cover:analyse(i, calls, line), + ?line cover:stop(), + + file:delete(File), + file:delete(File1), + ?line file:set_cwd(CWD), + ok. + otp_8665(doc) -> ["OTP-8665. Bugfix premature end."]; otp_8665(suite) -> diff --git a/lib/stdlib/test/escript_SUITE.erl b/lib/stdlib/test/escript_SUITE.erl index 77fd190e45..162ca6006f 100644 --- a/lib/stdlib/test/escript_SUITE.erl +++ b/lib/stdlib/test/escript_SUITE.erl @@ -31,6 +31,7 @@ epp/1, create_and_extract/1, foldl/1, + overflow/1, verify_sections/3 ]). @@ -48,7 +49,8 @@ all(suite) -> archive_script, epp, create_and_extract, - foldl + foldl, + overflow ]. init_per_testcase(_Case, Config) -> @@ -736,6 +738,17 @@ emulate_escript_foldl(Fun, Acc, File) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +overflow(Config) when is_list(Config) -> + Data = ?config(data_dir, Config), + Dir = filename:absname(Data), %Get rid of trailing slash. + ?line run(Dir, "arg_overflow", + [<<"ExitCode:0">>]), + ?line run(Dir, "linebuf_overflow", + [<<"ExitCode:0">>]), + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + run(Dir, Cmd0, Expected0) -> Expected = iolist_to_binary(expected_output(Expected0, Dir)), Cmd = case os:type() of diff --git a/lib/stdlib/test/escript_SUITE_data/arg_overflow b/lib/stdlib/test/escript_SUITE_data/arg_overflow new file mode 100755 index 0000000000..dd5accc051 --- /dev/null +++ b/lib/stdlib/test/escript_SUITE_data/arg_overflow @@ -0,0 +1,5 @@ +#! /usr/bin/env escript +%% -*- erlang -*- +%%!x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x +main(_) -> + halt(0). diff --git a/lib/stdlib/test/escript_SUITE_data/linebuf_overflow b/lib/stdlib/test/escript_SUITE_data/linebuf_overflow new file mode 100755 index 0000000000..33133c1ce9 --- /dev/null +++ b/lib/stdlib/test/escript_SUITE_data/linebuf_overflow @@ -0,0 +1,5 @@ +#! /usr/bin/env escript +%% -*- erlang -*- +%%!xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +main(_) -> + halt(0). diff --git a/lib/stdlib/test/string_SUITE.erl b/lib/stdlib/test/string_SUITE.erl index 3171b87c44..452e048dd7 100644 --- a/lib/stdlib/test/string_SUITE.erl +++ b/lib/stdlib/test/string_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2009. All Rights Reserved. +%% Copyright Ericsson AB 2004-2010. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -240,7 +240,8 @@ copies(Config) when is_list(Config) -> ?line "." = string:copies(".", 1), ?line 30 = length(string:copies("123", 10)), %% invalid arg type - ?line {'EXIT',_} = (catch string:chars("hej", -1)), + ?line {'EXIT',_} = (catch string:copies("hej", -1)), + ?line {'EXIT',_} = (catch string:copies("hej", 2.0)), ok. words(suite) -> diff --git a/lib/syntax_tools/src/erl_syntax.erl b/lib/syntax_tools/src/erl_syntax.erl index a40bf83c5a..9df5f26454 100644 --- a/lib/syntax_tools/src/erl_syntax.erl +++ b/lib/syntax_tools/src/erl_syntax.erl @@ -1818,7 +1818,7 @@ char_value(Node) -> %% %% @see char/1 --spec char_literal(syntaxTree()) -> string(). +-spec char_literal(syntaxTree()) -> nonempty_string(). char_literal(Node) -> io_lib:write_char(char_value(Node)). @@ -1908,7 +1908,7 @@ string_value(Node) -> %% %% @see string/1 --spec string_literal(syntaxTree()) -> string(). +-spec string_literal(syntaxTree()) -> nonempty_string(). string_literal(Node) -> io_lib:write_string(string_value(Node)). diff --git a/lib/syntax_tools/src/erl_syntax_lib.erl b/lib/syntax_tools/src/erl_syntax_lib.erl index daef74e874..97dfbfd7cd 100644 --- a/lib/syntax_tools/src/erl_syntax_lib.erl +++ b/lib/syntax_tools/src/erl_syntax_lib.erl @@ -49,10 +49,6 @@ -export_type([info_pair/0]). %% ===================================================================== - --type ordset(X) :: [X]. % XXX: TAKE ME OUT - -%% ===================================================================== %% @spec map(Function, Tree::syntaxTree()) -> syntaxTree() %% %% Function = (syntaxTree()) -> syntaxTree() @@ -480,7 +476,7 @@ new_variable_names(0, Names, _, _, _) -> %% @see annotate_bindings/1 %% @see //stdlib/ordsets --spec annotate_bindings(erl_syntax:syntaxTree(), ordset(atom())) -> +-spec annotate_bindings(erl_syntax:syntaxTree(), ordsets:ordset(atom())) -> erl_syntax:syntaxTree(). annotate_bindings(Tree, Env) -> diff --git a/lib/syntax_tools/src/igor.erl b/lib/syntax_tools/src/igor.erl index 7ec62f1dba..aa933eb54b 100644 --- a/lib/syntax_tools/src/igor.erl +++ b/lib/syntax_tools/src/igor.erl @@ -119,20 +119,16 @@ %% ===================================================================== --type ordset(X) :: [X]. % XXX: TAKE ME OUT - -%% ===================================================================== - %% Data structure for module information -record(module, {name :: atom(), vars = none :: [atom()] | 'none', - functions :: ordset({atom(), arity()}), - exports :: ordset({atom(), arity()}) - | ordset({{atom(), arity()}, term()}), - aliases :: ordset({{atom(), arity()}, - {atom(), {atom(), arity()}}}), - attributes :: ordset({atom(), term()}), + functions :: ordsets:ordset({atom(), arity()}), + exports :: ordsets:ordset({atom(), arity()}) + | ordsets:ordset({{atom(), arity()}, term()}), + aliases :: ordsets:ordset({{atom(), arity()}, + {atom(), {atom(), arity()}}}), + attributes :: ordsets:ordset({atom(), term()}), records :: [{atom(), [{atom(), term()}]}] }). @@ -149,7 +145,7 @@ default_printer(Tree, Options) -> -type moduleName() :: atom(). -type functionName() :: {atom(), arity()}. -type functionPair() :: {functionName(), {moduleName(), functionName()}}. --type stubDescriptor() :: [{moduleName(), [functionPair()], [attribute()]}]. +-type stubDescriptor() :: {moduleName(), [functionPair()], [attribute()]}. -type notes() :: 'always' | 'yes' | 'no'. @@ -209,7 +205,7 @@ parse_transform(Forms, Options) -> %% @spec merge(Name::atom(), Files::[filename()]) -> [filename()] %% @equiv merge(Name, Files, []) --spec merge(atom(), [file:filename()]) -> [file:filename()]. +-spec merge(atom(), [file:filename()]) -> [file:filename(),...]. merge(Name, Files) -> merge(Name, Files, []). @@ -343,7 +339,7 @@ merge(Name, Files) -> {suffix, ?DEFAULT_SUFFIX}, {verbose, false}]). --spec merge(atom(), [file:filename()], [option()]) -> [file:filename()]. +-spec merge(atom(), [file:filename()], [option()]) -> [file:filename(),...]. merge(Name, Files, Opts) -> Opts1 = Opts ++ ?DEFAULT_MERGE_OPTS, @@ -484,7 +480,7 @@ merge_files(Name, Trees, Files, Opts) -> %% %% Forms = syntaxTree() | [syntaxTree()] %% -%% @type stubDescriptor() = [{ModuleName, Functions, [Attribute]}] +%% @type stubDescriptor() = {ModuleName, Functions, [Attribute]} %% ModuleName = atom() %% Functions = [{FunctionName, {ModuleName, FunctionName}}] %% FunctionName = {atom(), integer()} @@ -687,15 +683,15 @@ merge_files(Name, Trees, Files, Opts) -> %% Data structure for merging environment. -record(merge, {target :: atom(), - sources :: ordset(atom()), - export :: ordset(atom()), - static :: ordset(atom()), - safe :: ordset(atom()), + sources :: ordsets:ordset(atom()), + export :: ordsets:ordset(atom()), + static :: ordsets:ordset(atom()), + safe :: ordsets:ordset(atom()), preserved :: boolean(), no_headers :: boolean(), notes :: notes(), redirect :: dict(), % = dict(atom(), atom()) - no_imports :: ordset(atom()), + no_imports :: ordsets:ordset(atom()), options :: [option()] }). diff --git a/system/doc/top/Makefile b/system/doc/top/Makefile index 148fefaf13..aac90fcaa4 100644 --- a/system/doc/top/Makefile +++ b/system/doc/top/Makefile @@ -246,7 +246,7 @@ release_docs_spec: docs $(INSTALL_DATA) $(INDEX_FILES) $(MAN_INDEX) $(TOP_HTML_FILES) $(RELSYSDIR) $(INSTALL_DIR) $(RELSYSDIR)/docbuild $(INSTALL_DATA) $(INDEX_SCRIPT) $(MAN_INDEX_SCRIPT) $(JAVASCRIPT_BUILD_SCRIPT) \ - $(INDEX_SCRIPT_SRC) $(MAN_INDEX_SCRIPT_SRC) $(JAVASCRIPT_BUILD_SCRIPT_SRC) \ + $(INDEX_SRC) $(MAN_INDEX_SRC) $(JAVASCRIPT_BUILD_SCRIPT_SRC) \ $(TEMPLATES) $(RELSYSDIR)/docbuild diff --git a/system/doc/top/src/erl_html_tools.erl b/system/doc/top/src/erl_html_tools.erl index fef56331fc..599268804e 100644 --- a/system/doc/top/src/erl_html_tools.erl +++ b/system/doc/top/src/erl_html_tools.erl @@ -40,7 +40,8 @@ group_order() -> {test, "Test"}, {doc, "Documentation"}, {orb, "Object Request Broker & IDL"}, - {misc, "Miscellaneous"} + {misc, "Miscellaneous"}, + {eric, "Ericsson Internal"} ]. top_index() -> |