diff options
Diffstat (limited to 'lib')
88 files changed, 2063 insertions, 582 deletions
diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl index fbe4d8617e..3b33a08cf7 100644 --- a/lib/compiler/src/v3_kernel.erl +++ b/lib/compiler/src/v3_kernel.erl @@ -147,6 +147,7 @@ attributes([]) -> []. include_attribute(type) -> false; include_attribute(spec) -> false; include_attribute(opaque) -> false; +include_attribute(export_type) -> false; include_attribute(_) -> true. function({#c_var{name={F,Arity}=FA},Body}, St0) -> diff --git a/lib/dialyzer/src/dialyzer_typesig.erl b/lib/dialyzer/src/dialyzer_typesig.erl index f68472d2fc..c45615d670 100644 --- a/lib/dialyzer/src/dialyzer_typesig.erl +++ b/lib/dialyzer/src/dialyzer_typesig.erl @@ -1406,9 +1406,13 @@ get_bif_constr({erlang, 'or', 2}, Dst, [Arg1, Arg2] = Args, _State) -> ArgV1 = mk_fun_var(ArgFun(Arg2), [Arg2, Dst]), ArgV2 = mk_fun_var(ArgFun(Arg1), [Arg1, Dst]), DstV = mk_fun_var(DstFun, Args), - Disj = mk_disj_constraint_list([mk_constraint(Arg1, sub, True), - mk_constraint(Arg2, sub, True), - mk_constraint(Dst, sub, False)]), + F = fun(A) -> + try [mk_constraint(A, sub, True)] + catch throw:error -> [] + end + end, + Constrs = F(Arg1) ++ F(Arg2), + Disj = mk_disj_constraint_list([mk_constraint(Dst, sub, False)|Constrs]), mk_conj_constraint_list([mk_constraint(Dst, sub, DstV), mk_constraint(Arg1, sub, ArgV1), mk_constraint(Arg2, sub, ArgV2), diff --git a/lib/erl_interface/doc/src/ei.xml b/lib/erl_interface/doc/src/ei.xml index d7af7a1b67..de4e4b4301 100644 --- a/lib/erl_interface/doc/src/ei.xml +++ b/lib/erl_interface/doc/src/ei.xml @@ -641,12 +641,14 @@ ei_x_encode_empty_list(&x); <p></p> <pre> ~a - an atom, char* +~c - a character, char ~s - a string, char* ~i - an integer, int ~l - a long integer, long int ~u - a unsigned long integer, unsigned long int ~f - a float, float ~d - a double float, double float +~p - an Erlang PID, erlang_pid* </pre> <p>For instance, to encode a tuple with some stuff:</p> <pre> diff --git a/lib/erl_interface/include/ei.h b/lib/erl_interface/include/ei.h index 466d84bb99..ae815b414a 100644 --- a/lib/erl_interface/include/ei.h +++ b/lib/erl_interface/include/ei.h @@ -80,21 +80,24 @@ #define ERL_NO_TIMEOUT -1 /* these are the control message types */ -#define ERL_LINK 1 -#define ERL_SEND 2 -#define ERL_EXIT 3 -#define ERL_UNLINK 4 -#define ERL_NODE_LINK 5 -#define ERL_REG_SEND 6 -#define ERL_GROUP_LEADER 7 -#define ERL_EXIT2 8 -#define ERL_PASS_THROUGH 'p' +#define ERL_LINK 1 +#define ERL_SEND 2 +#define ERL_EXIT 3 +#define ERL_UNLINK 4 +#define ERL_NODE_LINK 5 +#define ERL_REG_SEND 6 +#define ERL_GROUP_LEADER 7 +#define ERL_EXIT2 8 +#define ERL_PASS_THROUGH 'p' /* new ones for tracing, from Kenneth */ -#define ERL_SEND_TT 12 -#define ERL_EXIT_TT 13 -#define ERL_REG_SEND_TT 16 -#define ERL_EXIT2_TT 18 +#define ERL_SEND_TT 12 +#define ERL_EXIT_TT 13 +#define ERL_REG_SEND_TT 16 +#define ERL_EXIT2_TT 18 +#define ERL_MONITOR_P 19 +#define ERL_DEMONITOR_P 20 +#define ERL_MONITOR_P_EXIT 21 /* -------------------------------------------------------------------- */ diff --git a/lib/erl_interface/src/connect/ei_connect.c b/lib/erl_interface/src/connect/ei_connect.c index 99ccba0686..6dc6ebb348 100644 --- a/lib/erl_interface/src/connect/ei_connect.c +++ b/lib/erl_interface/src/connect/ei_connect.c @@ -938,7 +938,7 @@ int ei_do_receive_msg(int fd, int staticbuffer_p, return ERL_ERROR; } x->index = x->buffsz; - switch (msg->msgtype) { /* FIXME are these all? */ + switch (msg->msgtype) { /* FIXME does not handle trace tokens and monitors */ case ERL_SEND: case ERL_REG_SEND: case ERL_LINK: @@ -946,7 +946,6 @@ int ei_do_receive_msg(int fd, int staticbuffer_p, case ERL_GROUP_LEADER: case ERL_EXIT: case ERL_EXIT2: - case ERL_NODE_LINK: return ERL_MSG; default: @@ -1329,6 +1328,7 @@ static int send_name_or_challenge(int fd, char *nodename, put8(s, 'n'); put16be(s, version); put32be(s, (DFLAG_EXTENDED_REFERENCES + | DFLAG_DIST_MONITOR | DFLAG_EXTENDED_PIDS_PORTS | DFLAG_FUN_TAGS | DFLAG_NEW_FUN_TAGS diff --git a/lib/erl_interface/src/connect/eirecv.c b/lib/erl_interface/src/connect/eirecv.c index 7d72ddeeae..86852f947d 100644 --- a/lib/erl_interface/src/connect/eirecv.c +++ b/lib/erl_interface/src/connect/eirecv.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1998-2009. All Rights Reserved. + * Copyright Ericsson AB 1998-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 @@ -196,10 +196,6 @@ ei_recv_internal (int fd, ei_trace(1,&msg->token); /* turn on tracing */ break; - case ERL_NODE_LINK: /* { NODE_LINK } */ - if (ei_tracelevel >= 4) show_this_msg = 1; - break; - default: /* unknown type, just put any remaining bytes into buffer */ break; diff --git a/lib/erl_interface/src/legacy/erl_marshal.c b/lib/erl_interface/src/legacy/erl_marshal.c index a6c2f64dd0..70949a7adf 100644 --- a/lib/erl_interface/src/legacy/erl_marshal.c +++ b/lib/erl_interface/src/legacy/erl_marshal.c @@ -1646,11 +1646,14 @@ static int cmp_exe2(unsigned char **e1, unsigned char **e2) min = (i < j) ? i : j; k = 0; while (1) { - if (k++ == min) - return compare_top_ext(e1 , e2); - if ((ret = compare_top_ext(e1 , e2)) == 0) - continue; - return ret; + if (k++ == min){ + if (i == j) return 0; + if (i < j) return -1; + return 1; + } + if ((ret = compare_top_ext(e1 , e2)) == 0) + continue; + return ret; } case ERL_STRING_EXT: i = (**e1 << 8) | ((*e1)[1]); diff --git a/lib/erl_interface/src/legacy/global_register.c b/lib/erl_interface/src/legacy/global_register.c index 3a4de8b08e..f12eb6b448 100644 --- a/lib/erl_interface/src/legacy/global_register.c +++ b/lib/erl_interface/src/legacy/global_register.c @@ -31,7 +31,7 @@ int erl_global_register(int fd, const char *name, ETERM *pid) int index = 0; erlang_pid self; erlang_msg msg; - int needlink, needatom; + int needlink, needatom, needmonitor; int arity; int version; int msglen; @@ -65,7 +65,7 @@ int erl_global_register(int fd, const char *name, ETERM *pid) if (ei_send_reg_encoded(fd,&self,"rex",buf,index)) return -1; /* get the reply: expect link and an atom, or just an atom */ - needlink = needatom = 1; + needlink = needatom = needmonitor = 1; while (1) { /* get message */ while (1) { @@ -78,9 +78,15 @@ int erl_global_register(int fd, const char *name, ETERM *pid) case ERL_LINK: /* got link */ if (!needlink) return -1; - needlink = 0; + needlink = 0; break; + case ERL_MONITOR_P-10: + /* got monitor */ + if (!needmonitor) { return -1;} + needmonitor = 0; + break; + case ERL_SEND: /* got message - does it contain our atom? */ if (!needatom) return -1; diff --git a/lib/erl_interface/src/legacy/global_unregister.c b/lib/erl_interface/src/legacy/global_unregister.c index 514dbc3c68..97a1c2d03c 100644 --- a/lib/erl_interface/src/legacy/global_unregister.c +++ b/lib/erl_interface/src/legacy/global_unregister.c @@ -37,7 +37,7 @@ int erl_global_unregister(int fd, const char *name) erlang_msg msg; int i; int version,arity,msglen; - int needunlink, needatom; + int needunlink, needatom, needdemonitor; /* make a self pid */ self->num = fd; @@ -57,7 +57,7 @@ int erl_global_unregister(int fd, const char *name) if (ei_send_reg_encoded(fd,self,"rex",buf,index)) return -1; /* get the reply: expect unlink and an atom, or just an atom */ - needunlink = needatom = 1; + needunlink = needatom = needdemonitor = 1; while (1) { /* get message */ while (1) { @@ -68,11 +68,17 @@ int erl_global_unregister(int fd, const char *name) switch (i) { case ERL_UNLINK: - /* got link */ + /* got unlink */ if (!needunlink) return -1; needunlink = 0; break; + case ERL_DEMONITOR_P-10: + /* got demonitor */ + if (!needdemonitor) return -1; + needdemonitor = 0; + break; + case ERL_SEND: /* got message - does it contain our atom? */ if (!needatom) return -1; diff --git a/lib/erl_interface/src/misc/ei_format.c b/lib/erl_interface/src/misc/ei_format.c index b35421d4b2..dbd7a4479a 100644 --- a/lib/erl_interface/src/misc/ei_format.c +++ b/lib/erl_interface/src/misc/ei_format.c @@ -47,10 +47,12 @@ * array of unions. */ union arg { + char c; char* s; long l; unsigned long u; double d; + erlang_pid* pid; }; static int eiformat(const char** s, union arg** args, ei_x_buff* x); @@ -224,12 +226,14 @@ static int pquotedatom(const char** fmt, ei_x_buff* x) /* * The format letters are: * a - An atom + * c - A character * s - A string * i - An integer * l - A long integer * u - An unsigned long integer * f - A float * d - A double float + * p - An Erlang PID */ static int pformat(const char** fmt, union arg** args, ei_x_buff* x) { @@ -240,6 +244,10 @@ static int pformat(const char** fmt, union arg** args, ei_x_buff* x) res = ei_x_encode_atom(x, (*args)->s); (*args)++; break; + case 'c': + res = ei_x_encode_char(x, (*args)->c); + (*args)++; + break; case 's': res = ei_x_encode_string(x, (*args)->s); (*args)++; @@ -261,6 +269,10 @@ static int pformat(const char** fmt, union arg** args, ei_x_buff* x) res = ei_x_encode_double(x, (*args)->d); (*args)++; break; + case 'p': + res = ei_x_encode_pid(x, (*args)->pid); + (*args)++; + break; default: res = -1; break; @@ -396,6 +408,9 @@ static int read_args(const char* fmt, va_list ap, union arg **argp) return -1; /* Error, string not complete */ } switch (*p++) { + case 'c': + args[i++].c = (char) va_arg(ap, int); + break; case 'a': case 's': args[i++].s = va_arg(ap, char*); @@ -415,6 +430,9 @@ static int read_args(const char* fmt, va_list ap, union arg **argp) case 'd': args[i++].d = va_arg(ap, double); break; + case 'p': + args[i++].pid = va_arg(ap, erlang_pid*); + break; default: ei_free(args); /* Invalid specifier */ return -1; diff --git a/lib/erl_interface/src/misc/show_msg.c b/lib/erl_interface/src/misc/show_msg.c index 14bea5e01f..194296798b 100644 --- a/lib/erl_interface/src/misc/show_msg.c +++ b/lib/erl_interface/src/misc/show_msg.c @@ -181,11 +181,6 @@ int ei_show_sendmsg(FILE *stream, const char *header, const char *msgbuf) mbuf = header; break; - case ERL_NODE_LINK: - /* nothing to do */ - mbuf = header; - break; - default: break; } @@ -241,10 +236,6 @@ static void show_msg(FILE *stream, int direction, const erlang_msg *msg, show_pid(stream,&msg->to); break; - case ERL_NODE_LINK: - fprintf(stream,"NODE_LINK"); - break; - case ERL_REG_SEND: fprintf(stream,"REG_SEND From: "); show_pid(stream,&msg->from); diff --git a/lib/erl_interface/test/Makefile b/lib/erl_interface/test/Makefile index b7a1a4e4d8..07404fda4d 100644 --- a/lib/erl_interface/test/Makefile +++ b/lib/erl_interface/test/Makefile @@ -33,6 +33,7 @@ MODULES= \ ei_print_SUITE \ ei_tmo_SUITE \ erl_connect_SUITE \ + erl_global_SUITE \ erl_eterm_SUITE \ erl_ext_SUITE \ erl_format_SUITE \ diff --git a/lib/erl_interface/test/ei_connect_SUITE.erl b/lib/erl_interface/test/ei_connect_SUITE.erl index fe82a73ef9..3c72188e16 100644 --- a/lib/erl_interface/test/ei_connect_SUITE.erl +++ b/lib/erl_interface/test/ei_connect_SUITE.erl @@ -30,6 +30,7 @@ ei_send/1, ei_reg_send/1, + ei_format_pid/1, ei_rpc/1, rpc_test/1, ei_send_funs/1, @@ -41,6 +42,7 @@ all(suite) -> [ ei_send, ei_reg_send, + ei_format_pid, ei_rpc, ei_send_funs, ei_threaded_send, @@ -67,6 +69,19 @@ ei_send(Config) when is_list(Config) -> ?line runner:recv_eot(P), ok. +ei_format_pid(Config) when is_list(Config) -> + ?line S = self(), + ?line P = runner:start(?interpret), + ?line 0 = ei_connect_init(P, 42, erlang:get_cookie(), 0), + ?line {ok,Fd} = ei_connect(P, node()), + + ?line ok = ei_format_pid(P, Fd, S), + ?line receive S -> ok end, + + ?line runner:send_eot(P), + ?line runner:recv_eot(P), + ok. + ei_send_funs(Config) when is_list(Config) -> ?line P = runner:start(?interpret), ?line 0 = ei_connect_init(P, 42, erlang:get_cookie(), 0), @@ -189,6 +204,10 @@ ei_send(P, Fd, To, Msg) -> send_command(P, ei_send, [Fd,To,Msg]), get_send_result(P). +ei_format_pid(P, Fd, To) -> + send_command(P, ei_format_pid, [Fd, To]), + get_send_result(P). + ei_send_funs(P, Fd, To, Msg) -> send_command(P, ei_send_funs, [Fd,To,Msg]), get_send_result(P). diff --git a/lib/erl_interface/test/ei_connect_SUITE_data/ei_connect_test.c b/lib/erl_interface/test/ei_connect_SUITE_data/ei_connect_test.c index debd3e789b..8183ac9dd8 100644 --- a/lib/erl_interface/test/ei_connect_SUITE_data/ei_connect_test.c +++ b/lib/erl_interface/test/ei_connect_SUITE_data/ei_connect_test.c @@ -35,6 +35,7 @@ static void cmd_ei_connect_init(char* buf, int len); static void cmd_ei_connect(char* buf, int len); static void cmd_ei_send(char* buf, int len); +static void cmd_ei_format_pid(char* buf, int len); static void cmd_ei_send_funs(char* buf, int len); static void cmd_ei_reg_send(char* buf, int len); static void cmd_ei_rpc(char* buf, int len); @@ -57,6 +58,7 @@ static struct { "ei_reg_send", 3, cmd_ei_reg_send, "ei_rpc", 4, cmd_ei_rpc, "ei_set_get_tracelevel", 1, cmd_ei_set_get_tracelevel, + "ei_format_pid", 2, cmd_ei_format_pid, }; @@ -111,7 +113,7 @@ static void cmd_ei_connect_init(char* buf, int len) ei_x_buff res; if (ei_decode_long(buf, &index, &l) < 0) fail("expected int"); - sprintf(b, "c%d", l); + sprintf(b, "c%ld", l); /* FIXME don't use internal and maybe use skip?! */ ei_get_type_internal(buf, &index, &type, &size); if (ei_decode_atom(buf, &index, cookie) < 0) @@ -183,6 +185,25 @@ static void cmd_ei_send(char* buf, int len) ei_x_free(&x); } +static void cmd_ei_format_pid(char* buf, int len) +{ + int index = 0; + long fd; + erlang_pid pid; + ei_x_buff x; + + if (ei_decode_long(buf, &index, &fd) < 0) + fail("expected long"); + if (ei_decode_pid(buf, &index, &pid) < 0) + fail("expected pid (node)"); + if (ei_x_new_with_version(&x) < 0) + fail("ei_x_new_with_version"); + if (ei_x_format_wo_ver(&x, "~p", &pid) < 0) + fail("ei_x_format_wo_ver"); + send_errno_result(ei_send(fd, &pid, x.buff, x.index)); + ei_x_free(&x); +} + static void cmd_ei_send_funs(char* buf, int len) { int index = 0, n; diff --git a/lib/erl_interface/test/ei_format_SUITE.erl b/lib/erl_interface/test/ei_format_SUITE.erl index cbe9fa52d7..6d44e0adf3 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, [-1, 2, {a, "b"}, {c, 10}]} = get_term(P), + ?line {term, [-1, 2, $c, {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 ecdce402f5..a6eeb25abc 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, "[-1, +2, {~a,~s},{~a,~i}]", "a", "b", "c", 10); + ei_x_format(&x, "[-1, +2, ~c, {~a,~s},{~a,~i}]", 'c', "a", "b", "c", 10); send_bin_term(&x); free(x.buff); diff --git a/lib/erl_interface/test/erl_ext_SUITE_data/ext_test.c b/lib/erl_interface/test/erl_ext_SUITE_data/ext_test.c index ba1a6c66da..59e0e0cce7 100644 --- a/lib/erl_interface/test/erl_ext_SUITE_data/ext_test.c +++ b/lib/erl_interface/test/erl_ext_SUITE_data/ext_test.c @@ -82,6 +82,11 @@ TESTCASE(compare_list) { // erlang:term_to_binary([34,{a,n},a,erlang]) unsigned char term2[] = {131,108,0,0,0,4,97,34,104,2,100,0,1,97,100,0,1,110,100,0,1,97,100,0,6,101,114,108,97,110,103,106}; + // erlang:term_to_binary([0]) + unsigned char term3[] = {131,107,0,1,0}; + // erlang:term_to_binary([0, 1000]) + unsigned char term4[] = {131,108,0,0,0,2,97,0,98,0,0,3,232,106}; + erl_init(NULL, 0); start_a = term1; start_b = term2; @@ -90,6 +95,13 @@ TESTCASE(compare_list) { test_compare_ext("lists", start_a, end_a, start_b, end_b, 1); + start_a = term3; + start_b = term4; + end_a = term3 + sizeof(term3); + end_b = term4 + sizeof(term4); + + test_compare_ext("lists1", start_a, end_a, start_b, end_b, -1); + report(1); } diff --git a/lib/erl_interface/test/erl_global_SUITE.erl b/lib/erl_interface/test/erl_global_SUITE.erl new file mode 100644 index 0000000000..4f332037c6 --- /dev/null +++ b/lib/erl_interface/test/erl_global_SUITE.erl @@ -0,0 +1,133 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2000-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%% +-module(erl_global_SUITE). + +-include("test_server.hrl"). +-include("erl_global_SUITE_data/erl_global_test_cases.hrl"). + +-export([all/1,init_per_testcase/2,fin_per_testcase/2, + erl_global_registration/1, erl_global_whereis/1, erl_global_names/1]). + +-import(runner, [get_term/1,send_term/2]). + +-define(GLOBAL_NAME, global_register_node_test). + +all(suite) -> + [erl_global_registration, erl_global_whereis, erl_global_names]. + +init_per_testcase(_Case, Config) -> + Dog = ?t:timetrap(?t:minutes(0.25)), + [{watchdog, Dog}|Config]. + +fin_per_testcase(_Case, Config) -> + Dog = ?config(watchdog, Config), + test_server:timetrap_cancel(Dog), + ok. + +erl_global_registration(Config) when is_list(Config) -> + ?line P = runner:start(?interpret), + ?line {ok, Fd} = erl_connect(P, node(), 42, erlang:get_cookie(), 0), + + ?line ok = erl_global_register(P, Fd, ?GLOBAL_NAME), + ?line ok = erl_global_unregister(P, Fd, ?GLOBAL_NAME), + + ?line 0 = erl_close_connection(P,Fd), + ?line runner:send_eot(P), + ?line runner:recv_eot(P), + ok. + +erl_global_whereis(Config) when is_list(Config) -> + ?line P = runner:start(?interpret), + ?line {ok, Fd} = erl_connect(P, node(), 42, erlang:get_cookie(), 0), + + ?line Self = self(), + ?line yes = global:register_name(?GLOBAL_NAME, Self), + ?line Self = erl_global_whereis(P, Fd, ?GLOBAL_NAME), + ?line global:unregister_name(?GLOBAL_NAME), + ?line 0 = erl_close_connection(P, Fd), + ?line runner:send_eot(P), + ?line runner:recv_eot(P), + ok. + +erl_global_names(Config) when is_list(Config) -> + ?line P = runner:start(?interpret), + ?line {ok, Fd} = erl_connect(P, node(), 42, erlang:get_cookie(), 0), + + ?line Self = self(), + ?line global:register_name(?GLOBAL_NAME, Self), + ?line {Names1, _N1} = erl_global_names(P, Fd), + ?line true = lists:member(atom_to_list(?GLOBAL_NAME), Names1), + ?line global:unregister_name(?GLOBAL_NAME), + ?line {Names2, _N2} = erl_global_names(P, Fd), + ?line false = lists:member(atom_to_list(?GLOBAL_NAME), Names2), + ?line 0 = erl_close_connection(P, Fd), + ?line runner:send_eot(P), + ?line runner:recv_eot(P), + ok. + +%%% Interface functions for erl_interface functions. + +erl_connect(P, Node, Num, Cookie, Creation) -> + send_command(P, erl_connect, [Num, Node, Cookie, Creation]), + case get_term(P) of + {term,{Fd,_}} when Fd >= 0 -> {ok,Fd}; + {term,{-1,Errno}} -> {error,Errno} + end. + +erl_close_connection(P, FD) -> + send_command(P, erl_close_connection, [FD]), + case get_term(P) of + {term,Int} when is_integer(Int) -> Int + end. + +erl_global_register(P, Fd, Name) -> + send_command(P, erl_global_register, [Fd,Name]), + get_send_result(P). + +erl_global_whereis(P, Fd, Name) -> + send_command(P, erl_global_whereis, [Fd,Name]), + case get_term(P) of + {term, What} -> + What + end. + +erl_global_names(P, Fd) -> + send_command(P, erl_global_names, [Fd]), + case get_term(P) of + {term, What} -> + What + end. + +erl_global_unregister(P, Fd, Name) -> + send_command(P, erl_global_unregister, [Fd,Name]), + get_send_result(P). + +get_send_result(P) -> + case get_term(P) of + {term,{1,_}} -> ok; + {term,{0, 0}} -> ok; + {term,{-1, Errno}} -> {error,Errno}; + {term,{_,_}}-> + ?t:fail(bad_return_value) + end. + +send_command(P, Name, Args) -> + runner:send_term(P, {Name,list_to_tuple(Args)}). diff --git a/lib/erl_interface/test/erl_global_SUITE_data/Makefile.first b/lib/erl_interface/test/erl_global_SUITE_data/Makefile.first new file mode 100644 index 0000000000..8e3fcb924e --- /dev/null +++ b/lib/erl_interface/test/erl_global_SUITE_data/Makefile.first @@ -0,0 +1,21 @@ +# +# %CopyrightBegin% +# +# 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 +# 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% +# + +erl_global_test_decl.c: erl_global_test.c + erl -noinput -pa ../all_SUITE_data -s init_tc run erl_global_test -s erlang halt diff --git a/lib/erl_interface/test/erl_global_SUITE_data/Makefile.src b/lib/erl_interface/test/erl_global_SUITE_data/Makefile.src new file mode 100644 index 0000000000..ef846bc440 --- /dev/null +++ b/lib/erl_interface/test/erl_global_SUITE_data/Makefile.src @@ -0,0 +1,41 @@ +# +# %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_interface_mk_include@@[email protected] + +CC0 = @CC@ +CC = ..@DS@all_SUITE_data@DS@gccifier@exe@ -CC"$(CC0)" +LD = @LD@ +LIBPATH = @erl_interface_libpath@ +LIBERL = $(LIBPATH)/@erl_interface_lib@ +LIBEI = $(LIBPATH)/@erl_interface_eilib@ +LIBFLAGS = ../all_SUITE_data/runner@obj@ \ + $(LIBERL) $(LIBEI) @LIBS@ @erl_interface_sock_libs@ \ + @erl_interface_threadlib@ +CFLAGS = @EI_CFLAGS@ $(THR_DEFS) -I@erl_interface_include@ -I../all_SUITE_data +OBJS = erl_global_test@obj@ erl_global_test_decl@obj@ + +all: erl_global_test@exe@ + +erl_global_test@exe@: $(OBJS) $(LIBERL) $(LIBEI) + $(LD) @CROSSLDFLAGS@ -o $@ $(OBJS) $(LIBFLAGS) + +clean: + $(RM) $(OBJS) + $(RM) erl_global_test@exe@ diff --git a/lib/erl_interface/test/erl_global_SUITE_data/erl_global_test.c b/lib/erl_interface/test/erl_global_SUITE_data/erl_global_test.c new file mode 100644 index 0000000000..dc0d8a0091 --- /dev/null +++ b/lib/erl_interface/test/erl_global_SUITE_data/erl_global_test.c @@ -0,0 +1,263 @@ +/* + * %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% + */ + +/* + * Purpose: Tests the functions in erl_global.c. + * + * See the erl_global_SUITE.erl file for a "table of contents". + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "runner.h" + +static void cmd_erl_connect(ETERM* args); +static void cmd_erl_global_register(ETERM *args); +static void cmd_erl_global_whereis(ETERM *args); +static void cmd_erl_global_names(ETERM *args); +static void cmd_erl_global_unregister(ETERM *args); +static void cmd_erl_close_connection(ETERM *args); + +static void send_errno_result(int value); + +static struct { + char* name; + int num_args; /* Number of arguments. */ + void (*func)(ETERM* args); +} commands[] = { + "erl_connect", 4, cmd_erl_connect, + "erl_close_connection", 1, cmd_erl_close_connection, + "erl_global_register", 2, cmd_erl_global_register, + "erl_global_whereis", 2, cmd_erl_global_whereis, + "erl_global_names", 1, cmd_erl_global_names, + "erl_global_unregister", 2, cmd_erl_global_unregister, +}; + + +/* + * Sends a list contaning all data types to the Erlang side. + */ + +TESTCASE(interpret) +{ + ETERM* term; + + erl_init(NULL, 0); + + outer_loop: + + term = get_term(); + + if (term == NULL) { + report(1); + return; + } else { + ETERM* Func; + ETERM* Args; + int i; + + if (!ERL_IS_TUPLE(term) || ERL_TUPLE_SIZE(term) != 2) { + fail("term should be a tuple of size 2"); + } + + Func = erl_element(1, term); + if (!ERL_IS_ATOM(Func)) { + fail("function name should be an atom"); + } + Args = erl_element(2, term); + if (!ERL_IS_TUPLE(Args)) { + fail("function arguments should be a tuple"); + } + erl_free_term(term); + for (i = 0; i < sizeof(commands)/sizeof(commands[0]); i++) { + int n = strlen(commands[i].name); + if (ERL_ATOM_SIZE(Func) != n) { + continue; + } + if (memcmp(ERL_ATOM_PTR(Func), commands[i].name, n) == 0) { + erl_free_term(Func); + if (ERL_TUPLE_SIZE(Args) != commands[i].num_args) { + fail("wrong number of arguments"); + } + commands[i].func(Args); + erl_free_term(Args); + goto outer_loop; + } + } + fail("bad command"); + } +} + +#define VERIFY_TYPE(Test, Term) \ +if (!Test(Term)) { \ + fail("wrong type for " #Term); \ +} else { \ +} + +static void +cmd_erl_connect(ETERM* args) +{ + ETERM* number; + ETERM* node; + ETERM* cookie; + + int res; + char buffer[256]; + + number = ERL_TUPLE_ELEMENT(args, 0); + VERIFY_TYPE(ERL_IS_INTEGER, number); + node = ERL_TUPLE_ELEMENT(args, 1); + VERIFY_TYPE(ERL_IS_ATOM, node); + cookie = ERL_TUPLE_ELEMENT(args, 2); + VERIFY_TYPE(ERL_IS_ATOM, cookie); + + if (ERL_ATOM_SIZE(cookie) == 0) { + res = erl_connect_init(ERL_INT_VALUE(number), 0, 0); + } else { + memcpy(buffer, ERL_ATOM_PTR(cookie), ERL_ATOM_SIZE(cookie)); + buffer[ERL_ATOM_SIZE(cookie)] = '\0'; + res = erl_connect_init(ERL_INT_VALUE(number), buffer, 0); + } + + if(!res) { + send_errno_result(res); + return; + } + + memcpy(buffer, ERL_ATOM_PTR(node), ERL_ATOM_SIZE(node)); + buffer[ERL_ATOM_SIZE(node)] = '\0'; + send_errno_result(erl_connect(buffer)); +} + +static void +cmd_erl_close_connection(ETERM* args) +{ + ETERM* number; + ETERM* res; + + number = ERL_TUPLE_ELEMENT(args, 0); + VERIFY_TYPE(ERL_IS_INTEGER, number); + res = erl_mk_int(erl_close_connection(ERL_INT_VALUE(number))); + send_term(res); + erl_free_term(res); +} + +static void +cmd_erl_global_register(ETERM* args) +{ + ETERM* fd_term = ERL_TUPLE_ELEMENT(args, 0); + ETERM* name = ERL_TUPLE_ELEMENT(args, 1); + ETERM* pid = erl_mk_pid(erl_thisnodename(), 14, 0, 0); + + char buffer[256]; + + VERIFY_TYPE(ERL_IS_INTEGER, fd_term); + VERIFY_TYPE(ERL_IS_ATOM, name); + + memcpy(buffer, ERL_ATOM_PTR(name), ERL_ATOM_SIZE(name)); + buffer[ERL_ATOM_SIZE(name)] = '\0'; + + send_errno_result(erl_global_register(ERL_INT_VALUE(fd_term), buffer, pid)); + erl_free_term(pid); +} + +static void +cmd_erl_global_whereis(ETERM* args) +{ + ETERM* fd_term = ERL_TUPLE_ELEMENT(args, 0); + ETERM* name = ERL_TUPLE_ELEMENT(args, 1); + ETERM* pid = NULL; + + char buffer[256]; + + VERIFY_TYPE(ERL_IS_INTEGER, fd_term); + VERIFY_TYPE(ERL_IS_ATOM, name); + + memcpy(buffer, ERL_ATOM_PTR(name), ERL_ATOM_SIZE(name)); + buffer[ERL_ATOM_SIZE(name)] = '\0'; + + pid = erl_global_whereis(ERL_INT_VALUE(fd_term), buffer, NULL); + send_term(pid); + erl_free_term(pid); +} + +static void +cmd_erl_global_names(ETERM* args) +{ + ETERM* fd_term = ERL_TUPLE_ELEMENT(args, 0); + + ETERM* res_array[2], *res_tuple, *name; + char** names = NULL; + int count = 0, i; + + VERIFY_TYPE(ERL_IS_INTEGER, fd_term); + + names = erl_global_names(ERL_INT_VALUE(fd_term), &count); + + res_array[0] = erl_mk_empty_list(); + for(i=0; i<count; i++) { + name = erl_mk_string(names[i]); + res_array[0] = erl_cons(name, res_array[0]); + } + + free(names); + + res_array[1] = erl_mk_int(count); + res_tuple = erl_mk_tuple(res_array, 2); + + send_term(res_tuple); + + erl_free_compound(res_array[0]); + erl_free_term(res_array[1]); + erl_free_term(res_tuple); +} + +static void +cmd_erl_global_unregister(ETERM* args) +{ + ETERM* fd_term = ERL_TUPLE_ELEMENT(args, 0); + ETERM* name = ERL_TUPLE_ELEMENT(args, 1); + + char buffer[256]; + + VERIFY_TYPE(ERL_IS_INTEGER, fd_term); + VERIFY_TYPE(ERL_IS_ATOM, name); + + memcpy(buffer, ERL_ATOM_PTR(name), ERL_ATOM_SIZE(name)); + buffer[ERL_ATOM_SIZE(name)] = '\0'; + + send_errno_result(erl_global_unregister(ERL_INT_VALUE(fd_term), buffer)); +} + +static void +send_errno_result(int value) +{ + ETERM* res_array[2]; + ETERM* res_tuple; + + res_array[0] = erl_mk_int(value); + res_array[1] = erl_mk_int(erl_errno); + res_tuple = erl_mk_tuple(res_array, 2); + send_term(res_tuple); + erl_free_term(res_array[0]); + erl_free_term(res_array[1]); + erl_free_term(res_tuple); +} diff --git a/lib/erl_interface/test/port_call_SUITE.erl b/lib/erl_interface/test/port_call_SUITE.erl index 895e29ad2e..2c550e4c0c 100644 --- a/lib/erl_interface/test/port_call_SUITE.erl +++ b/lib/erl_interface/test/port_call_SUITE.erl @@ -42,6 +42,8 @@ all(suite) -> basic(suite) -> []; basic(Config) when is_list(Config) -> case os:type() of + {unix, linux} -> + do_basic(Config); {unix, sunos} -> do_basic(Config); {win32,_} -> diff --git a/lib/hipe/cerl/erl_bif_types.erl b/lib/hipe/cerl/erl_bif_types.erl index af36eed79a..309c118107 100644 --- a/lib/hipe/cerl/erl_bif_types.erl +++ b/lib/hipe/cerl/erl_bif_types.erl @@ -48,6 +48,7 @@ t_boolean/0, t_byte/0, t_char/0, + t_charlist/0, t_cons/0, t_cons/2, t_cons_hd/1, @@ -124,7 +125,8 @@ t_tuple/1, t_tuple_args/1, t_tuple_size/1, - t_tuple_subtypes/1 + t_tuple_subtypes/1, + t_unicode_string/0 ]). -ifdef(DO_ERL_BIF_TYPES_TEST). @@ -3801,7 +3803,7 @@ arg_types(erlang, now, 0) -> arg_types(erlang, open_port, 2) -> [t_sup(t_atom(), t_sup([t_tuple([t_atom('spawn'), t_string()]), t_tuple([t_atom('spawn_driver'), t_string()]), - t_tuple([t_atom('spawn_executable'), t_string()]), + t_tuple([t_atom('spawn_executable'), t_sup(t_unicode_string(),t_binary())]), t_tuple([t_atom('fd'), t_integer(), t_integer()])])), t_list(t_sup(t_sup([t_atom('stream'), t_atom('exit_status'), @@ -3817,8 +3819,8 @@ arg_types(erlang, open_port, 2) -> t_tuple([t_atom('line'), t_integer()]), t_tuple([t_atom('cd'), t_string()]), t_tuple([t_atom('env'), t_list(t_tuple(2))]), % XXX: More - t_tuple([t_atom('args'), t_list(t_string())]), - t_tuple([t_atom('arg0'), t_string()])])))]; + t_tuple([t_atom('args'), t_list(t_sup(t_unicode_string(),t_binary()))]), + t_tuple([t_atom('arg0'),t_sup(t_unicode_string(),t_binary())])])))]; arg_types(erlang, phash, 2) -> [t_any(), t_pos_integer()]; arg_types(erlang, phash2, 1) -> @@ -4521,11 +4523,11 @@ arg_types(os, timestamp, 0) -> arg_types(re, compile, 1) -> [t_iodata()]; arg_types(re, compile, 2) -> - [t_iodata(), t_list(t_re_compile_option())]; + [t_sup(t_iodata(), t_charlist()), t_list(t_re_compile_option())]; arg_types(re, run, 2) -> - [t_iodata(), t_re_RE()]; + [t_sup(t_iodata(), t_charlist()), t_re_RE()]; arg_types(re, run, 3) -> - [t_iodata(), t_re_RE(), t_list(t_re_run_option())]; + [t_sup(t_iodata(), t_charlist()), t_re_RE(), t_list(t_re_run_option())]; %%------- string -------------------------------------------------------------- arg_types(string, chars, 2) -> [t_char(), t_non_neg_integer()]; @@ -4944,10 +4946,11 @@ t_matchres() -> %% From the 'ets' documentation %%----------------------------- %% Option = Type | Access | named_table | {keypos,Pos} -%% | {heir,pid(),HeirData} | {heir,none} -%% | {write_concurrency,boolean()} +%% | {heir,pid(),HeirData} | {heir,none} | Tweaks %% Type = set | ordered_set | bag | duplicate_bag %% Access = public | protected | private +%% Tweaks = {write_concurrency,boolean()} +%% | {read_concurrency,boolean()} | compressed %% Pos = integer() %% HeirData = term() t_ets_new_options() -> @@ -4959,10 +4962,12 @@ t_ets_new_options() -> t_atom('protected'), t_atom('private'), t_atom('named_table'), + t_tuple([t_atom('keypos'), t_integer()]), t_tuple([t_atom('heir'), t_pid(), t_any()]), t_tuple([t_atom('heir'), t_atom('none')]), - t_tuple([t_atom('keypos'), t_integer()]), - t_tuple([t_atom('write_concurrency'), t_boolean()])])). + t_tuple([t_atom('write_concurrency'), t_boolean()]), + t_tuple([t_atom('read_concurrency'), t_boolean()]), + t_atom('compressed')])). t_ets_info_items() -> t_sup([t_atom('fixed'), @@ -4982,8 +4987,7 @@ t_ets_info_items() -> %% ===================================================================== t_prim_file_name() -> - t_sup([t_string(), - t_binary()]). + t_sup(t_unicode_string(), t_binary()). %% ===================================================================== %% These are used for the built-in functions of 'gen_tcp' @@ -5140,13 +5144,14 @@ t_re_MP() -> %% it's supposed to be an opaque data type t_tuple([t_atom('re_pattern'), t_integer(), t_integer(), t_binary()]). t_re_RE() -> - t_sup(t_re_MP(), t_iodata()). + t_sup([t_re_MP(), t_iodata(), t_charlist()]). t_re_compile_option() -> - t_sup([t_atoms(['anchored', 'caseless', 'dollar_endonly', 'dotall', - 'extended', 'firstline', 'multiline', 'no_auto_capture', - 'dupnames', 'ungreedy']), - t_tuple([t_atom('newline'), t_re_NLSpec()])]). + t_sup([t_atoms(['unicode', 'anchored', 'caseless', 'dollar_endonly', + 'dotall', 'extended', 'firstline', 'multiline', + 'no_auto_capture', 'dupnames', 'ungreedy']), + t_tuple([t_atom('newline'), t_re_NLSpec()]), + t_atoms(['bsr_anycrlf', 'bsr_unicode'])]). t_re_run_option() -> t_sup([t_atoms(['anchored', 'global', 'notbol', 'noteol', 'notempty']), @@ -5163,7 +5168,7 @@ t_re_Type() -> t_atoms(['index', 'list', 'binary']). t_re_NLSpec() -> - t_atoms(['cr', 'crlf', 'lf', 'anycrlf']). + t_atoms(['cr', 'crlf', 'lf', 'anycrlf', 'any']). t_re_ValueSpec() -> t_sup(t_atoms(['all', 'all_but_first', 'first', 'none']), t_re_ValueList()). diff --git a/lib/hipe/cerl/erl_types.erl b/lib/hipe/cerl/erl_types.erl index 1ed85af172..080d6936b2 100644 --- a/lib/hipe/cerl/erl_types.erl +++ b/lib/hipe/cerl/erl_types.erl @@ -62,6 +62,7 @@ t_boolean/0, t_byte/0, t_char/0, + t_charlist/0, t_collect_vars/1, t_cons/0, t_cons/2, @@ -195,6 +196,7 @@ t_tuple_size/1, t_tuple_sizes/1, t_tuple_subtypes/1, + t_unicode_string/0, t_unify/2, t_unify/3, t_unit/0, @@ -1455,6 +1457,26 @@ t_is_tuple(_) -> false. %% Non-primitive types, including some handy syntactic sugar types %% +-spec t_unicode_string() -> erl_type(). + +t_unicode_string() -> + t_list(t_unicode_char()). + +-spec t_charlist() -> erl_type(). + +t_charlist() -> + t_charlist(1). + +-spec t_charlist(non_neg_integer()) -> erl_type(). + +t_charlist(N) when N > 0 -> + t_maybe_improper_list(t_sup([t_unicode_char(), + t_unicode_binary(), + t_charlist(N-1)]), + t_sup(t_unicode_binary(), t_nil())); +t_charlist(0) -> + t_maybe_improper_list(t_any(), t_sup(t_unicode_binary(), t_nil())). + -spec t_constant() -> erl_type(). t_constant() -> @@ -1549,6 +1571,16 @@ t_parameterized_module() -> t_timeout() -> t_sup(t_non_neg_integer(), t_atom('infinity')). +-spec t_unicode_binary() -> erl_type(). + +t_unicode_binary() -> + t_binary(). % with characters encoded in UTF-8 coding standard + +-spec t_unicode_char() -> erl_type(). + +t_unicode_char() -> + t_integer(). % representing a valid unicode codepoint + %%----------------------------------------------------------------------------- %% Some built-in opaque types %% @@ -2825,7 +2857,7 @@ t_subtract(?list(Contents1, Termination1, Size1) = T, true -> case {Size1, Size2} of {?nonempty_qual, ?unknown_qual} -> ?none; - {?unknown_qual, ?nonempty_qual} -> Termination1; + {?unknown_qual, ?nonempty_qual} -> ?nil; {S, S} -> ?none end; false -> diff --git a/lib/hipe/doc/src/notes.xml b/lib/hipe/doc/src/notes.xml index c188db483d..8c9dbc0c18 100644 --- a/lib/hipe/doc/src/notes.xml +++ b/lib/hipe/doc/src/notes.xml @@ -30,6 +30,24 @@ </header> <p>This document describes the changes made to HiPE.</p> +<section><title>Hipe 3.7.8.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Several type specifications for standard libraries were + wrong in the R14B01 release. This is now corrected. The + corrections concern types in re,io,filename and the + module erlang itself.</p> + <p> + Own Id: OTP-9008</p> + </item> + </list> + </section> + +</section> + <section><title>Hipe 3.7.8</title> <section><title>Improvements and New Features</title> diff --git a/lib/hipe/vsn.mk b/lib/hipe/vsn.mk index fa9dc91ff0..513b1f4943 100644 --- a/lib/hipe/vsn.mk +++ b/lib/hipe/vsn.mk @@ -1 +1 @@ -HIPE_VSN = 3.7.8 +HIPE_VSN = 3.7.8.1 diff --git a/lib/ic/doc/src/notes.xml b/lib/ic/doc/src/notes.xml index 6684547572..5f6c31069c 100644 --- a/lib/ic/doc/src/notes.xml +++ b/lib/ic/doc/src/notes.xml @@ -31,6 +31,26 @@ </header> <section> + <title>IC 4.2.26</title> + + <section> + <title>Improvements and New Features</title> + <list type="bulleted"> + <item> + <p> + Partial support for recursive structs and unions. Only available + for the erl_corba backend and requires that Light IFR is used. + I.e. the IC option {light_ifr, true} and that Orber is configured + in such a way that Light IFR is activated. Recursive TypeCode is + currently not supported.</p> + <p> + Own Id: OTP-8868 Aux Id: seq11633</p> + </item> + </list> + </section> + </section> + + <section> <title>IC 4.2.25</title> <section> diff --git a/lib/ic/src/ic_forms.erl b/lib/ic/src/ic_forms.erl index 7409ddeb7b..fc46a2ed40 100644 --- a/lib/ic/src/ic_forms.erl +++ b/lib/ic/src/ic_forms.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% Copyright Ericsson AB 1998-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 @@ -65,6 +65,7 @@ get_line(X) when is_record(X, scoped_id) -> X#scoped_id.line; get_line(X) when is_record(X, module) -> get_line(X#module.id); get_line(X) when is_record(X, interface) -> get_line(X#interface.id); get_line(X) when is_record(X, forward) -> get_line(X#forward.id); +get_line(X) when is_record(X, constr_forward) -> get_line(X#constr_forward.id); get_line(X) when is_record(X, const) -> get_line(X#const.id); get_line(X) when is_record(X, typedef) -> get_line(X#typedef.id); get_line(X) when is_record(X, struct) -> get_line(X#struct.id); @@ -114,6 +115,7 @@ get_line(_) -> -1. get_id2(X) when is_record(X, module) -> get_id(X#module.id); get_id2(X) when is_record(X, interface) -> get_id(X#interface.id); get_id2(X) when is_record(X, forward) -> get_id(X#forward.id); +get_id2(X) when is_record(X, constr_forward) -> get_id(X#constr_forward.id); get_id2(X) when is_record(X, const) -> get_id(X#const.id); get_id2(X) when is_record(X, typedef) -> get_id(hd(X#typedef.id)); get_id2(X) when is_record(X, struct) -> get_id(X#struct.id); @@ -156,6 +158,7 @@ get_type(X) when is_record(X, param) -> X#param.type. %% Temporary place get_tk(X) when is_record(X, interface) -> X#interface.tk; get_tk(X) when is_record(X, forward) -> X#forward.tk; +get_tk(X) when is_record(X, constr_forward) -> X#constr_forward.tk; get_tk(X) when is_record(X, const) -> X#const.tk; get_tk(X) when is_record(X, type_dcl) -> X#type_dcl.tk; get_tk(X) when is_record(X, typedef) -> X#typedef.tk; @@ -228,6 +231,7 @@ clean_up_scope([N|Ns],Found) -> get_type_code2(_, _, X) when is_record(X, interface) -> X#interface.tk; get_type_code2(_, _, X) when is_record(X, forward) -> X#forward.tk; +get_type_code2(_, _, X) when is_record(X, constr_forward) -> X#constr_forward.tk; get_type_code2(_, _, X) when is_record(X, const) -> X#const.tk; get_type_code2(_, _, X) when is_record(X, type_dcl) -> X#type_dcl.tk; get_type_code2(_, _, X) when is_record(X, typedef) -> diff --git a/lib/ic/src/ic_pragma.erl b/lib/ic/src/ic_pragma.erl index 9165e3b03b..45cb64c9c8 100644 --- a/lib/ic/src/ic_pragma.erl +++ b/lib/ic/src/ic_pragma.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% Copyright Ericsson AB 1998-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 @@ -60,7 +60,7 @@ pragma_reg(G,X) -> init_pragma_status(S), registerOptions(G,S), pragma_reg_all(G, S, [], X), - denote_specific_code_opts(G), %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + denote_specific_code_opts(G), case get_pragma_compilation_status(S) of true -> %% Remove ugly pragmas from form @@ -132,6 +132,7 @@ applyCodeOpt(G) -> %% This removes all pragma records from the form. %% When debugged, it can be enbodied in pragma_reg_all. +cleanup(undefined,C) -> C; cleanup([],C) -> C; cleanup([X|Xs],CSF) -> cleanup(Xs, CSF++cleanup(X)). @@ -279,7 +280,12 @@ pragma_reg(G, S, N, X) when is_record(X, union) -> pragma_reg(G, S, N, X) when is_record(X, struct) -> mk_ref(G,[get_id2(X) | N],struct_ref), mk_file_data(G,X,N,struct), - pragma_reg_all(G, S, N, X#struct.body); + case X#struct.body of + undefined -> + ok; + _ -> + pragma_reg_all(G, S, N, X#struct.body) + end; pragma_reg(G, _S, N, X) when is_record(X, attr) -> XX = #id_of{type=X}, diff --git a/lib/ic/src/ic_symtab.erl b/lib/ic/src/ic_symtab.erl index 889c75e3a2..d710154a5d 100644 --- a/lib/ic/src/ic_symtab.erl +++ b/lib/ic/src/ic_symtab.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% Copyright Ericsson AB 1998-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 @@ -69,6 +69,8 @@ store(G, N, X) -> ets:insert(G#genobj.symtab, {Name, X}); {ok, Y} when is_record(Y, forward) -> ets:insert(G#genobj.symtab, {Name, X}); + {ok, Y} when is_record(Y, constr_forward) -> + ets:insert(G#genobj.symtab, {Name, X}); {ok, _Y} -> ic_error:error(G, {multiply_defined, X}) end. diff --git a/lib/ic/src/icforms.hrl b/lib/ic/src/icforms.hrl index d1869e6330..1b394a11b4 100644 --- a/lib/ic/src/icforms.hrl +++ b/lib/ic/src/icforms.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-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 @@ -34,6 +34,7 @@ -record(module, {id, body}). -record(interface, {id, inherit, body, inherit_body, tk}). -record(forward, {id, tk}). +-record(constr_forward, {id, tk}). -record(const, {type, id, val, tk}). -record(type_dcl, {type, tk}). -record(typedef, {type, id, tk}). diff --git a/lib/ic/src/icparse.yrl b/lib/ic/src/icparse.yrl index 25b0f452e7..d0dd6cde4c 100644 --- a/lib/ic/src/icparse.yrl +++ b/lib/ic/src/icparse.yrl @@ -1,21 +1,20 @@ -%%<copyright> -%% <year>1997-2007</year> -%% <holder>Ericsson AB, All Rights Reserved</holder> -%%</copyright> -%%<legalnotice> +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1997-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. -%% -%% The Initial Developer of the Original Code is Ericsson AB. -%%</legalnotice> +%% +%% %CopyrightEnd% %% %%------------------------------------------------------------ %% Yecc spec for IDL @@ -150,6 +149,7 @@ Nonterminals 'ZorM_<integer_literal>' '<fixed_pt_type>' '<fixed_pt_const_type>' + '<constr_forward_decl>' . @@ -473,6 +473,7 @@ OE_preproc -> '#' '<integer_literal>' '<string_literal>' '<type_dcl>' -> '<struct_type>' : '$1' . '<type_dcl>' -> '<union_type>' : '$1' . '<type_dcl>' -> '<enum_type>' : '$1' . +'<type_dcl>' -> '<constr_forward_decl>' : '$1' . %% (28) NIY multiple declarators (FIXED) '<type_declarator>' -> '<type_spec>' '<declarators>' @@ -832,6 +833,9 @@ OE_preproc -> '#' '<integer_literal>' '<string_literal>' '<fixed_pt_type>' -> 'fixed' '<' '<positive_int_const>' ',' '<positive_int_const>' '>' : #fixed{digits='$3',scale='$5'} . +%% (99) +'<constr_forward_decl>' -> 'struct' '<identifier>' : #constr_forward{id='$2', tk=tk_struct} . +'<constr_forward_decl>' -> 'union' '<identifier>' : #constr_forward{id='$2', tk=tk_union} . %% Added clause 'ZorM_<string_literal>' -> '$empty' : [] . diff --git a/lib/ic/src/ictype.erl b/lib/ic/src/ictype.erl index 4704191bee..9e20801464 100644 --- a/lib/ic/src/ictype.erl +++ b/lib/ic/src/ictype.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-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 @@ -407,6 +407,18 @@ check(G, S, N, X) when is_record(X, forward) -> tktab_add(G, S, N, X, {tk_objref, ictk:get_IR_ID(G, N, X), ic_forms:get_id2(X)}), X; +check(G, S, N, #constr_forward{tk = tk_struct} = X) -> + ?STDDBG, + ID = ic_forms:get_id2(X), + Module = list_to_atom(string:join(lists:reverse([ID|N]), "_")), + tktab_add(G, S, N, X, {tk_struct, ictk:get_IR_ID(G, N, X), ID, Module}), + X; +check(G, S, N, #constr_forward{tk = tk_union} = X) -> + ?STDDBG, + ID = ic_forms:get_id2(X), + Module = list_to_atom(string:join(lists:reverse([ID|N]), "_")), + tktab_add(G, S, N, X, {tk_union, ictk:get_IR_ID(G, N, X), ID, [], [], Module}), + X; check(G, S, N, X) when is_record(X, const) -> ?STDDBG, @@ -427,21 +439,6 @@ check(G, S, N, X) when is_record(X, const) -> end end; -check(G, S, N, X) when is_record(X, const) -> - ?STDDBG, - case tk_base(G, S, N, ic_forms:get_type(X)) of - Err when element(1, Err) == error -> X; - TK -> - check_const_tk(G, S, N, X, TK), - case iceval:eval_const(G, S, N, TK, X#const.val) of - Err when element(1, Err) == error -> X; - Val -> - V = iceval:get_val(Val), - tktab_add(G, S, N, X, TK, V), - X#const{val=V, tk=TK} - end - end; - check(G, S, N, X) when is_record(X, except) -> ?STDDBG, TK = tk(G, S, N, X), @@ -795,9 +792,15 @@ tktab_add_id(G, S, N, X, Id, TK, Aux) -> Name = [Id | N], UName = mk_uppercase(Name), case ets:lookup(S, Name) of - [{_, forward, _, _}] when is_record(X, interface) -> ok; - [XX] when is_record(X, forward) andalso element(2, XX)==interface -> ok; - [_] -> ic_error:error(G, {multiply_defined, X}); + [{_, forward, _, _}] when is_record(X, interface) -> + ok; + [{_, constr_forward, _, _}] when is_record(X, union) orelse + is_record(X, struct) -> + ok; + [XX] when is_record(X, forward) andalso element(2, XX)==interface -> + ok; + [_] -> + ic_error:error(G, {multiply_defined, X}); [] -> case ets:lookup(S, UName) of [] -> ok; diff --git a/lib/ic/vsn.mk b/lib/ic/vsn.mk index 074d0b3d39..6d6c7fa625 100644 --- a/lib/ic/vsn.mk +++ b/lib/ic/vsn.mk @@ -1 +1 @@ -IC_VSN = 4.2.25 +IC_VSN = 4.2.26 diff --git a/lib/inets/doc/src/http_server.xml b/lib/inets/doc/src/http_server.xml index 68dfd1add0..47ed9cd229 100644 --- a/lib/inets/doc/src/http_server.xml +++ b/lib/inets/doc/src/http_server.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE chapter SYSTEM "chapter.dtd"> <chapter> @@ -766,7 +766,7 @@ http://your.server.org/eval?httpd_example:print(atom_to_list(apply(erlang,halt,[ <code> -module(mnesia_test). -export([start/0,load_data/0]). --include("mod_auth.hrl"). +-include_lib("mod_auth.hrl"). first_start() -> mnesia:create_schema([node()]), diff --git a/lib/inets/include/mod_auth.hrl b/lib/inets/include/mod_auth.hrl new file mode 100644 index 0000000000..cf931e681a --- /dev/null +++ b/lib/inets/include/mod_auth.hrl @@ -0,0 +1,33 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1998-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% +%% +%% + +-ifndef(mod_auth_hrl). +-define(mod_auth_hrl, true). + +-record(httpd_user, + {username, + password, + user_data}). + +-record(httpd_group, + {name, + userlist}). + +-endif. % -ifdef(mod_auth_hrl). diff --git a/lib/inets/src/http_server/Makefile b/lib/inets/src/http_server/Makefile index bdd8c5ee3c..55cc68dede 100644 --- a/lib/inets/src/http_server/Makefile +++ b/lib/inets/src/http_server/Makefile @@ -82,9 +82,7 @@ MODULES = \ mod_security \ mod_security_server -INCLUDE = ../../include - -HRL_FILES = $(INCLUDE)/httpd.hrl httpd_internal.hrl mod_auth.hrl +HRL_FILES = httpd.hrl httpd_internal.hrl mod_auth.hrl ERL_FILES = $(MODULES:%=%.erl) @@ -100,7 +98,6 @@ include ../inets_app/inets.mk ERL_COMPILE_FLAGS += \ $(INETS_FLAGS) \ $(INETS_ERL_COMPILE_FLAGS) \ - -I$(INCLUDE) \ -I../inets_app \ -I../http_lib \ diff --git a/lib/inets/src/http_server/httpd.hrl b/lib/inets/src/http_server/httpd.hrl new file mode 100644 index 0000000000..4eba833e2c --- /dev/null +++ b/lib/inets/src/http_server/httpd.hrl @@ -0,0 +1,27 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1997-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% +%% +%% This is a simple wrapper for code that has not been updated to +%% handle the move of this file to the include dir. + +-ifndef(src_httpd_hrl). +-define(src_httpd_hrl, true). + +-include_lib("inets/include/httpd.hrl"). + +-endif. % -ifdef(src_httpd_hrl). diff --git a/lib/inets/src/http_server/mod_auth.hrl b/lib/inets/src/http_server/mod_auth.hrl index 9b316cecc4..674e6d1652 100644 --- a/lib/inets/src/http_server/mod_auth.hrl +++ b/lib/inets/src/http_server/mod_auth.hrl @@ -1,29 +1,27 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1997-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% %% +%% %CopyrightEnd% %% +%% This is a simple wrapper for code that has not been updated to +%% handle the move of this file to the include dir. + +-ifndef(src_mod_auth_hrl). +-define(src_mod_auth_hrl, true). --record(httpd_user, - {username, - password, - user_data}). +-include_lib("inets/include/mod_auth.hrl"). --record(httpd_group, - {name, - userlist}). - +-endif. % -ifdef(src_mod_auth_hrl). diff --git a/lib/inets/src/inets_app/Makefile b/lib/inets/src/inets_app/Makefile index 4632ff3b68..20e22917e2 100644 --- a/lib/inets/src/inets_app/Makefile +++ b/lib/inets/src/inets_app/Makefile @@ -47,7 +47,9 @@ MODULES = \ inets_sup \ inets_regexp -HRL_FILES = inets_internal.hrl +INTERNAL_HRL_FILES = inets_internal.hrl +EXTERNAL_HRL_FILES = ../../include/httpd.hrl \ + ../../include/mod_auth.hrl ERL_FILES = $(MODULES:%=%.erl) @@ -74,8 +76,7 @@ include inets.mk ERL_COMPILE_FLAGS += \ $(INETS_FLAGS) \ - $(INETS_ERL_COMPILE_FLAGS) \ - -I../../include + $(INETS_ERL_COMPILE_FLAGS) # ---------------------------------------------------- @@ -110,7 +111,9 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt $(INSTALL_DIR) $(RELSYSDIR)/src $(INSTALL_DIR) $(RELSYSDIR)/src/inets_app - $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src/inets_app + $(INSTALL_DATA) $(INTERNAL_HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src/inets_app + $(INSTALL_DIR) $(RELSYSDIR)/include + $(INSTALL_DATA) $(EXTERNAL_HRL_FILES) $(RELSYSDIR)/include $(INSTALL_DIR) $(RELSYSDIR)/ebin $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk index f462290a99..67737ee552 100644 --- a/lib/inets/vsn.mk +++ b/lib/inets/vsn.mk @@ -18,7 +18,7 @@ # %CopyrightEnd% APPLICATION = inets -INETS_VSN = 5.5.1 +INETS_VSN = 5.5.2 PRE_VSN = APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)" diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractConnection.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractConnection.java index ab0b299bf9..9ba6a4a0ab 100644 --- a/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractConnection.java +++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractConnection.java @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2000-2009. All Rights Reserved. + * 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 @@ -68,7 +68,6 @@ public abstract class AbstractConnection extends Thread { protected static final int sendTag = 2; protected static final int exitTag = 3; protected static final int unlinkTag = 4; - protected static final int nodeLinkTag = 5; protected static final int regSendTag = 6; protected static final int groupLeaderTag = 7; protected static final int exit2Tag = 8; @@ -697,7 +696,6 @@ public abstract class AbstractConnection extends Thread { // absolutely no idea what to do with these, so we ignore // them... case groupLeaderTag: // { GROUPLEADER, FromPid, ToPid} - case nodeLinkTag: // { NODELINK } // (just show trace) if (traceLevel >= ctrlThreshold) { System.out.println("<- " + headerType(head) + " " @@ -880,9 +878,6 @@ public abstract class AbstractConnection extends Thread { case unlinkTag: return "UNLINK"; - case nodeLinkTag: - return "NODELINK"; - case regSendTag: return "REG_SEND"; diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMsg.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMsg.java index 80d8a5ccae..6f507bf4bb 100644 --- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMsg.java +++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMsg.java @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2000-2009. All Rights Reserved. + * 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 @@ -54,7 +54,6 @@ public class OtpMsg { public static final int sendTag = 2; public static final int exitTag = 3; public static final int unlinkTag = 4; - /* public static final int nodeLinkTag = 5; */ public static final int regSendTag = 6; /* public static final int groupLeaderTag = 7; */ public static final int exit2Tag = 8; diff --git a/lib/kernel/doc/src/file.xml b/lib/kernel/doc/src/file.xml index d3441d3623..36fce464c5 100644 --- a/lib/kernel/doc/src/file.xml +++ b/lib/kernel/doc/src/file.xml @@ -660,10 +660,10 @@ f.txt: {person, "kalle", 25}. </func> <func> <name>native_name_encoding() -> latin1 | utf8</name> - <fsummary>Retunr the VMs configure filename encoding.</fsummary> + <fsummary>Return the VM's configured filename encoding.</fsummary> <desc> <p>This function returns the configured default file name encoding to use for raw file names. Generally an application supplying file names raw (as binaries), should obey the character encoding returned by this function.</p> - <p>By default, the VM uses ISO-latin-1 file name encoding on filesystems and/or OSes that use completely transparent file naming. This includes all Unix versions except for MacOSX, where the vfs layer enforces UTF-8 file naming. By giving the experimental option <c>+fnu</c> when starting Erlang, UTF-8 translation of file names can be turned on even for those systems. If Unicode file name translation is in effect, the system behaves as usual as long as file names conform to the encoding, but will return file names that are not properly encoded in UTF-8 as raw file names (i.e. binaries).</p> + <p>By default, the VM uses ISO-latin-1 file name encoding on filesystems and/or OSes that use completely transparent file naming. This includes all Unix versions except MacOSX, where the vfs layer enforces UTF-8 file naming. By giving the experimental option <c>+fnu</c> when starting Erlang, UTF-8 translation of file names can be turned on even for those systems. If Unicode file name translation is in effect, the system behaves as usual as long as file names conform to the encoding, but will return file names that are not properly encoded in UTF-8 as raw file names (i.e. binaries).</p> <p>On Windows, this function also returns <c>utf8</c> by default. The OS uses a pure Unicode naming scheme and file names are always possible to interpret as valid Unicode. The fact that the underlying Windows OS actually encodes file names using little endian UTF-16 can be ignored by the Erlang programmer. Windows and MacOSX are the only operating systems where the VM operates in Unicode file name mode by default.</p> </desc> </func> diff --git a/lib/kernel/test/erl_distribution_SUITE.erl b/lib/kernel/test/erl_distribution_SUITE.erl index 21a96f804a..a215ec3608 100644 --- a/lib/kernel/test/erl_distribution_SUITE.erl +++ b/lib/kernel/test/erl_distribution_SUITE.erl @@ -845,13 +845,16 @@ monitor_nodes_otp_6481_test(Config, TestType) when is_list(Config) -> ?line {ok, Node} = start_node(Name, "", this), ?line receive {nodeup, Node} -> ok end, - ?line spawn(Node, + ?line RemotePid = spawn(Node, fun () -> - receive after 1000 -> ok end, - lists:foreach(fun (No) -> - Me ! {NodeMsg, No} - end, - Seq), + receive after 1500 -> ok end, + % infinit loop of msgs + % we want an endless stream of messages and the kill + % the node mercilessly. + % We then want to ensure that the nodedown message arrives + % last ... without garbage after it. + Pid = spawn(fun() -> node_loop_send(Me, NodeMsg, 1) end), + receive {Me, kill_it} -> ok end, halt() end), @@ -860,9 +863,11 @@ monitor_nodes_otp_6481_test(Config, TestType) when is_list(Config) -> %% Verify that '{nodeup, Node}' comes before '{NodeMsg, 1}' (the message %% bringing up the connection). - %%?line no_msgs(500), % Why wait? It fails test sometimes /sverker + ?line no_msgs(500), ?line {nodeup, Node} = receive Msg1 -> Msg1 end, - ?line {NodeMsg, 1} = receive Msg2 -> Msg2 end, + ?line {NodeMsg, 1} = receive Msg2 -> Msg2 end, + % msg stream has begun, kill the node + ?line RemotePid ! {self(), kill_it}, %% Verify that '{nodedown, Node}' comes after the last '{NodeMsg, N}' %% message. @@ -883,6 +888,10 @@ flush_node_msgs(NodeMsg, No) -> OtherMsg -> OtherMsg end. +node_loop_send(Pid, Msg, No) -> + Pid ! {Msg, No}, + node_loop_send(Pid, Msg, No + 1). + monitor_nodes_errors(doc) -> []; monitor_nodes_errors(suite) -> diff --git a/lib/kernel/test/file_name_SUITE.erl b/lib/kernel/test/file_name_SUITE.erl index fea4df8539..fbafbcd9b7 100644 --- a/lib/kernel/test/file_name_SUITE.erl +++ b/lib/kernel/test/file_name_SUITE.erl @@ -507,8 +507,16 @@ check_very_icky(Mod) -> end, ?line {NumOK,NumNOK} = filelib:fold_files(".",".*",true,fun(_F,{N,M}) when is_list(_F) -> io:format("~ts~n",[_F]),{N+1,M}; (_F,{N,M}) -> io:format("~p~n",[_F]),{N,M+1} end,{0,0}), ?line ok = filelib:fold_files(".",[1076,1089,1072,124,46,42],true,fun(_F,_) -> ok end,false), - ?line SF3 = unicode:characters_to_binary("���subfil3",file:native_name_encoding()), - ?line Sorted = lists:sort([SF3,<<"���subfil2">>]), + ?line SF3 = unicode:characters_to_binary("���subfil3", + file:native_name_encoding()), + ?line SF2 = case treat_icky(<<"���subfil2">>) of + LF2 when is_list(LF2) -> + unicode:characters_to_binary(LF2, + file:native_name_encoding()); + BF2 -> + BF2 + end, + ?line Sorted = lists:sort([SF3,SF2]), ?line Sorted = lists:sort(filelib:wildcard("*",<<"���subdir2">>)), ok catch diff --git a/lib/orber/doc/src/ch_idl_to_erlang_mapping.xml b/lib/orber/doc/src/ch_idl_to_erlang_mapping.xml index a97ad65f0e..964ae3e92d 100644 --- a/lib/orber/doc/src/ch_idl_to_erlang_mapping.xml +++ b/lib/orber/doc/src/ch_idl_to_erlang_mapping.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>1997</year><year>2009</year> + <year>1997</year><year>2010</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -445,7 +445,19 @@ void op(in myEnum a);</cell> <section> <title>Struct Data Type</title> <p>A <c>struct</c> may have Basic, Template, Scoped Names and Constructed - types as members.</p> + types as members. By using forward declaration we can define a recursive struct:</p> + <code type="none"><![CDATA[ +struct myStruct; // Forward declaration +typedef sequence<myStruct> myStructSeq; +struct myStruct { + myStructSeq chain; +}; + +// Deprecated definition (anonymous) not supported by IC +struct myStruct { + sequence<myStruct> chain; +}; + ]]></code> </section> <section> @@ -510,6 +522,25 @@ union LongUnion2 switch(long) { default: boolean DefaultValue; }; </code> + <p>In the same way as structs, unions can be recursive if forward + declaration is used (anonymous types is deprecated and not supported):</p> + <code type="none"><![CDATA[ +// Forward declaration +union myUnion; +typedef sequence<myUnion>myUnionSeq; +union myUnion switch (long) { + case 1 : myUnionSeq chain; + default: boolean DefaultValue; +}; + ]]></code> + + <note> + <p>Recursive types (union and struct) require Light IFR. I.e. the + IC option {light_ifr, true} is used and that Orber is configured in such a way that + Light IFR is activated. Recursive TypeCode is currently not supported, which is + why these cannot be encapsulated in an any data type.</p> + </note> + </section> <warning> <p>Every field in, for example, a struct must be initiated. Otherwise @@ -890,7 +921,7 @@ attribute long RWAttribute; object internal state with its object reference. The object internal state is an Erlang term which has a format defined by the user.</p> <note> - <p>It is is not always the case that the internal state will be the first parameter, as stubs can use their own object reference as the first parameter (see the IC documentation).</p> + <p>It is not always the case that the internal state will be the first parameter, as stubs can use their own object reference as the first parameter (see the IC documentation).</p> </note> <p>A function call will invoke an operation. The first parameter of the function should be the object reference and then diff --git a/lib/orber/doc/src/notes.xml b/lib/orber/doc/src/notes.xml index 6eda16a517..ba16682f0b 100644 --- a/lib/orber/doc/src/notes.xml +++ b/lib/orber/doc/src/notes.xml @@ -33,6 +33,36 @@ </header> <section> + <title>Orber 3.6.19</title> + + <section> + <title>Improvements and New Features</title> + <list type="bulleted"> + <item> + <p> + Partial support for recursive structs and unions. + Only available for the erl_corba backend and requires + that Light IFR is used. I.e. the IC option {light_ifr, true} + and that Orber is configured in such a way that Light IFR + is activated. Recursive TypeCode is currently not supported.</p> + <p> + Own Id: OTP-8868 Aux Id: seq11633</p> + </item> + </list> + </section> + <section> + <title>Fixed Bugs and Malfunctions</title> + <list type="bulleted"> + <item> + <p>The SSL option {ssl_imp, old} was not used if ssl_generation was + set to 2. Only R14B was affected by this.</p> + <p>Own Id: OTP-8994 Aux Id: seq11747</p> + </item> + </list> + </section> + </section> + + <section> <title>Orber 3.6.18</title> <section> <title>Fixed Bugs and Malfunctions</title> diff --git a/lib/orber/src/cdr_decode.erl b/lib/orber/src/cdr_decode.erl index 9d30098940..36ef6ce02f 100644 --- a/lib/orber/src/cdr_decode.erl +++ b/lib/orber/src/cdr_decode.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-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 @@ -898,9 +898,13 @@ dec_sequence_struct(Version, Message, N, TypeCodeList, Len, ByteOrder, Buff, C, {Seq, Rest2, Len2, NewC2} = dec_sequence_struct(Version, Rest1, N - 1, TypeCodeList, Len1, ByteOrder, Buff, NewC, Name), {[list_to_tuple([Name |Struct]) | Seq], Rest2, Len2, NewC2}. -dec_sequence_union(_, Message, 0, _DiscrTC, _Default, _ElementList, Len, _ByteOrder, _Buff, C, _Name) -> + + +dec_sequence_union(_, Message, 0, _DiscrTC, _Default, _ElementList, + Len, _ByteOrder, _Buff, C, _Name) -> {[], Message, Len, C}; -dec_sequence_union(Version, Message, N, DiscrTC, Default, ElementList, Len, ByteOrder, Buff, C, Name) -> +dec_sequence_union(Version, Message, N, DiscrTC, Default, ElementList, + Len, ByteOrder, Buff, C, Name) when is_list(ElementList) -> {Label, Rest1, Len1, NewC} = dec_type(DiscrTC, Version, Message, Len, ByteOrder, Buff, C), Result = dec_union(Version, stringify_enum(DiscrTC, Label), ElementList, Default, @@ -916,7 +920,20 @@ dec_sequence_union(Version, Message, N, DiscrTC, Default, ElementList, Len, Byte DiscrTC, Default, ElementList, Len2, ByteOrder, Buff, NewC3, Name), - {[{Name, Label, Value} | Seq], Rest3, Len3, NewC4}. + {[{Name, Label, Value} | Seq], Rest3, Len3, NewC4}; +dec_sequence_union(Version, Message, N, _DiscrTC, _Default, Module, + Len, ByteOrder, Buff, C, Name) when is_atom(Module) -> + case catch Module:tc() of + {tk_union, _, _, DiscrTC, Default, ElementList} -> + dec_sequence_union(Version, Message, N, DiscrTC, Default, ElementList, + Len, ByteOrder, Buff, C, Name); + What -> + orber:dbg("[~p] ~p:dec_sequence_union(~p). Union module doesn't exist or incorrect.", + [?LINE, ?MODULE, What], ?DEBUG_LEVEL), + corba:raise(#'MARSHAL'{completion_status=?COMPLETED_MAYBE}) + end. + + %% A special case; when something is encapsulated (i.e. sent as octet-sequence) %% we sometimes don not want the result to be converted to a list. @@ -993,14 +1010,16 @@ dec_wstring(Version, Message, Len, ByteOrder, Buff, C) -> %% Func: dec_union/9 %%----------------------------------------------------------------- %% ## NEW IIOP 1.2 ## -dec_union(Version, ?SYSTEM_TYPE, Name, DiscrTC, Default, ElementList, Bytes, Len, ByteOrder, Buff, C) -> +dec_union(Version, ?SYSTEM_TYPE, Name, DiscrTC, Default, ElementList, Bytes, + Len, ByteOrder, Buff, C) -> {Label, Rest1, Len1, NewC} = dec_type(DiscrTC, Version, Bytes, Len, ByteOrder, Buff, C), {Value, Rest2, Len2, NewC3} = dec_union(Version, Label, ElementList, Default, Rest1, Len1, ByteOrder, Buff, NewC), {{Name, Label, Value}, Rest2, Len2, NewC3}; -dec_union(Version, IFRId, _, DiscrTC, Default, ElementList, Bytes, Len, ByteOrder, Buff, C) -> +dec_union(Version, IFRId, _, DiscrTC, Default, ElementList, Bytes, Len, + ByteOrder, Buff, C) when is_list(ElementList) -> {Label, Rest1, Len1, NewC} = dec_type(DiscrTC, Version, Bytes, Len, ByteOrder, Buff, C), Result = dec_union(Version, stringify_enum(DiscrTC, Label), ElementList, Default, Rest1, Len1, ByteOrder, Buff, NewC), @@ -1012,7 +1031,20 @@ dec_union(Version, IFRId, _, DiscrTC, Default, ElementList, Bytes, Len, ByteOrde X end, Name = ifrid_to_name(IFRId, ?IFR_UnionDef), - {{Name, Label, Value}, Rest2, Len2, NewC3}. + {{Name, Label, Value}, Rest2, Len2, NewC3}; +dec_union(Version, IFRId, _, _DiscrTC, _Default, Module, Bytes, Len, + ByteOrder, Buff, C) when is_atom(Module) -> + case catch Module:tc() of + {tk_union, _, Name, DiscrTC, Default, ElementList} -> + dec_union(Version, IFRId, Name, DiscrTC, Default, ElementList, Bytes, Len, + ByteOrder, Buff, C); + What -> + orber:dbg("[~p] ~p:dec_union(~p). Union module doesn't exist or incorrect.", + [?LINE, ?MODULE, What], ?DEBUG_LEVEL), + corba:raise(#'MARSHAL'{completion_status=?COMPLETED_MAYBE}) + end. + + dec_union(_, _, [], Default, Message, Len, _, _Buff, C) when Default < 0 -> {undefined, Message, Len, C}; @@ -1047,7 +1079,16 @@ dec_struct1(_, [], Message, Len, _ByteOrder, _, C) -> dec_struct1(Version, [{_ElemName, ElemType} | TypeCodeList], Message, Len, ByteOrder, Buff, C) -> {Element, Rest, Len1, NewC} = dec_type(ElemType, Version, Message, Len, ByteOrder, Buff, C), {Struct, Rest1, Len2, NewC2} = dec_struct1(Version, TypeCodeList, Rest, Len1, ByteOrder, Buff, NewC), - {[Element |Struct], Rest1, Len2, NewC2}. + {[Element |Struct], Rest1, Len2, NewC2}; +dec_struct1(Version, Module, Message, Len, ByteOrder, Buff, C) -> + case catch Module:tc() of + {tk_struct, _, _, TypeCodeList} -> + dec_struct1(Version, TypeCodeList, Message, Len, ByteOrder, Buff, C); + What -> + orber:dbg("[~p] ~p:dec_struct1(~p). Struct module doesn't exist or incorrect.", + [?LINE, ?MODULE, What], ?DEBUG_LEVEL), + corba:raise(#'MARSHAL'{completion_status=?COMPLETED_MAYBE}) + end. ifrid_to_name([], Type) -> orber:dbg("[~p] ~p:ifrid_to_name([], ~p). No Id supplied.", @@ -1232,7 +1273,9 @@ get_user_exception_type(TypeId) -> %%----------------------------------------------------------------- dec_type_code(Version, Message, Len, ByteOrder, Buff, C) -> {TypeNo, Message1, Len1, NewC} = dec_type('tk_ulong', Version, Message, Len, ByteOrder, Buff, C), - dec_type_code(TypeNo, Version, Message1, Len1, ByteOrder, Buff, NewC). + TC = dec_type_code(TypeNo, Version, Message1, Len1, ByteOrder, Buff, NewC), + erase(orber_indirection), + TC. %%----------------------------------------------------------------- %% Func: dec_type_code/5 @@ -1441,13 +1484,22 @@ dec_type_code(33, Version, Message, Len, ByteOrder, Buff, C) -> {"name", {'tk_string', 0}}]}, Version, Rest1, 1, ByteOrder1, Buff, C+1+Ex), {{'tk_local_interface', RepId, Name}, Message1, Len1, NewC}; -dec_type_code(16#ffffffff, Version, Message, Len, ByteOrder, Buff, C) -> %% placeholder +dec_type_code(16#ffffffff, Version, Message, Len, ByteOrder, Buff, C) -> {Indirection, Message1, Len1, NewC} = dec_type('tk_long', Version, Message, Len, ByteOrder, Buff, C), Position = C+Indirection, - <<_:Position/binary, SubBuff/binary>> = Buff, - {TC, _, _, _} = dec_type_code(Version, SubBuff, Position, ByteOrder, Buff, Position), - {TC, Message1, Len1, NewC}; + case put(orber_indirection, Position) of + Position -> +%% {{'none', Indirection}, Message1, Len1, NewC}; + %% Recursive TypeCode. Break the loop. + orber:dbg("[~p] cdr_decode:dec_type_code(~p); Recursive TC not supported.", + [?LINE,Position], ?DEBUG_LEVEL), + corba:raise(#'MARSHAL'{completion_status=?COMPLETED_NO}); + _ -> + <<_:Position/binary, SubBuff/binary>> = Buff, + {TC, _, _, _} = dec_type_code(Version, SubBuff, Position, ByteOrder, Buff, Position), + {TC, Message1, Len1, NewC} + end; dec_type_code(Type, _, _, _, _, _, _) -> orber:dbg("[~p] cdr_decode:dec_type_code(~p); No match.", [?LINE, Type], ?DEBUG_LEVEL), diff --git a/lib/orber/src/cdr_encode.erl b/lib/orber/src/cdr_encode.erl index 3ecb8833f5..eaf3c5b7dc 100644 --- a/lib/orber/src/cdr_encode.erl +++ b/lib/orber/src/cdr_encode.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-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 @@ -815,11 +815,21 @@ enc_wstring(Env, String, MaxLength, Bytes, Len) -> %%----------------------------------------------------------------- %% Func: enc_union/5 %%----------------------------------------------------------------- -enc_union(Env, {_, Label, Value}, DiscrTC, Default, TypeCodeList, Bytes, Len) -> +enc_union(Env, {_, Label, Value}, DiscrTC, Default, TypeCodeList, + Bytes, Len) when is_list(TypeCodeList) -> {ByteSequence, Len1} = enc_type(DiscrTC, Env, Label, Bytes, Len), Label2 = stringify_enum(DiscrTC,Label), enc_union2(Env, {Label2, Value},TypeCodeList, Default, - ByteSequence, Len1, undefined). + ByteSequence, Len1, undefined); +enc_union(Env, Value, _DiscrTC, _Default, Module, Bytes, Len) when is_atom(Module) -> + case catch Module:tc() of + {tk_union, _, _, DiscrTC, Default, ElementList} -> + enc_union(Env, Value, DiscrTC, Default, ElementList, Bytes, Len); + What -> + orber:dbg("[~p] ~p:enc_union(~p). Union module doesn't exist or incorrect.", + [?LINE, ?MODULE, What], ?DEBUG_LEVEL), + corba:raise(#'MARSHAL'{completion_status=?COMPLETED_MAYBE}) + end. enc_union2(_Env, _What, [], Default, Bytes, Len, _) when Default < 0 -> {Bytes, Len}; @@ -840,9 +850,19 @@ stringify_enum(_, Label) -> %%----------------------------------------------------------------- %% Func: enc_struct/4 %%----------------------------------------------------------------- -enc_struct(Env, Struct, TypeCodeList, Bytes, Len) -> +enc_struct(Env, Struct, TypeCodeList, Bytes, Len) when is_list(TypeCodeList) -> [_Name | StructList] = tuple_to_list(Struct), - enc_struct1(Env, StructList, TypeCodeList, Bytes, Len). + enc_struct1(Env, StructList, TypeCodeList, Bytes, Len); +enc_struct(Env, Struct, Module, Bytes, Len) -> + [Module | StructList] = tuple_to_list(Struct), + case catch Module:tc() of + {tk_struct, _, _, TypeCodeList} -> + enc_struct1(Env, StructList, TypeCodeList, Bytes, Len); + What -> + orber:dbg("[~p] ~p:enc_struct([], ~p). Struct module doesn't exist or incorrect.", + [?LINE, ?MODULE, What], ?DEBUG_LEVEL), + corba:raise(#'MARSHAL'{completion_status=?COMPLETED_MAYBE}) + end. enc_struct1(_Env, [], [], Bytes, Len) -> {Bytes, Len}; diff --git a/lib/orber/src/orber_socket.erl b/lib/orber/src/orber_socket.erl index af6df01b7d..84ed193ebb 100644 --- a/lib/orber/src/orber_socket.erl +++ b/lib/orber/src/orber_socket.erl @@ -496,27 +496,17 @@ check_port(Port, _, _) -> %%----------------------------------------------------------------- %% Check Options. -%% We need this as a work-around since the SSL-app doesn't allow us -%% to pass 'inet' as an option. Also needed for R9B :-( check_options(normal, Options, _Generation) -> - case orber:ip_version() of - inet -> - Options; - inet6 -> - %% Necessary for R9B. Should be [orber:ip_version()|Options]; - [inet6|Options] - end; + [orber:ip_version()|Options]; check_options(ssl, Options, Generation) -> case orber:ip_version() of inet when Generation > 2 -> [{ssl_imp, new}|Options]; inet -> - Options; + [{ssl_imp, old}|Options]; inet6 when Generation > 2 -> [{ssl_imp, new}, inet6|Options]; inet6 -> - %% Will fail until SSL supports this option. - %% Note, we want this happen! - [inet6|Options] + [{ssl_imp, old}, inet6|Options] end. diff --git a/lib/orber/test/Makefile b/lib/orber/test/Makefile index 4601e84d2c..5495735318 100644 --- a/lib/orber/test/Makefile +++ b/lib/orber/test/Makefile @@ -120,7 +120,11 @@ GEN_MOD_TEST_SERVER = \ orber_test_server_uni \ orber_test_server_uni_d \ orber_test_timeout_server \ - orber_parent_inherrit + orber_parent_inherrit \ + orber_test_server_rec_struct \ + orber_test_server_rec_struct_seq \ + orber_test_server_rec_union \ + orber_test_server_rec_union_seq GEN_HRL_TEST_SERVER = \ oe_orber_test_server.hrl \ diff --git a/lib/orber/test/orber_test_lib.erl b/lib/orber/test/orber_test_lib.erl index a694dc58c4..b95cf4b0ec 100644 --- a/lib/orber/test/orber_test_lib.erl +++ b/lib/orber/test/orber_test_lib.erl @@ -1280,6 +1280,22 @@ test_coding(Obj, Local) -> ?match({'EXCEPTION',{'MARSHAL',_,_,_}}, orber_test_server: testing_iiop_server_marshal(Obj, "string")), + + RecS = #orber_test_server_rec_struct{chain = [#orber_test_server_rec_struct{chain = []}]}, + ?match(RecS, orber_test_server:testing_iiop_rec_struct(Obj, RecS)), + + RecU = #orber_test_server_rec_union{label = 'RecursiveType', + value = [#orber_test_server_rec_union{label = 'RecursiveType', + value = []}]}, + ?match(RecU, orber_test_server:testing_iiop_rec_union(Obj, RecU)), + +%% RecA1 = #any{typecode = unsupported, value = RecS}, +%% RecA2 = #any{typecode = unsupported, value = RecU}, +%% ?match(RecA1, +%% orber_test_server:testing_iiop_rec_any(Obj, RecA1)), +%% ?match(RecA2, +%% orber_test_server:testing_iiop_rec_any(Obj, RecA2)), + ok. %%--------------- Testing Post- & Pre-cond ------------------- diff --git a/lib/orber/test/orber_test_server.idl b/lib/orber/test/orber_test_server.idl index a88211c941..438c10e19b 100644 --- a/lib/orber/test/orber_test_server.idl +++ b/lib/orber/test/orber_test_server.idl @@ -28,7 +28,7 @@ module orber_parent { }; module orber_test { - + // interface server interface server : orber_parent::inherrit { typedef string array[2]; @@ -89,6 +89,23 @@ module orber_test { const fixed52 fixed52negconst2 = -123.00d; const fixed52 fixed52negconst3 = -023.00d; + struct rec_struct; // Forward declaration + typedef sequence<rec_struct> rec_struct_seq; + struct rec_struct { + rec_struct_seq chain; + }; + + + union rec_union; // Forward declaration + typedef sequence<rec_union>rec_union_seq; + + enum MyEnum {RecursiveType, NameType}; + + union rec_union switch (MyEnum) { + case RecursiveType : rec_union_seq chain; + case NameType : string aName; + }; + void stop_normal(); void stop_brutal(); @@ -123,6 +140,12 @@ module orber_test { void testing_iiop_context(); void testing_iiop_server_marshal(inout StrLength6 Str); + // Recursive types + any testing_iiop_rec_any(in any RecType); + rec_struct testing_iiop_rec_struct(in rec_struct RecS); + rec_union testing_iiop_rec_union(in rec_union RecU); + + oneway void testing_iiop_oneway_delay(in long Time); void testing_iiop_twoway_delay(in long Time); diff --git a/lib/orber/test/orber_test_server_impl.erl b/lib/orber/test/orber_test_server_impl.erl index 35296cb619..10a9caf242 100644 --- a/lib/orber/test/orber_test_server_impl.erl +++ b/lib/orber/test/orber_test_server_impl.erl @@ -1,7 +1,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 @@ -55,6 +55,9 @@ testing_iiop_void/2, testing_iiop_context/2, testing_iiop_server_marshal/3, + testing_iiop_rec_any/3, + testing_iiop_rec_struct/3, + testing_iiop_rec_union/3, relay_call/3, relay_cast/3, %% Testing pseudo calls. @@ -197,6 +200,16 @@ testing_iiop_context(_Self, State) -> testing_iiop_server_marshal(_Self, State, _String) -> {reply, {ok, false}, State}. +testing_iiop_rec_any(_Self, State, RAny) -> + {reply, RAny, State}. + +testing_iiop_rec_struct(_Self, State, RecS) -> + {reply, RecS, State}. + +testing_iiop_rec_union(_Self, State, RecU) -> + {reply, RecU, State}. + + testing_iiop_oneway_delay(_Self, State, Time) -> timer:sleep(Time), {noreply, State}. diff --git a/lib/orber/vsn.mk b/lib/orber/vsn.mk index 584a52ab84..b0c5a253a2 100644 --- a/lib/orber/vsn.mk +++ b/lib/orber/vsn.mk @@ -1 +1 @@ -ORBER_VSN = 3.6.18 +ORBER_VSN = 3.6.19 diff --git a/lib/percept/src/percept.erl b/lib/percept/src/percept.erl index f5e0f7e469..3a2d9f7601 100644 --- a/lib/percept/src/percept.erl +++ b/lib/percept/src/percept.erl @@ -185,10 +185,27 @@ stop_webserver() -> undefined -> {error, not_started}; Pid -> - Pid ! {self(), get_port}, - receive Port -> ok end, - Pid ! quit, - stop_webserver(Port) + do_stop([], Pid) + end. + +do_stop([], Pid)-> + Pid ! {self(), get_port}, + Port = receive P -> P end, + do_stop(Port, Pid); +do_stop(Port, [])-> + case whereis(percept_httpd) of + undefined -> + {error, not_started}; + Pid -> + do_stop(Port, Pid) + end; +do_stop(Port, Pid)-> + case find_service_pid_from_port(inets:services_info(), Port) of + undefined -> + {error, not_started}; + Pid2 -> + Pid ! quit, + inets:stop(httpd, Pid2) end. %% @spec stop_webserver(integer()) -> ok | {error, not_started} @@ -196,12 +213,7 @@ stop_webserver() -> %% @hidden stop_webserver(Port) -> - case find_service_pid_from_port(inets:services_info(), Port) of - undefined -> - {error, not_started}; - Pid -> - inets:stop(httpd, Pid) - end. + do_stop(Port,[]). %%========================================================================== %% diff --git a/lib/percept/src/percept_db.erl b/lib/percept/src/percept_db.erl index edb0d79a29..52e9afb78f 100644 --- a/lib/percept/src/percept_db.erl +++ b/lib/percept/src/percept_db.erl @@ -33,7 +33,7 @@ ]). -include("percept.hrl"). - +-define(STOP_TIMEOUT, 1000). %%========================================================================== %% %% Type definitions @@ -77,17 +77,32 @@ start() -> case erlang:whereis(percept_db) of undefined -> - Pid = spawn( fun() -> init_percept_db() end), - erlang:register(percept_db, Pid), - {started, Pid}; + {started, do_start()}; PerceptDB -> - erlang:unregister(percept_db), - PerceptDB ! {action, stop}, - Pid = spawn( fun() -> init_percept_db() end), - erlang:register(percept_db, Pid), - {restarted, Pid} + {restarted, restart(PerceptDB)} end. +%% @spec restart(pid()) -> pid() +%% @private +%% @doc restarts the percept database. + +-spec restart(pid())-> pid(). + +restart(PerceptDB)-> + stop_sync(PerceptDB), + do_start(). + +%% @spec do_start(pid()) -> pid() +%% @private +%% @doc starts the percept database. + +-spec do_start()-> pid(). + +do_start()-> + Pid = spawn( fun() -> init_percept_db() end), + erlang:register(percept_db, Pid), + Pid. + %% @spec stop() -> not_started | {stopped, Pid} %% Pid = pid() %% @doc Stops the percept database. @@ -103,6 +118,22 @@ stop() -> {stopped, Pid} end. +%% @spec stop_sync(pid()) -> true +%% @private +%% @doc Stops the percept database, with a synchronous call. + +-spec stop_sync(pid())-> true. + +stop_sync(Pid)-> + MonitorRef = erlang:monitor(process, Pid), + stop(), + receive + {'DOWN', MonitorRef, _Type, Pid, _Info}-> + true + after ?STOP_TIMEOUT-> + exit(Pid, kill) + end. + %% @spec insert(tuple()) -> ok %% @doc Inserts a trace or profile message to the database. diff --git a/lib/percept/test/percept_SUITE.erl b/lib/percept/test/percept_SUITE.erl index ff7cccdaa8..964ac68481 100644 --- a/lib/percept/test/percept_SUITE.erl +++ b/lib/percept/test/percept_SUITE.erl @@ -70,6 +70,10 @@ webserver(Config) when is_list(Config) -> % Explicit start inets? ?line {started, _, Port} = percept:start_webserver(), ?line ok = percept:stop_webserver(Port), + ?line {started, _, _} = percept:start_webserver(), + ?line ok = percept:stop_webserver(), + ?line {started, _, NewPort} = percept:start_webserver(), + ?line ok = percept:stop_webserver(NewPort), ?line application:stop(inets), ok. diff --git a/lib/percept/test/percept_db_SUITE.erl b/lib/percept/test/percept_db_SUITE.erl new file mode 100644 index 0000000000..79be9714ba --- /dev/null +++ b/lib/percept/test/percept_db_SUITE.erl @@ -0,0 +1,76 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2007-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% +%% + +-module(percept_db_SUITE). +-include("test_server.hrl"). + +%% Test server specific exports +-export([all/1]). +-export([init_per_suite/1, end_per_suite/1]). +-export([init_per_testcase/2, end_per_testcase/2]). + +%% Test cases +-export([ + start/1 + ]). + +%% Default timetrap timeout (set in init_per_testcase) +-define(default_timeout, ?t:minutes(2)). +-define(restarts, 10). +-define(alive_timeout, 500). + +init_per_suite(Config) when is_list(Config) -> + Config. + +end_per_suite(Config) when is_list(Config) -> + Config. + +init_per_testcase(_Case, Config) -> + Dog = ?t:timetrap(?default_timeout), + [{max_size, 300}, {watchdog,Dog} | Config]. + +end_per_testcase(_Case, Config) -> + Dog = ?config(watchdog, Config), + ?t:timetrap_cancel(Dog), + ok. + +all(suite) -> + % Test cases + [start]. + +%%---------------------------------------------------------------------- +%% Tests +%%---------------------------------------------------------------------- + +start(suite) -> + []; +start(doc) -> + ["Percept_db start and restart test."]; +start(Config) when is_list(Config) -> + ok = restart(?restarts), + {stopped, _DB} = percept_db:stop(), + ok. + +restart(0)-> + ok; +restart(N)-> + {_, DB} = percept_db:start(), + timer:sleep(?alive_timeout), + true = erlang:is_process_alive(DB), + restart(N-1). diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml index 313f1e59c9..2ccbc5348c 100644 --- a/lib/ssl/doc/src/notes.xml +++ b/lib/ssl/doc/src/notes.xml @@ -31,7 +31,31 @@ <p>This document describes the changes made to the SSL application. </p> - <section><title>SSL 4.1.1</title> + <section><title>SSL 4.1.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + The ssl application caches certificate files, it will now + invalidate cache entries if the diskfile is changed.</p> + <p> + Own Id: OTP-8965 Aux Id: seq11739 </p> + </item> + <item> + <p> + Now runs the terminate function before returning from the + call made by ssl:close/1, as before the caller of + ssl:close/1 could get problems with the reuseaddr option.</p> + <p> + Own Id: OTP-8992</p> + </item> + </list> + </section> + +</section> + +<section><title>SSL 4.1.1</title> <section><title>Fixed Bugs and Malfunctions</title> <list> diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml index ec272379bb..daf7b77527 100644 --- a/lib/ssl/doc/src/ssl.xml +++ b/lib/ssl/doc/src/ssl.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1999</year><year>2010</year> + <year>1999</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -72,10 +72,10 @@ {verify_fun, {fun(), term()}} | {fail_if_no_peer_cert, boolean()} {depth, integer()} | - {cert, der_bin()}| {certfile, path()} | - {key, der_bin()} | {keyfile, path()} | {password, string()} | - {cacerts, [der_bin()]} | {cacertfile, path()} | - |{dh, der_bin()} | {dhfile, path()} | {ciphers, ciphers()} | + {cert, der_encoded()}| {certfile, path()} | + {key, der_encoded()} | {keyfile, path()} | {password, string()} | + {cacerts, [der_encoded()]} | {cacertfile, path()} | + |{dh, der_encoded()} | {dhfile, path()} | {ciphers, ciphers()} | {ssl_imp, ssl_imp()} | {reuse_sessions, boolean()} | {reuse_session, fun()} </c></p> @@ -95,7 +95,7 @@ <p><c>path() = string() - representing a file path.</c></p> - <p><c>der_bin() = binary() -Asn1 DER encoded entity as an erlang binary.</c></p> + <p><c>der_encoded() = binary() -Asn1 DER encoded entity as an erlang binary.</c></p> <p><c>host() = hostname() | ipaddress()</c></p> @@ -136,14 +136,14 @@ <taglist> - <tag>{cert, der_bin()}</tag> + <tag>{cert, der_encoded()}</tag> <item> The DER encoded users certificate. If this option is supplied it will override the certfile option.</item> <tag>{certfile, path()}</tag> <item>Path to a file containing the user's certificate.</item> - <tag>{key, der_bin()}</tag> + <tag>{key, der_encoded()}</tag> <item> The DER encoded users private key. If this option is supplied it will override the keyfile option.</item> @@ -158,7 +158,7 @@ Only used if the private keyfile is password protected. </item> - <tag>{cacerts, [der_bin()]}</tag> + <tag>{cacerts, [der_encoded()]}</tag> <item> The DER encoded trusted certificates. If this option is supplied it will override the cacertfile option.</item> @@ -301,7 +301,7 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} | <taglist> - <tag>{dh, der_bin()}</tag> + <tag>{dh, der_encoded()}</tag> <item>The DER encoded Diffie Hellman parameters. If this option is supplied it will override the dhfile option. </item> diff --git a/lib/ssl/src/ssl.appup.src b/lib/ssl/src/ssl.appup.src index 51c5289bd2..a9c07ec87c 100644 --- a/lib/ssl/src/ssl.appup.src +++ b/lib/ssl/src/ssl.appup.src @@ -1,10 +1,12 @@ %% -*- erlang -*- {"%VSN%", [ + {"4.1.1", [{restart_application, ssl}]}, {"4.1", [{restart_application, ssl}]}, {"4.0.1", [{restart_application, ssl}]} ], [ + {"4.1.1", [{restart_application, ssl}]}, {"4.1", [{restart_application, ssl}]}, {"4.0.1", [{restart_application, ssl}]} ]}. diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index 7e5929d708..0e108c430f 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2010. All Rights Reserved. +%% Copyright Ericsson AB 1999-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -49,6 +49,27 @@ inet_ssl, %% inet options for internal ssl socket cb %% Callback info }). +-type option() :: socketoption() | ssloption() | transportoption(). +-type socketoption() :: [{property(), term()}]. %% See gen_tcp and inet +-type property() :: atom(). + +-type ssloption() :: {verify, verify_type()} | + {verify_fun, {fun(), InitialUserState::term()}} | + {fail_if_no_peer_cert, boolean()} | {depth, integer()} | + {cert, der_encoded()} | {certfile, path()} | {key, der_encoded()} | + {keyfile, path()} | {password, string()} | {cacerts, [der_encoded()]} | + {cacertfile, path()} | {dh, der_encoded()} | {dhfile, path()} | + {ciphers, ciphers()} | {ssl_imp, ssl_imp()} | {reuse_sessions, boolean()} | + {reuse_session, fun()}. + +-type verify_type() :: verify_none | verify_peer. +-type path() :: string(). +-type ciphers() :: [erl_cipher_suite()] | + string(). % (according to old API) +-type ssl_imp() :: new | old. + +-type transportoption() :: {CallbackModule::atom(), DataTag::atom(), ClosedTag::atom()}. + %%-------------------------------------------------------------------- -spec start() -> ok. @@ -77,8 +98,8 @@ stop() -> application:stop(ssl). %%-------------------------------------------------------------------- --spec connect(host() | port(), list()) -> {ok, #sslsocket{}}. --spec connect(host() | port(), list() | port_num(), timeout() | list()) -> {ok, #sslsocket{}}. +-spec connect(host() | port(), [option()]) -> {ok, #sslsocket{}}. +-spec connect(host() | port(), [option()] | port_num(), timeout() | list()) -> {ok, #sslsocket{}}. -spec connect(host() | port(), port_num(), list(), timeout()) -> {ok, #sslsocket{}}. %% @@ -126,7 +147,7 @@ connect(Host, Port, Options0, Timeout) -> end. %%-------------------------------------------------------------------- --spec listen(port_num(), list()) ->{ok, #sslsocket{}} | {error, reason()}. +-spec listen(port_num(), [option()]) ->{ok, #sslsocket{}} | {error, reason()}. %% %% Description: Creates a ssl listen socket. @@ -189,9 +210,10 @@ transport_accept(#sslsocket{} = ListenSocket, Timeout) -> ssl_broker:transport_accept(Pid, ListenSocket, Timeout). %%-------------------------------------------------------------------- --spec ssl_accept(#sslsocket{}) -> {ok, #sslsocket{}} | {error, reason()}. --spec ssl_accept(#sslsocket{}, list() | timeout()) -> {ok, #sslsocket{}} | {error, reason()}. --spec ssl_accept(port(), list(), timeout()) -> {ok, #sslsocket{}} | {error, reason()}. +-spec ssl_accept(#sslsocket{}) -> ok | {error, reason()}. +-spec ssl_accept(#sslsocket{} | port(), timeout()| [option()]) -> + ok | {ok, #sslsocket{}} | {error, reason()}. +-spec ssl_accept(port(), [option()], timeout()) -> {ok, #sslsocket{}} | {error, reason()}. %% %% Description: Performs accept on a ssl listen socket. e.i. performs %% ssl handshake. diff --git a/lib/ssl/src/ssl_certificate_db.erl b/lib/ssl/src/ssl_certificate_db.erl index 2a5a7f3394..f34459de37 100644 --- a/lib/ssl/src/ssl_certificate_db.erl +++ b/lib/ssl/src/ssl_certificate_db.erl @@ -27,7 +27,9 @@ -export([create/0, remove/1, add_trusted_certs/3, remove_trusted_certs/2, lookup_trusted_cert/3, issuer_candidate/1, - lookup_cached_certs/1, cache_pem_file/3]). + lookup_cached_certs/1, cache_pem_file/4, uncache_pem_file/2, lookup/2]). + +-type time() :: {non_neg_integer(), non_neg_integer(), non_neg_integer()}. %%==================================================================== %% Internal application API @@ -98,17 +100,35 @@ add_trusted_certs(Pid, File, [CertsDb, FileToRefDb, PidToFileDb]) -> insert(Pid, File, PidToFileDb), {ok, Ref}. %%-------------------------------------------------------------------- --spec cache_pem_file(pid(), string(), certdb_ref()) -> term(). +-spec cache_pem_file(pid(), string(), time(), certdb_ref()) -> term(). %% %% Description: Cache file as binary in DB %%-------------------------------------------------------------------- -cache_pem_file(Pid, File, [CertsDb, _FileToRefDb, PidToFileDb]) -> +cache_pem_file(Pid, File, Time, [CertsDb, _FileToRefDb, PidToFileDb]) -> {ok, PemBin} = file:read_file(File), Content = public_key:pem_decode(PemBin), - insert({file, File}, Content, CertsDb), + insert({file, File}, {Time, Content}, CertsDb), insert(Pid, File, PidToFileDb), {ok, Content}. +%-------------------------------------------------------------------- +-spec uncache_pem_file(string(), certdb_ref()) -> no_return(). +%% +%% Description: If a cached file is no longer valid (changed on disk) +%% we must terminate the connections using the old file content, and +%% when those processes are finish the cache will be cleaned. It is +%% a rare but possible case a new ssl client/server is started with +%% a filename with the same name as previously started client/server +%% but with different content. +%% -------------------------------------------------------------------- +uncache_pem_file(File, [_CertsDb, _FileToRefDb, PidToFileDb]) -> + [Pids] = select(PidToFileDb, [{{'$1', File},[],['$$']}]), + lists:foreach(fun(Pid) -> + exit(Pid, shutdown) + end, Pids). + + + %%-------------------------------------------------------------------- -spec remove_trusted_certs(pid(), certdb_ref()) -> term(). @@ -174,6 +194,22 @@ issuer_candidate(PrevCandidateKey) -> end. %%-------------------------------------------------------------------- +-spec lookup(term(), term()) -> term() | undefined. +%% +%% Description: Looks up an element in a certificat <Db>. +%%-------------------------------------------------------------------- +lookup(Key, Db) -> + case ets:lookup(Db, Key) of + [] -> + undefined; + Contents -> + Pick = fun({_, Data}) -> Data; + ({_,_,Data}) -> Data + end, + [Pick(Data) || Data <- Contents] + end. + +%%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- certificate_db_name() -> @@ -191,16 +227,8 @@ ref_count(Key, Db,N) -> delete(Key, Db) -> _ = ets:delete(Db, Key). -lookup(Key, Db) -> - case ets:lookup(Db, Key) of - [] -> - undefined; - Contents -> - Pick = fun({_, Data}) -> Data; - ({_,_,Data}) -> Data - end, - [Pick(Data) || Data <- Contents] - end. +select(Db, MatchSpec)-> + ets:select(Db, MatchSpec). remove_certs(Ref, CertsDb) -> ets:match_delete(CertsDb, {{Ref, '_', '_'}, '_'}). diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index 6c9ac65b64..489895cf29 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2010. All Rights Reserved. +%% Copyright Ericsson AB 2007-2011. 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 @@ -70,7 +70,6 @@ %% {{md5_hash, sha_hash}, {prev_md5, prev_sha}} (binary()) tls_handshake_hashes, % see above tls_cipher_texts, % list() received but not deciphered yet - own_cert, % binary() session, % #session{} from ssl_handshake.hrl session_cache, % session_cache_cb, % @@ -90,7 +89,8 @@ log_alert, % boolean() renegotiation, % {boolean(), From | internal | peer} recv_during_renegotiation, %boolean() - send_queue % queue() + send_queue, % queue() + terminated = false % }). -define(DEFAULT_DIFFIE_HELLMAN_PARAMS, @@ -304,8 +304,9 @@ init([Role, Host, Port, Socket, {SSLOpts0, _} = Options, try ssl_init(SSLOpts0, Role) of {ok, Ref, CacheRef, OwnCert, Key, DHParams} -> + Session = State0#state.session, State = State0#state{tls_handshake_hashes = Hashes0, - own_cert = OwnCert, + session = Session#session{own_certificate = OwnCert}, cert_db_ref = Ref, session_cache = CacheRef, private_key = Key, @@ -331,14 +332,13 @@ init([Role, Host, Port, Socket, {SSLOpts0, _} = Options, %%-------------------------------------------------------------------- hello(start, #state{host = Host, port = Port, role = client, ssl_options = SslOpts, + session = #session{own_certificate = Cert} = Session0, transport_cb = Transport, socket = Socket, connection_states = ConnectionStates, - renegotiation = {Renegotiation, _}} - = State0) -> - + renegotiation = {Renegotiation, _}} = State0) -> Hello = ssl_handshake:client_hello(Host, Port, ConnectionStates, - SslOpts, Renegotiation), + SslOpts, Renegotiation, Cert), Version = Hello#client_hello.client_version, Hashes0 = ssl_handshake:init_hashes(), @@ -347,13 +347,13 @@ hello(start, #state{host = Host, port = Port, role = client, Transport:send(Socket, BinMsg), State1 = State0#state{connection_states = CS2, negotiated_version = Version, %% Requested version - session = - #session{session_id = Hello#client_hello.session_id, - is_resumable = false}, + session = + Session0#session{session_id = Hello#client_hello.session_id, + is_resumable = false}, tls_handshake_hashes = Hashes1}, {Record, State} = next_record(State1), next_state(hello, Record, State); - + hello(start, #state{role = server} = State0) -> {Record, State} = next_record(State0), next_state(hello, Record, State); @@ -370,7 +370,6 @@ hello(#server_hello{cipher_suite = CipherSuite, negotiated_version = ReqVersion, renegotiation = {Renegotiation, _}, ssl_options = SslOptions} = State0) -> - case ssl_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation) of {Version, NewId, ConnectionStates} -> {KeyAlgorithm, _, _} = @@ -396,13 +395,11 @@ hello(#server_hello{cipher_suite = CipherSuite, hello(Hello = #client_hello{client_version = ClientVersion}, State = #state{connection_states = ConnectionStates0, - port = Port, session = Session0, + port = Port, session = #session{own_certificate = Cert} = Session0, renegotiation = {Renegotiation, _}, session_cache = Cache, session_cache_cb = CacheCb, - ssl_options = SslOpts, - own_cert = Cert}) -> - + ssl_options = SslOpts}) -> case ssl_handshake:hello(Hello, SslOpts, {Port, Session0, Cache, CacheCb, ConnectionStates0, Cert}, Renegotiation) of {Version, {Type, Session}, ConnectionStates} -> @@ -536,7 +533,7 @@ certify(#server_hello_done{}, connection_states = ConnectionStates0, negotiated_version = Version, premaster_secret = undefined, - role = client} = State0) -> + role = client} = State0) -> case ssl_handshake:master_secret(Version, Session, ConnectionStates0, client) of {MasterSecret, ConnectionStates1} -> @@ -613,7 +610,6 @@ certify_client_key_exchange(#client_diffie_hellman_public{dh_public = ClientPubl diffie_hellman_params = #'DHParameter'{prime = P, base = G}, 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), @@ -656,8 +652,7 @@ cipher(#finished{verify_data = Data} = Finished, role = Role, session = #session{master_secret = MasterSecret} = Session0, - tls_handshake_hashes = Hashes0} = State) -> - + tls_handshake_hashes = Hashes0} = State) -> case ssl_handshake:verify_connection(Version, Finished, opposite_role(Role), MasterSecret, Hashes0) of @@ -678,15 +673,15 @@ cipher(Msg, State) -> %%-------------------------------------------------------------------- connection(#hello_request{}, #state{host = Host, port = Port, socket = Socket, + session = #session{own_certificate = Cert}, ssl_options = SslOpts, negotiated_version = Version, transport_cb = Transport, connection_states = ConnectionStates0, renegotiation = {Renegotiation, _}, tls_handshake_hashes = Hashes0} = State0) -> - Hello = ssl_handshake:client_hello(Host, Port, ConnectionStates0, - SslOpts, Renegotiation), + SslOpts, Renegotiation, Cert), {BinMsg, ConnectionStates1, Hashes1} = encode_handshake(Hello, Version, ConnectionStates0, Hashes0), @@ -777,8 +772,12 @@ handle_sync_event(start, _, connection, State) -> handle_sync_event(start, From, StateName, State) -> {next_state, StateName, State#state{from = From}}; -handle_sync_event(close, _, _StateName, State) -> - {stop, normal, ok, State}; +handle_sync_event(close, _, StateName, State) -> + %% Run terminate before returning + %% so that the reuseaddr inet-option will work + %% as intended. + (catch terminate(user_close, StateName, State)), + {stop, normal, ok, State#state{terminated = true}}; handle_sync_event({shutdown, How0}, _, StateName, #state{transport_cb = Transport, @@ -966,6 +965,11 @@ handle_info(Msg, StateName, State) -> %% necessary cleaning up. When it returns, the gen_fsm terminates with %% Reason. The return value is ignored. %%-------------------------------------------------------------------- +terminate(_, _, #state{terminated = true}) -> + %% Happens when user closes the connection using ssl:close/1 + %% we want to guarantee that Transport:close has been called + %% when ssl:close/1 returns. + ok; terminate(Reason, connection, #state{negotiated_version = Version, connection_states = ConnectionStates, transport_cb = Transport, @@ -975,14 +979,14 @@ terminate(Reason, connection, #state{negotiated_version = Version, notify_renegotiater(Renegotiate), BinAlert = terminate_alert(Reason, Version, ConnectionStates), Transport:send(Socket, BinAlert), - workaround_transport_delivery_problems(Socket, Transport), + workaround_transport_delivery_problems(Socket, Transport, Reason), Transport:close(Socket); -terminate(_Reason, _StateName, #state{transport_cb = Transport, +terminate(Reason, _StateName, #state{transport_cb = Transport, socket = Socket, send_queue = SendQueue, renegotiation = Renegotiate}) -> notify_senders(SendQueue), notify_renegotiater(Renegotiate), - workaround_transport_delivery_problems(Socket, Transport), + workaround_transport_delivery_problems(Socket, Transport, Reason), Transport:close(Socket). %%-------------------------------------------------------------------- @@ -1082,6 +1086,7 @@ init_private_key({rsa, PrivateKey}, _, _,_) -> init_private_key({dsa, PrivateKey},_,_,_) -> public_key:der_decode('DSAPrivateKey', PrivateKey). +-spec(handle_file_error/6 :: (_,_,_,_,_,_) -> no_return()). handle_file_error(Line, Error, {badmatch, Reason}, File, Throw, Stack) -> file_error(Line, Error, Reason, File, Throw, Stack); handle_file_error(Line, Error, Reason, File, Throw, Stack) -> @@ -1147,7 +1152,7 @@ certify_client(#state{client_certificate_requested = true, role = client, transport_cb = Transport, negotiated_version = Version, cert_db_ref = CertDbRef, - own_cert = OwnCert, + session = #session{own_certificate = OwnCert}, socket = Socket, tls_handshake_hashes = Hashes0} = State) -> Certificate = ssl_handshake:certificate(OwnCert, CertDbRef, client), @@ -1163,10 +1168,10 @@ verify_client_cert(#state{client_certificate_requested = true, role = client, connection_states = ConnectionStates0, transport_cb = Transport, negotiated_version = Version, - own_cert = OwnCert, socket = Socket, private_key = PrivateKey, - session = #session{master_secret = MasterSecret}, + session = #session{master_secret = MasterSecret, + own_certificate = OwnCert}, tls_handshake_hashes = Hashes0} = State) -> case ssl_handshake:client_certificate_verify(OwnCert, MasterSecret, @@ -1332,7 +1337,7 @@ certify_server(#state{transport_cb = Transport, connection_states = ConnectionStates, tls_handshake_hashes = Hashes, cert_db_ref = CertDbRef, - own_cert = OwnCert} = State) -> + session = #session{own_certificate = OwnCert}} = State) -> case ssl_handshake:certificate(OwnCert, CertDbRef, server) of CertMsg = #certificate{} -> {BinCertMsg, NewConnectionStates, NewHashes} = @@ -1359,7 +1364,6 @@ key_exchange(#state{role = server, key_algorithm = Algo, when Algo == dhe_dss; Algo == dhe_rsa; Algo == dh_anon -> - Keys = crypto:dh_generate_key([crypto:mpint(P), crypto:mpint(G)]), ConnectionState = ssl_record:pending_connection_state(ConnectionStates0, read), @@ -1911,14 +1915,22 @@ next_state_connection(StateName, #state{send_queue = Queue0, next_state_is_connection(State) end. +%% In next_state_is_connection/1: clear tls_handshake_hashes, +%% premaster_secret and public_key_info (only needed during handshake) +%% to reduce memory foot print of a connection. next_state_is_connection(State = #state{recv_during_renegotiation = true, socket_options = #socket_options{active = false}}) -> - passive_receive(State#state{recv_during_renegotiation = false}, connection); + passive_receive(State#state{recv_during_renegotiation = false, + premaster_secret = undefined, + public_key_info = undefined, + tls_handshake_hashes = {<<>>, <<>>}}, connection); next_state_is_connection(State0) -> {Record, State} = next_record_if_active(State0), - next_state(connection, Record, State). + next_state(connection, Record, State#state{premaster_secret = undefined, + public_key_info = undefined, + tls_handshake_hashes = {<<>>, <<>>}}). register_session(_, _, _, #session{is_resumable = true} = Session) -> Session; %% Already registered @@ -2185,7 +2197,8 @@ notify_renegotiater({true, From}) when not is_atom(From) -> notify_renegotiater(_) -> ok. -terminate_alert(Reason, Version, ConnectionStates) when Reason == normal; Reason == shutdown -> +terminate_alert(Reason, Version, ConnectionStates) when Reason == normal; Reason == shutdown; + Reason == user_close -> {BinAlert, _} = encode_alert(?ALERT_REC(?WARNING, ?CLOSE_NOTIFY), Version, ConnectionStates), BinAlert; @@ -2194,10 +2207,13 @@ terminate_alert(_, Version, ConnectionStates) -> Version, ConnectionStates), BinAlert. -workaround_transport_delivery_problems(Socket, Transport) -> +workaround_transport_delivery_problems(_,_, user_close) -> + ok; +workaround_transport_delivery_problems(Socket, Transport, _) -> %% Standard trick to try to make sure all %% data sent to to tcp port is really sent - %% before tcp port is closed. + %% before tcp port is closed so that the peer will + %% get a correct error message. inet:setopts(Socket, [{active, false}]), Transport:shutdown(Socket, write), Transport:recv(Socket, 0). diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index c7a1c4965d..125c28b373 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -30,7 +30,7 @@ -include("ssl_internal.hrl"). -include_lib("public_key/include/public_key.hrl"). --export([master_secret/4, client_hello/5, server_hello/4, hello/4, +-export([master_secret/4, client_hello/6, server_hello/4, hello/4, hello_request/0, certify/6, certificate/3, client_certificate_verify/5, certificate_verify/5, certificate_request/2, key_exchange/2, server_key_exchange_hash/2, @@ -49,13 +49,13 @@ %%==================================================================== %%-------------------------------------------------------------------- -spec client_hello(host(), port_num(), #connection_states{}, - #ssl_options{}, boolean()) -> #client_hello{}. + #ssl_options{}, boolean(), der_cert()) -> #client_hello{}. %% %% Description: Creates a client hello message. %%-------------------------------------------------------------------- client_hello(Host, Port, ConnectionStates, #ssl_options{versions = Versions, ciphers = UserSuites} - = SslOpts, Renegotiation) -> + = SslOpts, Renegotiation, OwnCert) -> Fun = fun(Version) -> ssl_record:protocol_version(Version) @@ -65,7 +65,7 @@ client_hello(Host, Port, ConnectionStates, #ssl_options{versions = Versions, SecParams = Pending#connection_state.security_parameters, Ciphers = available_suites(UserSuites, Version), - Id = ssl_manager:client_session_id(Host, Port, SslOpts), + Id = ssl_manager:client_session_id(Host, Port, SslOpts, OwnCert), #client_hello{session_id = Id, client_version = Version, @@ -571,7 +571,7 @@ select_session(Hello, Port, Session, Version, #ssl_options{ciphers = UserSuites} = SslOpts, Cache, CacheCb, Cert) -> SuggestedSessionId = Hello#client_hello.session_id, SessionId = ssl_manager:server_session_id(Port, SuggestedSessionId, - SslOpts), + SslOpts, Cert), Suites = available_suites(Cert, UserSuites, Version), case ssl_session:is_new(SuggestedSessionId, SessionId) of diff --git a/lib/ssl/src/ssl_handshake.hrl b/lib/ssl/src/ssl_handshake.hrl index 68a7802ef2..8ae4d2332e 100644 --- a/lib/ssl/src/ssl_handshake.hrl +++ b/lib/ssl/src/ssl_handshake.hrl @@ -36,6 +36,7 @@ -record(session, { session_id, peer_certificate, + own_certificate, compression_method, cipher_suite, master_secret, diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl index 43a85c2d9d..715941e3ad 100644 --- a/lib/ssl/src/ssl_internal.hrl +++ b/lib/ssl/src/ssl_internal.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2010. All Rights Reserved. +%% Copyright Ericsson AB 2007-2011. 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 @@ -19,7 +19,6 @@ %% - -ifndef(ssl_internal). -define(ssl_internal, true). diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl index 3b02d96562..f845b1ecc0 100644 --- a/lib/ssl/src/ssl_manager.erl +++ b/lib/ssl/src/ssl_manager.erl @@ -29,8 +29,8 @@ %% Internal application API -export([start_link/1, connection_init/2, cache_pem_file/1, - lookup_trusted_cert/3, issuer_candidate/1, client_session_id/3, - server_session_id/3, + lookup_trusted_cert/3, issuer_candidate/1, client_session_id/4, + server_session_id/4, register_session/2, register_session/3, invalidate_session/2, invalidate_session/3]). @@ -43,6 +43,7 @@ -include("ssl_handshake.hrl"). -include("ssl_internal.hrl"). +-include_lib("kernel/include/file.hrl"). -record(state, { session_cache, @@ -76,16 +77,17 @@ start_link(Opts) -> connection_init(Trustedcerts, Role) -> call({connection_init, Trustedcerts, Role}). %%-------------------------------------------------------------------- --spec cache_pem_file(string()) -> {ok, term()}. +-spec cache_pem_file(string()) -> {ok, term()} | {error, reason()}. %% -%% Description: Cach a pem file and +%% Description: Cach a pem file and return its content. %%-------------------------------------------------------------------- -cache_pem_file(File) -> - case ssl_certificate_db:lookup_cached_certs(File) of - [{_,Content}] -> - {ok, Content}; - [] -> - call({cache_pem, File}) +cache_pem_file(File) -> + try file:read_file_info(File) of + {ok, #file_info{mtime = LastWrite}} -> + cache_pem_file(File, LastWrite) + catch + _:Reason -> + {error, Reason} end. %%-------------------------------------------------------------------- -spec lookup_trusted_cert(reference(), serialnumber(), issuer()) -> @@ -106,20 +108,21 @@ lookup_trusted_cert(Ref, SerialNumber, Issuer) -> issuer_candidate(PrevCandidateKey) -> ssl_certificate_db:issuer_candidate(PrevCandidateKey). %%-------------------------------------------------------------------- --spec client_session_id(host(), port_num(), #ssl_options{}) -> session_id(). +-spec client_session_id(host(), port_num(), #ssl_options{}, + der_cert() | undefined) -> session_id(). %% %% Description: Select a session id for the client. %%-------------------------------------------------------------------- -client_session_id(Host, Port, SslOpts) -> - call({client_session_id, Host, Port, SslOpts}). +client_session_id(Host, Port, SslOpts, OwnCert) -> + call({client_session_id, Host, Port, SslOpts, OwnCert}). %%-------------------------------------------------------------------- --spec server_session_id(host(), port_num(), #ssl_options{}) -> session_id(). +-spec server_session_id(host(), port_num(), #ssl_options{}, der_cert()) -> session_id(). %% %% Description: Select a session id for the server. %%-------------------------------------------------------------------- -server_session_id(Port, SuggestedSessionId, SslOpts) -> - call({server_session_id, Port, SuggestedSessionId, SslOpts}). +server_session_id(Port, SuggestedSessionId, SslOpts, OwnCert) -> + call({server_session_id, Port, SuggestedSessionId, SslOpts, OwnCert}). %%-------------------------------------------------------------------- -spec register_session(port_num(), #session{}) -> ok. @@ -201,28 +204,35 @@ handle_call({{connection_init, Trustedcerts, _Role}, Pid}, _From, end, {reply, Result, State}; -handle_call({{client_session_id, Host, Port, SslOpts}, _}, _, +handle_call({{client_session_id, Host, Port, SslOpts, OwnCert}, _}, _, #state{session_cache = Cache, session_cache_cb = CacheCb} = State) -> - Id = ssl_session:id({Host, Port, SslOpts}, Cache, CacheCb), + Id = ssl_session:id({Host, Port, SslOpts}, Cache, CacheCb, OwnCert), {reply, Id, State}; -handle_call({{server_session_id, Port, SuggestedSessionId, SslOpts}, _}, +handle_call({{server_session_id, Port, SuggestedSessionId, SslOpts, OwnCert}, _}, _, #state{session_cache_cb = CacheCb, session_cache = Cache, session_lifetime = LifeTime} = State) -> Id = ssl_session:id(Port, SuggestedSessionId, SslOpts, - Cache, CacheCb, LifeTime), + Cache, CacheCb, LifeTime, OwnCert), {reply, Id, State}; -handle_call({{cache_pem, File},Pid}, _, State = #state{certificate_db = Db}) -> - try ssl_certificate_db:cache_pem_file(Pid,File,Db) of +handle_call({{cache_pem, File, LastWrite}, Pid}, _, + #state{certificate_db = Db} = State) -> + try ssl_certificate_db:cache_pem_file(Pid, File, LastWrite, Db) of Result -> {reply, Result, State} catch _:Reason -> {reply, {error, Reason}, State} - end. + end; +handle_call({{recache_pem, File, LastWrite}, Pid}, From, + #state{certificate_db = Db} = State) -> + ssl_certificate_db:uncache_pem_file(File, Db), + cast({recache_pem, File, LastWrite, Pid, From}), + {noreply, State}. + %%-------------------------------------------------------------------- -spec handle_cast(msg(), #state{}) -> {noreply, #state{}}. %% Possible return values not used now. @@ -259,7 +269,21 @@ handle_cast({invalidate_session, Port, #session{session_id = ID}}, #state{session_cache = Cache, session_cache_cb = CacheCb} = State) -> CacheCb:delete(Cache, {Port, ID}), - {noreply, State}. + {noreply, State}; + +handle_cast({recache_pem, File, LastWrite, Pid, From}, + #state{certificate_db = [_, FileToRefDb, _]} = State0) -> + case ssl_certificate_db:lookup(File, FileToRefDb) of + undefined -> + {reply, Msg, State} = handle_call({{cache_pem, File, LastWrite}, Pid}, From, State0), + gen_server:reply(From, Msg), + {noreply, State}; + _ -> %% Send message to self letting cleanup messages be handled + %% first so that no reference to the old version of file + %% exists when we cache the new one. + cast({recache_pem, File, LastWrite, Pid, From}), + {noreply, State0} + end. %%-------------------------------------------------------------------- -spec handle_info(msg(), #state{}) -> {noreply, #state{}}. @@ -286,12 +310,14 @@ handle_info({'EXIT', _, _}, State) -> handle_info({'DOWN', _Ref, _Type, _Pid, ecacertfile}, State) -> {noreply, State}; +handle_info({'DOWN', _Ref, _Type, Pid, shutdown}, State) -> + handle_info({remove_trusted_certs, Pid}, State); handle_info({'DOWN', _Ref, _Type, Pid, _Reason}, State) -> erlang:send_after(?CERTIFICATE_CACHE_CLEANUP, self(), {remove_trusted_certs, Pid}), {noreply, State}; handle_info({remove_trusted_certs, Pid}, - State = #state{certificate_db = Db}) -> + #state{certificate_db = Db} = State) -> ssl_certificate_db:remove_trusted_certs(Pid, Db), {noreply, State}; @@ -362,3 +388,16 @@ session_validation({{{Host, Port}, _}, Session}, LifeTime) -> session_validation({{Port, _}, Session}, LifeTime) -> validate_session(Port, Session, LifeTime), LifeTime. + +cache_pem_file(File, LastWrite) -> + case ssl_certificate_db:lookup_cached_certs(File) of + [{_, {Mtime, Content}}] -> + case LastWrite of + Mtime -> + {ok, Content}; + _ -> + call({recache_pem, File, LastWrite}) + end; + [] -> + call({cache_pem, File, LastWrite}) + end. diff --git a/lib/ssl/src/ssl_session.erl b/lib/ssl/src/ssl_session.erl index 25e7445180..dc4b7a711c 100644 --- a/lib/ssl/src/ssl_session.erl +++ b/lib/ssl/src/ssl_session.erl @@ -28,7 +28,7 @@ -include("ssl_internal.hrl"). %% Internal application API --export([is_new/2, id/3, id/6, valid_session/2]). +-export([is_new/2, id/4, id/7, valid_session/2]). -define(GEN_UNIQUE_ID_MAX_TRIES, 10). @@ -48,13 +48,14 @@ is_new(_ClientSuggestion, _ServerDecision) -> true. %%-------------------------------------------------------------------- --spec id({host(), port_num(), #ssl_options{}}, cache_ref(), atom()) -> binary(). +-spec id({host(), port_num(), #ssl_options{}}, cache_ref(), atom(), + undefined | binary()) -> binary(). %% %% Description: Should be called by the client side to get an id %% for the client hello message. %%-------------------------------------------------------------------- -id(ClientInfo, Cache, CacheCb) -> - case select_session(ClientInfo, Cache, CacheCb) of +id(ClientInfo, Cache, CacheCb, OwnCert) -> + case select_session(ClientInfo, Cache, CacheCb, OwnCert) of no_session -> <<>>; SessionId -> @@ -63,19 +64,19 @@ id(ClientInfo, Cache, CacheCb) -> %%-------------------------------------------------------------------- -spec id(port_num(), binary(), #ssl_options{}, cache_ref(), - atom(), seconds()) -> binary(). + atom(), seconds(), binary()) -> binary(). %% %% Description: Should be called by the server side to get an id %% for the server hello message. %%-------------------------------------------------------------------- -id(Port, <<>>, _, Cache, CacheCb, _) -> +id(Port, <<>>, _, Cache, CacheCb, _, _) -> new_id(Port, ?GEN_UNIQUE_ID_MAX_TRIES, Cache, CacheCb); id(Port, SuggestedSessionId, #ssl_options{reuse_sessions = ReuseEnabled, reuse_session = ReuseFun}, - Cache, CacheCb, SecondLifeTime) -> + Cache, CacheCb, SecondLifeTime, OwnCert) -> case is_resumable(SuggestedSessionId, Port, ReuseEnabled, - ReuseFun, Cache, CacheCb, SecondLifeTime) of + ReuseFun, Cache, CacheCb, SecondLifeTime, OwnCert) of true -> SuggestedSessionId; false -> @@ -93,19 +94,20 @@ valid_session(#session{time_stamp = TimeStamp}, LifeTime) -> %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- -select_session({HostIP, Port, SslOpts}, Cache, CacheCb) -> +select_session({HostIP, Port, SslOpts}, Cache, CacheCb, OwnCert) -> Sessions = CacheCb:select_session(Cache, {HostIP, Port}), - select_session(Sessions, SslOpts). + select_session(Sessions, SslOpts, OwnCert). -select_session([], _) -> +select_session([], _, _) -> no_session; select_session(Sessions, #ssl_options{ciphers = Ciphers, - reuse_sessions = ReuseSession}) -> + reuse_sessions = ReuseSession}, OwnCert) -> IsResumable = fun(Session) -> ReuseSession andalso (Session#session.is_resumable) andalso lists:member(Session#session.cipher_suite, Ciphers) + andalso (OwnCert == Session#session.own_certificate) end, case [Id || [Id, Session] <- Sessions, IsResumable(Session)] of [] -> @@ -140,14 +142,16 @@ new_id(Port, Tries, Cache, CacheCb) -> end. is_resumable(SuggestedSessionId, Port, ReuseEnabled, ReuseFun, Cache, - CacheCb, SecondLifeTime) -> + CacheCb, SecondLifeTime, OwnCert) -> case CacheCb:lookup(Cache, {Port, SuggestedSessionId}) of #session{cipher_suite = CipherSuite, + own_certificate = SessionOwnCert, compression_method = Compression, is_resumable = Is_resumable, peer_certificate = PeerCert} = Session -> ReuseEnabled andalso Is_resumable + andalso (OwnCert == SessionOwnCert) andalso valid_session(Session, SecondLifeTime) andalso ReuseFun(SuggestedSessionId, PeerCert, Compression, CipherSuite); diff --git a/lib/ssl/src/ssl_ssl3.erl b/lib/ssl/src/ssl_ssl3.erl index c49f9f1e6d..f2926b2d2f 100644 --- a/lib/ssl/src/ssl_ssl3.erl +++ b/lib/ssl/src/ssl_ssl3.erl @@ -102,11 +102,6 @@ mac_hash(Method, Mac_write_secret, Seq_num, Type, Length, Fragment) -> %% hash(MAC_write_secret + pad_1 + seq_num + %% SSLCompressed.type + SSLCompressed.length + %% SSLCompressed.fragment)); - case Method of - ?NULL -> ok; - _ -> - ok - end, Mac = mac_hash(Method, Mac_write_secret, [<<?UINT64(Seq_num), ?BYTE(Type), ?UINT16(Length)>>, Fragment]), diff --git a/lib/ssl/src/ssl_tls1.erl b/lib/ssl/src/ssl_tls1.erl index 3784483e9c..5f9850c386 100644 --- a/lib/ssl/src/ssl_tls1.erl +++ b/lib/ssl/src/ssl_tls1.erl @@ -128,11 +128,6 @@ mac_hash(Method, Mac_write_secret, Seq_num, Type, {Major, Minor}, %% HMAC_hash(MAC_write_secret, seq_num + TLSCompressed.type + %% TLSCompressed.version + TLSCompressed.length + %% TLSCompressed.fragment)); - case Method of - ?NULL -> ok; - _ -> - ok - end, Mac = hmac_hash(Method, Mac_write_secret, [<<?UINT64(Seq_num), ?BYTE(Type), ?BYTE(Major), ?BYTE(Minor), ?UINT16(Length)>>, diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl index 8f9554f3ce..962d2d8cf0 100644 --- a/lib/ssl/test/ssl_basic_SUITE.erl +++ b/lib/ssl/test/ssl_basic_SUITE.erl @@ -27,6 +27,7 @@ -include("test_server.hrl"). -include("test_server_line.hrl"). -include_lib("public_key/include/public_key.hrl"). + -include("ssl_alert.hrl"). -define('24H_in_sec', 86400). @@ -125,6 +126,9 @@ init_per_testcase(empty_protocol_versions, Config) -> ssl:start(), Config; +init_per_testcase(different_ca_peer_sign, Config0) -> + ssl_test_lib:make_mix_cert(Config0); + init_per_testcase(_TestCase, Config0) -> Config = lists:keydelete(watchdog, 1, Config0), Dog = test_server:timetrap(?TIMEOUT), @@ -205,7 +209,10 @@ all(suite) -> invalid_signature_client, invalid_signature_server, cert_expired, client_with_cert_cipher_suites_handshake, unknown_server_ca_fail, der_input, unknown_server_ca_accept_verify_none, unknown_server_ca_accept_verify_peer, - unknown_server_ca_accept_backwardscompatibilty + unknown_server_ca_accept_backwardscompatibilty, + %different_ca_peer_sign, + no_reuses_session_server_restart_new_cert, + no_reuses_session_server_restart_new_cert_file, reuseaddr ]. %% Test cases starts here. @@ -321,7 +328,6 @@ basic_test(Config) -> ssl_test_lib:close(Server), ssl_test_lib:close(Client). - %%-------------------------------------------------------------------- controlling_process(doc) -> @@ -521,9 +527,7 @@ client_closes_socket(Config) when is_list(Config) -> _Client = spawn_link(Connect), - ssl_test_lib:check_result(Server, {error,closed}), - - ssl_test_lib:close(Server). + ssl_test_lib:check_result(Server, {error,closed}). %%-------------------------------------------------------------------- @@ -738,7 +742,6 @@ socket_options(Config) when is_list(Config) -> ssl_test_lib:check_result(Server, ok, Client, ok), ssl_test_lib:close(Server), - ssl_test_lib:close(Client), {ok, Listen} = ssl:listen(0, ServerOpts), {ok,[{mode,list}]} = ssl:getopts(Listen, [mode]), @@ -841,6 +844,7 @@ send_recv(Config) when is_list(Config) -> ssl_test_lib:close(Server), ssl_test_lib:close(Client). +%%-------------------------------------------------------------------- send_close(doc) -> [""]; @@ -867,8 +871,7 @@ send_close(Config) when is_list(Config) -> ok = ssl:send(SslS, "Hello world"), {ok,<<"Hello world">>} = ssl:recv(SslS, 11), gen_tcp:close(TcpS), - {error, _} = ssl:send(SslS, "Hello world"), - ssl_test_lib:close(Server). + {error, _} = ssl:send(SslS, "Hello world"). %%-------------------------------------------------------------------- close_transport_accept(doc) -> @@ -1043,8 +1046,7 @@ tcp_connect(Config) when is_list(Config) -> {Server, {error, Error}} -> test_server:format("Error ~p", [Error]) end - end, - ssl_test_lib:close(Server). + end. dummy(_Socket) -> @@ -1149,13 +1151,13 @@ ecertfile(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -ecacertfile(doc) -> +ecacertfile(doc) -> ["Test what happens with an invalid cacert file"]; -ecacertfile(suite) -> +ecacertfile(suite) -> []; -ecacertfile(Config) when is_list(Config) -> +ecacertfile(Config) when is_list(Config) -> ClientOpts = [{reuseaddr, true}|?config(client_opts, Config)], ServerBadOpts = [{reuseaddr, true}|?config(server_bad_ca, Config)], {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), @@ -1532,7 +1534,7 @@ erlang_cipher_suite(Suite) -> Suite. cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) -> - process_flag(trap_exit, true), + %% process_flag(trap_exit, true), test_server:format("Testing CipherSuite ~p~n", [CipherSuite]), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), @@ -1556,16 +1558,8 @@ cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) -> Result = ssl_test_lib:wait_for_result(Server, ok, Client, ok), ssl_test_lib:close(Server), - receive - {'EXIT', Server, normal} -> - ok - end, ssl_test_lib:close(Client), - receive - {'EXIT', Client, normal} -> - ok - end, - process_flag(trap_exit, false), + case Result of ok -> []; @@ -1607,7 +1601,6 @@ reuse_session(suite) -> []; reuse_session(Config) when is_list(Config) -> - process_flag(trap_exit, true), ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), @@ -1615,13 +1608,13 @@ reuse_session(Config) when is_list(Config) -> Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, session_info_result, []}}, - {options, ServerOpts}]), + {mfa, {?MODULE, session_info_result, []}}, + {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), Client0 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, - {mfa, {ssl_test_lib, no_result, []}}, + {mfa, {ssl_test_lib, no_result, []}}, {from, self()}, {options, ClientOpts}]), SessionInfo = receive @@ -1629,16 +1622,16 @@ reuse_session(Config) when is_list(Config) -> Info end, - Server ! listen, + Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, %% Make sure session is registered test_server:sleep(?SLEEP), Client1 = - ssl_test_lib:start_client([{node, ClientNode}, - {port, Port}, {host, Hostname}, - {mfa, {?MODULE, session_info_result, []}}, - {from, self()}, {options, ClientOpts}]), + ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {mfa, {?MODULE, session_info_result, []}}, + {from, self()}, {options, ClientOpts}]), receive {Client1, SessionInfo} -> ok; @@ -1648,10 +1641,10 @@ reuse_session(Config) when is_list(Config) -> test_server:fail(session_not_reused) end, - Server ! listen, + Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, Client2 = - ssl_test_lib:start_client([{node, ClientNode}, + ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {mfa, {?MODULE, session_info_result, []}}, {from, self()}, {options, [{reuse_sessions, false} @@ -1665,10 +1658,6 @@ reuse_session(Config) when is_list(Config) -> end, ssl_test_lib:close(Server), - ssl_test_lib:close(Client0), - ssl_test_lib:close(Client1), - ssl_test_lib:close(Client2), - Server1 = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, @@ -1680,7 +1669,7 @@ reuse_session(Config) when is_list(Config) -> Client3 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port1}, {host, Hostname}, - {mfa, {?MODULE, session_info_result, []}}, + {mfa, {ssl_test_lib, no_result, []}}, {from, self()}, {options, ClientOpts}]), SessionInfo1 = @@ -1689,7 +1678,7 @@ reuse_session(Config) when is_list(Config) -> Info1 end, - Server1 ! listen, + Server1 ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, %% Make sure session is registered test_server:sleep(?SLEEP), @@ -1705,14 +1694,16 @@ reuse_session(Config) when is_list(Config) -> test_server:fail( session_reused_when_session_reuse_disabled_by_server); {Client4, _Other} -> + test_server:format("OTHER: ~p ~n", [_Other]), ok end, - + ssl_test_lib:close(Server1), + ssl_test_lib:close(Client0), + ssl_test_lib:close(Client1), + ssl_test_lib:close(Client2), ssl_test_lib:close(Client3), - ssl_test_lib:close(Client4), - process_flag(trap_exit, false). - + ssl_test_lib:close(Client4). session_info_result(Socket) -> ssl:session_info(Socket). @@ -1725,7 +1716,6 @@ reuse_session_expired(suite) -> []; reuse_session_expired(Config) when is_list(Config) -> - process_flag(trap_exit, true), ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), @@ -1746,8 +1736,8 @@ reuse_session_expired(Config) when is_list(Config) -> {Server, Info} -> Info end, - - Server ! listen, + + Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, %% Make sure session is registered test_server:sleep(?SLEEP), @@ -1767,7 +1757,7 @@ reuse_session_expired(Config) when is_list(Config) -> end, Server ! listen, - + %% Make sure session is unregistered due to expiration test_server:sleep((?EXPIRE+1) * 1000), @@ -1782,12 +1772,12 @@ reuse_session_expired(Config) when is_list(Config) -> {Client2, _} -> ok end, - + process_flag(trap_exit, false), ssl_test_lib:close(Server), ssl_test_lib:close(Client0), ssl_test_lib:close(Client1), - ssl_test_lib:close(Client2), - process_flag(trap_exit, false). + ssl_test_lib:close(Client2). + %%-------------------------------------------------------------------- server_does_not_want_to_reuse_session(doc) -> ["Test reuse of sessions (short handshake)"]; @@ -1820,10 +1810,11 @@ server_does_not_want_to_reuse_session(Config) when is_list(Config) -> Info end, - Server ! listen, + Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, %% Make sure session is registered test_server:sleep(?SLEEP), + ssl_test_lib:close(Client0), Client1 = ssl_test_lib:start_client([{node, ClientNode}, @@ -1836,11 +1827,9 @@ server_does_not_want_to_reuse_session(Config) when is_list(Config) -> {Client1, _Other} -> ok end, - + ssl_test_lib:close(Server), - ssl_test_lib:close(Client0), - ssl_test_lib:close(Client1), - process_flag(trap_exit, false). + ssl_test_lib:close(Client1). %%-------------------------------------------------------------------- @@ -2007,6 +1996,7 @@ server_verify_none_active_once(Config) when is_list(Config) -> ssl_test_lib:check_result(Server, ok, Client, ok), ssl_test_lib:close(Server), ssl_test_lib:close(Client). + %%-------------------------------------------------------------------- server_verify_client_once_passive(doc) -> @@ -2034,7 +2024,7 @@ server_verify_client_once_passive(Config) when is_list(Config) -> ssl_test_lib:check_result(Server, ok, Client0, ok), ssl_test_lib:close(Client0), - Server ! listen, + Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, @@ -2072,7 +2062,7 @@ server_verify_client_once_active(Config) when is_list(Config) -> ssl_test_lib:check_result(Server, ok, Client0, ok), ssl_test_lib:close(Client0), - Server ! listen, + Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, @@ -2083,7 +2073,6 @@ server_verify_client_once_active(Config) when is_list(Config) -> ssl_test_lib:close(Server), ssl_test_lib:close(Client1). - %%-------------------------------------------------------------------- server_verify_client_once_active_once(doc) -> @@ -2111,18 +2100,17 @@ server_verify_client_once_active_once(Config) when is_list(Config) -> ssl_test_lib:check_result(Server, ok, Client0, ok), ssl_test_lib:close(Client0), - Server ! listen, - + Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, result_ok, []}}, - {options, [{active, once} | ClientOpts]}]), + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, result_ok, []}}, + {options, [{active, once} | ClientOpts]}]), ssl_test_lib:check_result(Client1, ok), ssl_test_lib:close(Server), ssl_test_lib:close(Client1). - + %%-------------------------------------------------------------------- server_verify_no_cacerts(doc) -> @@ -2130,9 +2118,8 @@ server_verify_no_cacerts(doc) -> server_verify_no_cacerts(suite) -> []; - server_verify_no_cacerts(Config) when is_list(Config) -> - ServerOpts = ServerOpts = ?config(server_opts, Config), + ServerOpts = ?config(server_opts, Config), {_, ServerNode, _} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, {from, self()}, @@ -2293,8 +2280,6 @@ client_verify_none_active_once(Config) when is_list(Config) -> ssl_test_lib:close(Server), ssl_test_lib:close(Client). - - %%-------------------------------------------------------------------- client_renegotiate(doc) -> ["Test ssl:renegotiate/1 on client."]; @@ -2303,7 +2288,6 @@ client_renegotiate(suite) -> []; client_renegotiate(Config) when is_list(Config) -> - process_flag(trap_exit, true), ServerOpts = ?config(server_opts, Config), ClientOpts = ?config(client_opts, Config), @@ -2326,11 +2310,9 @@ client_renegotiate(Config) when is_list(Config) -> {options, [{reuse_sessions, false} | ClientOpts]}]), ssl_test_lib:check_result(Client, ok, Server, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client), - process_flag(trap_exit, false), - ok. + ssl_test_lib:close(Client). + %%-------------------------------------------------------------------- server_renegotiate(doc) -> ["Test ssl:renegotiate/1 on server."]; @@ -2339,7 +2321,6 @@ server_renegotiate(suite) -> []; server_renegotiate(Config) when is_list(Config) -> - process_flag(trap_exit, true), ServerOpts = ?config(server_opts, Config), ClientOpts = ?config(client_opts, Config), @@ -2362,8 +2343,7 @@ server_renegotiate(Config) when is_list(Config) -> ssl_test_lib:check_result(Server, ok, Client, ok), ssl_test_lib:close(Server), - ssl_test_lib:close(Client), - ok. + ssl_test_lib:close(Client). %%-------------------------------------------------------------------- client_renegotiate_reused_session(doc) -> @@ -2373,7 +2353,6 @@ client_renegotiate_reused_session(suite) -> []; client_renegotiate_reused_session(Config) when is_list(Config) -> - process_flag(trap_exit, true), ServerOpts = ?config(server_opts, Config), ClientOpts = ?config(client_opts, Config), @@ -2396,11 +2375,8 @@ client_renegotiate_reused_session(Config) when is_list(Config) -> {options, [{reuse_sessions, true} | ClientOpts]}]), ssl_test_lib:check_result(Client, ok, Server, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client), - process_flag(trap_exit, false), - ok. + ssl_test_lib:close(Client). %%-------------------------------------------------------------------- server_renegotiate_reused_session(doc) -> ["Test ssl:renegotiate/1 on server when the ssl session will be reused."]; @@ -2409,7 +2385,6 @@ server_renegotiate_reused_session(suite) -> []; server_renegotiate_reused_session(Config) when is_list(Config) -> - process_flag(trap_exit, true), ServerOpts = ?config(server_opts, Config), ClientOpts = ?config(client_opts, Config), @@ -2432,9 +2407,7 @@ server_renegotiate_reused_session(Config) when is_list(Config) -> ssl_test_lib:check_result(Server, ok, Client, ok), ssl_test_lib:close(Server), - ssl_test_lib:close(Client), - ok. - + ssl_test_lib:close(Client). %%-------------------------------------------------------------------- client_no_wrap_sequence_number(doc) -> ["Test that erlang client will renegotiate session when", @@ -2446,7 +2419,6 @@ client_no_wrap_sequence_number(suite) -> []; client_no_wrap_sequence_number(Config) when is_list(Config) -> - process_flag(trap_exit, true), ServerOpts = ?config(server_opts, Config), ClientOpts = ?config(client_opts, Config), @@ -2471,11 +2443,8 @@ client_no_wrap_sequence_number(Config) when is_list(Config) -> {renegotiate_at, N} | ClientOpts]}]), ssl_test_lib:check_result(Client, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client), - process_flag(trap_exit, false), - ok. + ssl_test_lib:close(Client). %%-------------------------------------------------------------------- server_no_wrap_sequence_number(doc) -> ["Test that erlang server will renegotiate session when", @@ -2487,7 +2456,6 @@ server_no_wrap_sequence_number(suite) -> []; server_no_wrap_sequence_number(Config) when is_list(Config) -> - process_flag(trap_exit, true), ServerOpts = ?config(server_opts, Config), ClientOpts = ?config(client_opts, Config), @@ -2511,9 +2479,7 @@ server_no_wrap_sequence_number(Config) when is_list(Config) -> ssl_test_lib:check_result(Server, ok), ssl_test_lib:close(Server), - ssl_test_lib:close(Client), - ok. - + ssl_test_lib:close(Client). %%-------------------------------------------------------------------- extended_key_usage(doc) -> ["Test cert that has a critical extended_key_usage extension"]; @@ -2885,9 +2851,7 @@ unknown_server_ca_fail(Config) when is_list(Config) -> | ClientOpts]}]), ssl_test_lib:check_result(Server, {error,"unknown ca"}, - Client, {error, "unknown ca"}), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). + Client, {error, "unknown ca"}). %%-------------------------------------------------------------------- unknown_server_ca_accept_verify_none(doc) -> @@ -3052,12 +3016,205 @@ der_input_opts(Opts) -> {Cert, {rsa, Key}, CaCerts, DHParams}. %%-------------------------------------------------------------------- +%% different_ca_peer_sign(doc) -> +%% ["Check that a CA can have a different signature algorithm than the peer cert."]; + +%% different_ca_peer_sign(suite) -> +%% []; + +%% different_ca_peer_sign(Config) when is_list(Config) -> +%% ClientOpts = ?config(client_mix_opts, Config), +%% ServerOpts = ?config(server_mix_verify_opts, Config), + +%% {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), +%% Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, +%% {from, self()}, +%% {mfa, {?MODULE, send_recv_result_active_once, []}}, +%% {options, [{active, once}, +%% {verify, verify_peer} | ServerOpts]}]), +%% Port = ssl_test_lib:inet_port(Server), + +%% Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, +%% {host, Hostname}, +%% {from, self()}, +%% {mfa, {?MODULE, +%% send_recv_result_active_once, +%% []}}, +%% {options, [{active, once}, +%% {verify, verify_peer} +%% | ClientOpts]}]), + +%% ssl_test_lib:check_result(Server, ok, Client, ok), +%% ssl_test_lib:close(Server), +%% ssl_test_lib:close(Client). + + +%%-------------------------------------------------------------------- +no_reuses_session_server_restart_new_cert(doc) -> + ["Check that a session is not reused if the server is restarted with a new cert."]; + +no_reuses_session_server_restart_new_cert(suite) -> + []; + +no_reuses_session_server_restart_new_cert(Config) when is_list(Config) -> + + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + DsaServerOpts = ?config(server_dsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, session_info_result, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client0 = + ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {mfa, {ssl_test_lib, no_result, []}}, + {from, self()}, {options, ClientOpts}]), + SessionInfo = + receive + {Server, Info} -> + Info + end, + + %% Make sure session is registered + test_server:sleep(?SLEEP), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client0), + + Server1 = + ssl_test_lib:start_server([{node, ServerNode}, {port, Port}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, DsaServerOpts}]), + + Client1 = + ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {mfa, {?MODULE, session_info_result, []}}, + {from, self()}, {options, ClientOpts}]), + receive + {Client1, SessionInfo} -> + test_server:fail(session_reused_when_server_has_new_cert); + {Client1, _Other} -> + ok + end, + ssl_test_lib:close(Server1), + ssl_test_lib:close(Client1). + +%%-------------------------------------------------------------------- +no_reuses_session_server_restart_new_cert_file(doc) -> + ["Check that a session is not reused if a server is restarted with a new " + "cert contained in a file with the same name as the old cert."]; + +no_reuses_session_server_restart_new_cert_file(suite) -> + []; + +no_reuses_session_server_restart_new_cert_file(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_verification_opts, Config), + DsaServerOpts = ?config(server_dsa_opts, Config), + PrivDir = ?config(priv_dir, Config), + + NewServerOpts = new_config(PrivDir, ServerOpts), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, session_info_result, []}}, + {options, NewServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client0 = + ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {mfa, {ssl_test_lib, no_result, []}}, + {from, self()}, {options, ClientOpts}]), + SessionInfo = + receive + {Server, Info} -> + Info + end, + + %% Make sure session is registered and we get + %% new file time stamp when calling new_config! + test_server:sleep(?SLEEP* 2), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client0), + + NewServerOpts = new_config(PrivDir, DsaServerOpts), + + Server1 = + ssl_test_lib:start_server([{node, ServerNode}, {port, Port}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, NewServerOpts}]), + Client1 = + ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {mfa, {?MODULE, session_info_result, []}}, + {from, self()}, {options, ClientOpts}]), + receive + {Client1, SessionInfo} -> + test_server:fail(session_reused_when_server_has_new_cert); + {Client1, _Other} -> + ok + end, + ssl_test_lib:close(Server1), + ssl_test_lib:close(Client1). + +%%-------------------------------------------------------------------- +reuseaddr(doc) -> + [""]; + +reuseaddr(suite) -> + []; + +reuseaddr(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, [{active, false} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = + ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, [{active, false} | ClientOpts]}]), + test_server:sleep(?SLEEP), + ssl_test_lib:close(Server), + + Server1 = + ssl_test_lib:start_server([{node, ServerNode}, {port, Port}, + {from, self()}, + {mfa, {?MODULE, send_recv_result, []}}, + {options, [{active, false} | ServerOpts]}]), + Client1 = + ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, send_recv_result, []}}, + {options, [{active, false} | ClientOpts]}]), + + ssl_test_lib:check_result(Server1, ok, Client1, ok), + ssl_test_lib:close(Server1), + ssl_test_lib:close(Client1). + +%%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- erlang_ssl_receive(Socket, Data) -> receive {ssl, Socket, Data} -> - io:format("Received ~p~n",[Data]), + test_server:format("Received ~p~n",[Data]), ok; Other -> test_server:fail({unexpected_message, Other}) @@ -3087,7 +3244,6 @@ send_recv_result_active_once(Socket) -> result_ok(_Socket) -> ok. - renegotiate(Socket, Data) -> test_server:format("Renegotiating ~n", []), Result = ssl:renegotiate(Socket), @@ -3104,3 +3260,24 @@ renegotiate_reuse_session(Socket, Data) -> %% Make sure session is registerd test_server:sleep(?SLEEP), renegotiate(Socket, Data). + + +new_config(PrivDir, ServerOpts0) -> + CaCertFile = proplists:get_value(cacertfile, ServerOpts0), + CertFile = proplists:get_value(certfile, ServerOpts0), + KeyFile = proplists:get_value(keyfile, ServerOpts0), + NewCaCertFile = filename:join(PrivDir, "new_ca.pem"), + NewCertFile = filename:join(PrivDir, "new_cert.pem"), + NewKeyFile = filename:join(PrivDir, "new_key.pem"), + file:copy(CaCertFile, NewCaCertFile), + file:copy(CertFile, NewCertFile), + file:copy(KeyFile, NewKeyFile), + ServerOpts1 = proplists:delete(cacertfile, ServerOpts0), + ServerOpts2 = proplists:delete(certfile, ServerOpts1), + ServerOpts = proplists:delete(keyfile, ServerOpts2), + + {ok, PEM} = file:read_file(NewCaCertFile), + test_server:format("CA file content: ~p~n", [public_key:pem_decode(PEM)]), + + [{cacertfile, NewCaCertFile}, {certfile, NewCertFile}, + {keyfile, NewKeyFile} | ServerOpts]. diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl index e1e8214ed6..f6ccbe85e3 100644 --- a/lib/ssl/test/ssl_test_lib.erl +++ b/lib/ssl/test/ssl_test_lib.erl @@ -81,14 +81,20 @@ run_server(ListenSocket, Opts) -> no_result_msg -> ok; Msg -> - test_server:format("Msg: ~p ~n", [Msg]), + test_server:format("Server Msg: ~p ~n", [Msg]), Pid ! {self(), Msg} end, - receive + receive listen -> run_server(ListenSocket, Opts); + {listen, MFA} -> + run_server(ListenSocket, [MFA | proplists:delete(mfa, Opts)]); close -> - ok = rpc:call(Node, ssl, close, [AcceptSocket]) + test_server:format("Server closing ~p ~n", [self()]), + Result = rpc:call(Node, ssl, close, [AcceptSocket], 500), + test_server:format("Result ~p ~n", [Result]); + {ssl_closed, _} -> + ok end. %%% To enable to test with s_client -reconnect @@ -151,19 +157,30 @@ run_client(Opts) -> no_result_msg -> ok; Msg -> + test_server:format("Client Msg: ~p ~n", [Msg]), Pid ! {self(), Msg} end, - receive + receive close -> - ok = rpc:call(Node, ssl, close, [Socket]) + test_server:format("Client closing~n", []), + rpc:call(Node, ssl, close, [Socket]); + {ssl_closed, Socket} -> + ok end; {error, Reason} -> - test_server:format("Client: connection failed: ~p ~n", [Reason]), + test_server:format("Client: connection failed: ~p ~n", [Reason]), Pid ! {self(), {error, Reason}} end. close(Pid) -> - Pid ! close. + test_server:format("Close ~p ~n", [Pid]), + Monitor = erlang:monitor(process, Pid), + Pid ! close, + receive + {'DOWN', Monitor, process, Pid, Reason} -> + erlang:demonitor(Monitor), + test_server:format("Pid: ~p down due to:~p ~n", [Pid, Reason]) + end. check_result(Server, ServerMsg, Client, ClientMsg) -> receive @@ -208,47 +225,27 @@ check_result(Pid, Msg) -> test_server:fail(Reason) end. -check_result_ignore_renegotiation_reject(Pid, Msg) -> - receive - {Pid, fail_session_fatal_alert_during_renegotiation} -> - test_server:comment("Server rejected old renegotiation"), - ok; - {ssl_error, _, esslconnect} -> - test_server:comment("Server rejected old renegotiation"), - ok; - {Pid, Msg} -> - ok; - {Port, {data,Debug}} when is_port(Port) -> - io:format("openssl ~s~n",[Debug]), - check_result(Pid,Msg); - Unexpected -> - Reason = {{expected, {Pid, Msg}}, - {got, Unexpected}}, - test_server:fail(Reason) - end. - - wait_for_result(Server, ServerMsg, Client, ClientMsg) -> receive {Server, ServerMsg} -> receive {Client, ClientMsg} -> - ok; - Unexpected -> - Unexpected + ok + %% Unexpected -> + %% Unexpected end; {Client, ClientMsg} -> receive {Server, ServerMsg} -> - ok; - Unexpected -> - Unexpected + ok + %% Unexpected -> + %% Unexpected end; {Port, {data,Debug}} when is_port(Port) -> io:format("openssl ~s~n",[Debug]), - wait_for_result(Server, ServerMsg, Client, ClientMsg); - Unexpected -> - Unexpected + wait_for_result(Server, ServerMsg, Client, ClientMsg) + %% Unexpected -> + %% Unexpected end. @@ -258,9 +255,9 @@ wait_for_result(Pid, Msg) -> ok; {Port, {data,Debug}} when is_port(Port) -> io:format("openssl ~s~n",[Debug]), - wait_for_result(Pid,Msg); - Unexpected -> - Unexpected + wait_for_result(Pid,Msg) + %% Unexpected -> + %% Unexpected end. cert_options(Config) -> @@ -327,8 +324,8 @@ cert_options(Config) -> make_dsa_cert(Config) -> - {ServerCaCertFile, ServerCertFile, ServerKeyFile} = make_dsa_cert_files("server", Config), - {ClientCaCertFile, ClientCertFile, ClientKeyFile} = make_dsa_cert_files("client", Config), + {ServerCaCertFile, ServerCertFile, ServerKeyFile} = make_cert_files("server", Config, dsa, dsa, ""), + {ClientCaCertFile, ClientCertFile, ClientKeyFile} = make_cert_files("client", Config, dsa, dsa, ""), [{server_dsa_opts, [{ssl_imp, new},{reuseaddr, true}, {cacertfile, ServerCaCertFile}, {certfile, ServerCertFile}, {keyfile, ServerKeyFile}]}, @@ -342,22 +339,41 @@ make_dsa_cert(Config) -> | Config]. - -make_dsa_cert_files(RoleStr, Config) -> - CaInfo = {CaCert, _} = erl_make_certs:make_cert([{key, dsa}]), - {Cert, CertKey} = erl_make_certs:make_cert([{key, dsa}, {issuer, CaInfo}]), +make_mix_cert(Config) -> + {ServerCaCertFile, ServerCertFile, ServerKeyFile} = make_cert_files("server", Config, dsa, + rsa, "mix"), + {ClientCaCertFile, ClientCertFile, ClientKeyFile} = make_cert_files("client", Config, dsa, + rsa, "mix"), + [{server_mix_opts, [{ssl_imp, new},{reuseaddr, true}, + {cacertfile, ServerCaCertFile}, + {certfile, ServerCertFile}, {keyfile, ServerKeyFile}]}, + {server_mix_verify_opts, [{ssl_imp, new},{reuseaddr, true}, + {cacertfile, ClientCaCertFile}, + {certfile, ServerCertFile}, {keyfile, ServerKeyFile}, + {verify, verify_peer}]}, + {client_mix_opts, [{ssl_imp, new},{reuseaddr, true}, + {cacertfile, ClientCaCertFile}, + {certfile, ClientCertFile}, {keyfile, ClientKeyFile}]} + | Config]. + +make_cert_files(RoleStr, Config, Alg1, Alg2, Prefix) -> + Alg1Str = atom_to_list(Alg1), + Alg2Str = atom_to_list(Alg2), + CaInfo = {CaCert, _} = erl_make_certs:make_cert([{key, Alg1}]), + {Cert, CertKey} = erl_make_certs:make_cert([{key, Alg2}, {issuer, CaInfo}]), CaCertFile = filename:join([?config(priv_dir, Config), - RoleStr, "dsa_cacerts.pem"]), + RoleStr, Prefix ++ Alg1Str ++ "_cacerts.pem"]), CertFile = filename:join([?config(priv_dir, Config), - RoleStr, "dsa_cert.pem"]), + RoleStr, Prefix ++ Alg2Str ++ "_cert.pem"]), KeyFile = filename:join([?config(priv_dir, Config), - RoleStr, "dsa_key.pem"]), + RoleStr, Prefix ++ Alg2Str ++ "_key.pem"]), der_to_pem(CaCertFile, [{'Certificate', CaCert, not_encrypted}]), der_to_pem(CertFile, [{'Certificate', Cert, not_encrypted}]), der_to_pem(KeyFile, [CertKey]), {CaCertFile, CertFile, KeyFile}. + start_upgrade_server(Args) -> Result = spawn_link(?MODULE, run_upgrade_server, [Args]), receive @@ -395,10 +411,12 @@ run_upgrade_server(Opts) -> end, {Module, Function, Args} = proplists:get_value(mfa, Opts), Msg = rpc:call(Node, Module, Function, [SslAcceptSocket | Args]), + test_server:format("Upgrade Server Msg: ~p ~n", [Msg]), Pid ! {self(), Msg}, receive close -> - ok = rpc:call(Node, ssl, close, [SslAcceptSocket]) + test_server:format("Upgrade Server closing~n", []), + rpc:call(Node, ssl, close, [SslAcceptSocket]) end catch error:{badmatch, Error} -> Pid ! {self(), Error} @@ -428,10 +446,12 @@ run_upgrade_client(Opts) -> test_server:format("apply(~p, ~p, ~p)~n", [Module, Function, [SslSocket | Args]]), Msg = rpc:call(Node, Module, Function, [SslSocket | Args]), + test_server:format("Upgrade Client Msg: ~p ~n", [Msg]), Pid ! {self(), Msg}, receive close -> - ok = rpc:call(Node, ssl, close, [SslSocket]) + test_server:format("Upgrade Client closing~n", []), + rpc:call(Node, ssl, close, [SslSocket]) end. start_upgrade_server_error(Args) -> diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl index afedeaf099..46ad0c17b6 100644 --- a/lib/ssl/test/ssl_to_openssl_SUITE.erl +++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl @@ -1164,10 +1164,6 @@ cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) -> close_port(OpenSslPort), %% Clean close down! ssl_test_lib:close(Client), - receive - {'EXIT', Client, normal} -> - ok - end, Return = case Result of ok -> diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk index ee692adb3b..b1ae0db7e7 100644 --- a/lib/ssl/vsn.mk +++ b/lib/ssl/vsn.mk @@ -1,2 +1,2 @@ -SSL_VSN = 4.1.1 +SSL_VSN = 4.1.2 diff --git a/lib/stdlib/doc/src/filelib.xml b/lib/stdlib/doc/src/filelib.xml index 969aff4fcb..47d64f245c 100644 --- a/lib/stdlib/doc/src/filelib.xml +++ b/lib/stdlib/doc/src/filelib.xml @@ -105,7 +105,7 @@ dirname() = filename()</code> interpreted as Unicode may be encountered, in which case the <c>fun()</c> must be prepared to handle raw file names (i.e. binaries). If the regular expression contains - codepoints beyond 255, it will not match file names that does + codepoints beyond 255, it will not match file names that do not conform to the expected character encoding (i.e. are not encoded in valid UTF-8).</p> diff --git a/lib/stdlib/doc/src/notes.xml b/lib/stdlib/doc/src/notes.xml index 940044d0a8..a8fe41f000 100644 --- a/lib/stdlib/doc/src/notes.xml +++ b/lib/stdlib/doc/src/notes.xml @@ -30,6 +30,24 @@ </header> <p>This document describes the changes made to the STDLIB application.</p> +<section><title>STDLIB 1.17.2.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Several type specifications for standard libraries were + wrong in the R14B01 release. This is now corrected. The + corrections concern types in re,io,filename and the + module erlang itself.</p> + <p> + Own Id: OTP-9008</p> + </item> + </list> + </section> + +</section> + <section><title>STDLIB 1.17.2</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/stdlib/doc/src/unicode_usage.xml b/lib/stdlib/doc/src/unicode_usage.xml index c02ea3cbcb..416df1f02c 100644 --- a/lib/stdlib/doc/src/unicode_usage.xml +++ b/lib/stdlib/doc/src/unicode_usage.xml @@ -173,19 +173,19 @@ Eshell V5.7 (abort with ^G) <taglist> <tag>Mandatory Unicode file naming</tag> <item> -<p>Windows and, for most common uses, MacOSX enforces Unicode support for file names. All files created in the filesystem has names that can consistently be interpreted. In MacOSX, all file names are retrieved in UTF-8 encoding, while Windows have selected an approach where each system call handling file names has a special Unicode aware variant, giving much the same effect. There are no file names on these systems that are not Unicode file names, why the default behavior of the Erlang VM is to work in "Unicode file name translation mode", meaning that a file name can be given as a Unicode list and that will be automatically translated to the proper name encoding for the underlying operating and file system.</p> +<p>Windows and, for most common uses, MacOSX enforces Unicode support for file names. All files created in the filesystem have names that can consistently be interpreted. In MacOSX, all file names are retrieved in UTF-8 encoding, while Windows has selected an approach where each system call handling file names has a special Unicode aware variant, giving much the same effect. There are no file names on these systems that are not Unicode file names, why the default behavior of the Erlang VM is to work in "Unicode file name translation mode", meaning that a file name can be given as a Unicode list and that will be automatically translated to the proper name encoding for the underlying operating and file system.</p> <p>Doing i.e. a <c>file:list_dir/1</c> on one of these systems may return Unicode lists with codepoints beyond 255, depending on the content of the actual filesystem.</p> <p>As the feature is fairly new, you may still stumble upon non core applications that cannot handle being provided with file names containing characters with codepoints larger than 255, but the core Erlang system should have no problems with Unicode file names.</p> </item> <tag>Transparent file naming</tag> <item> <p>Most Unix operating systems have adopted a simpler approach, namely that Unicode file naming is not enforced, but by convention. Those systems usually use UTF-8 encoding for Unicode file names, but do not enforce it. On such a system, a file name containing characters having codepoints between 128 and 255 may be named either as plain ISO-latin-1 or using UTF-8 encoding. As no consistency is enforced, the Erlang VM can do no consistent translation of all file names. If the VM would automatically select encoding based on heuristics, one could get unexpected behavior on these systems, therefore file names not being encoded in UTF-8 are returned as "raw file names" if Unicode file naming support is turned on.</p> -<p>A raw file name is not a list, but a binary. Many non core applications still does not handle file names given as binaries, why such raw names are avoided by default. This means that systems having implemented Unicode file naming through transparent file systems and an UTF-8 convention, does not by default have Unicode file naming turned on. Explicitly turning Unicode file name handling on for these types of systems is considered experimental.</p> +<p>A raw file name is not a list, but a binary. Many non core applications still do not handle file names given as binaries, why such raw names are avoided by default. This means that systems having implemented Unicode file naming through transparent file systems and an UTF-8 convention, do not by default have Unicode file naming turned on. Explicitly turning Unicode file name handling on for these types of systems is considered experimental.</p> </item> </taglist> <p>The Unicode file naming support was introduced with OTP release R14B01. A VM operating in Unicode file mode can work with files having names in any language or character set (as long as it's supported by the underlying OS and file system). The Unicode character list is used to denote file or directory names and if the file system content is listed, you will also be able to get Unicode lists as return value. The support lies in the kernel and stdlib modules, why most applications (that does not explicitly require the file names to be in the ISO-latin-1 range) will benefit from the Unicode support without change.</p> -<p>On Operating systems with mandatory Unicode file names, this means that you more easily conform to the file names of other (non Erlang) applications, and you can also process file names that, at least on Windows, where completely inaccessible (due to having names that could not be represented in ISO-latin-1). Also you will avoid creating incomprehensible file names on MacOSX as the vfs layer of the OS will accept all your file names as UTF-8 and will not rewrite them.</p> +<p>On Operating systems with mandatory Unicode file names, this means that you more easily conform to the file names of other (non Erlang) applications, and you can also process file names that, at least on Windows, were completely inaccessible (due to having names that could not be represented in ISO-latin-1). Also you will avoid creating incomprehensible file names on MacOSX as the vfs layer of the OS will accept all your file names as UTF-8 and will not rewrite them.</p> <p>For most systems, turning on Unicode file name translation is no problem even if it uses transparent file naming. Very few systems have mixed file name encodings. A consistent UTF-8 named system will work perfectly in Unicode file name mode. It is still however considered experimental in R14B01. Unicode file name translation is turned on with the <c>+fnu</c> switch to the <c>erl</c> program. If the VM is started in Unicode file name translation mode, <c>file:native_name_encoding/0</c> will return the atom <c>utf8</c>.</p> @@ -197,10 +197,10 @@ Eshell V5.7 (abort with ^G) <section> <title>Notes about raw file names and automatic file name conversion</title> -<p>Raw file names is introduced together with Unicode file name support in erts-5.8.2 (OTP R14B01). The reason the "raw file names" is introduced in the system is to be able to consistently represent file names given in different encodings on the same system. Having the VM automatically translate a file name that is not in UTF-8 when to a list of Unicode characters might seem practical, but this would open up for both duplicate file names and other inconsistent behavior. Consider a directory containing a file named "bj�rn" in ISO-latin-1, while the Erlang VM is operating in Unicode file name mode (and therefore expecting UTF-8 file naming). The ISO-latin-1 name is not valid UTF-8 and one could be tempted to think that automatic conversion in for example <c>file:list_dir/1</c> is a good idea. But what would happen if we later tried to open the file and has the name as a Unicode list (magically converted from the ISO-latin-1 file name)? The VM will convert the file name given to UTF-8, as this is the encoding expected. Effectively this means trying to open the file named <<"bj�rn"/utf8>>. This file does not exist, and even if it existed it would not be the same file as the one that was listed. We could even create two files named "bj�rn", one named in the UTF-8 encoding and one not. If <c>file:list_dir/1</c> would automatically convert the ISO-latin-1 file name to a list, we would get two identical file names as the result. To avoid this, we need to differentiate between file names being properly encoded according to the Unicode file naming convention (i.e. UTF-8) and file names being invalid under the encoding. This is done by representing invalid encoding as "raw" file names, i.e. as binaries.</p> -<p>The core system of Erlang (kernel and stdlib) accept raw file names except for loadable drivers and executables invoked using <c>open_port({spawn, ...} ...)</c>. <c>open_port({spawn_executable, ...} ...)</c> however does accept them. As mentioned earlier, the arguments given in the option list to <c>open_port({spawn_executable, ...} ...)</c> undergo the same conversion as the file names, meaning that the executable will be provided with arguments in UTF-8 as well. This translation is avoided consistently with how the file names are treated, by giving the argument as a binary.</p> +<p>Raw file names is introduced together with Unicode file name support in erts-5.8.2 (OTP R14B01). The reason "raw file names" is introduced in the system is to be able to consistently represent file names given in different encodings on the same system. Having the VM automatically translate a file name that is not in UTF-8 to a list of Unicode characters might seem practical, but this would open up for both duplicate file names and other inconsistent behavior. Consider a directory containing a file named "bj�rn" in ISO-latin-1, while the Erlang VM is operating in Unicode file name mode (and therefore expecting UTF-8 file naming). The ISO-latin-1 name is not valid UTF-8 and one could be tempted to think that automatic conversion in for example <c>file:list_dir/1</c> is a good idea. But what would happen if we later tried to open the file and have the name as a Unicode list (magically converted from the ISO-latin-1 file name)? The VM will convert the file name given to UTF-8, as this is the encoding expected. Effectively this means trying to open the file named <<"bj�rn"/utf8>>. This file does not exist, and even if it existed it would not be the same file as the one that was listed. We could even create two files named "bj�rn", one named in the UTF-8 encoding and one not. If <c>file:list_dir/1</c> would automatically convert the ISO-latin-1 file name to a list, we would get two identical file names as the result. To avoid this, we need to differentiate between file names being properly encoded according to the Unicode file naming convention (i.e. UTF-8) and file names being invalid under the encoding. This is done by representing invalid encoding as "raw" file names, i.e. as binaries.</p> +<p>The core system of Erlang (kernel and stdlib) accepts raw file names except for loadable drivers and executables invoked using <c>open_port({spawn, ...} ...)</c>. <c>open_port({spawn_executable, ...} ...)</c> however does accept them. As mentioned earlier, the arguments given in the option list to <c>open_port({spawn_executable, ...} ...)</c> undergo the same conversion as the file names, meaning that the executable will be provided with arguments in UTF-8 as well. This translation is avoided consistently with how the file names are treated, by giving the argument as a binary.</p> <p>To force Unicode file name translation mode on systems where this is not the default is considered experimental in OTP R14B01 due to the raw file names possibly being a new experience to the programmer and that the non core applications of OTP are not tested for compliance with raw file names yet. Unicode file name translation is expected to be default in future releases.</p> -<p>If working with raw file names, one can still conform to the encoding convention of the Erlang VM by using the <c>file:native_name_encoding/0</c> function, which returns either the atom <c>latin1</c> or the atom <c>utf8</c> depending on the file name translation mode. On Linux, an VM started without explicitly stating the file name translation mode will default to <c>latin1</c> as the native file name encoding, why file names on the disk encoded as UTF-8 will be returned as a list of the names interpreted as ISO-latin-1. The "UTF-8 list" is not a practical type for displaying or operating on in Erlang, but it is backward compatible and usable in all functions requiring a file name. On Windows and MacOSX, the default behavior is that of file name translation, why the <c>file:native_name_encoding/0</c> by default returns <c>utf8</c> on those systems (the fact that Windows actually does not use UTF-8 on the file system level can safely be ignored by the Erlang programmer). The default behavior can be changed using the <c>+fnu</c> or <c>+fnl</c> options to the VM, see the <c>erl</c> command manual page.</p> +<p>If working with raw file names, one can still conform to the encoding convention of the Erlang VM by using the <c>file:native_name_encoding/0</c> function, which returns either the atom <c>latin1</c> or the atom <c>utf8</c> depending on the file name translation mode. On Linux, a VM started without explicitly stating the file name translation mode will default to <c>latin1</c> as the native file name encoding, why file names on the disk encoded as UTF-8 will be returned as a list of the names interpreted as ISO-latin-1. The "UTF-8 list" is not a practical type for displaying or operating on in Erlang, but it is backward compatible and usable in all functions requiring a file name. On Windows and MacOSX, the default behavior is that of file name translation, why the <c>file:native_name_encoding/0</c> by default returns <c>utf8</c> on those systems (the fact that Windows actually does not use UTF-8 on the file system level can safely be ignored by the Erlang programmer). The default behavior can be changed using the <c>+fnu</c> or <c>+fnl</c> options to the VM, see the <c>erl</c> command manual page.</p> <p>Even if you are operating without Unicode file naming translation automatically done by the VM, you can access and create files with names in UTF-8 encoding by using raw file names encoded as UTF-8. Enforcing the UTF-8 encoding regardless of the mode the Erlang VM is started in might, in some circumstances be a good idea, as the convention of using UTF-8 file names is spreading.</p> </section> <section> diff --git a/lib/stdlib/src/base64.erl b/lib/stdlib/src/base64.erl index ebef998ee1..a14a72ac6d 100644 --- a/lib/stdlib/src/base64.erl +++ b/lib/stdlib/src/base64.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2009. All Rights Reserved. +%% Copyright Ericsson AB 2007-2011. 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 @@ -114,7 +114,7 @@ decode(List) when is_list(List) -> mime_decode(Bin) when is_binary(Bin) -> mime_decode_binary(<<>>, Bin); mime_decode(List) when is_list(List) -> - list_to_binary(mime_decode_l(List)). + mime_decode(list_to_binary(List)). -spec decode_l(string()) -> string(). @@ -125,7 +125,7 @@ decode_l(List) -> -spec mime_decode_l(string()) -> string(). mime_decode_l(List) -> - L = strip_illegal(List, []), + L = strip_illegal(List, [], 0), decode(L, []). %%------------------------------------------------------------------------- @@ -198,6 +198,9 @@ decode_binary(Result, <<>>) -> true = is_binary(Result), Result. +%% Skipping pad character if not at end of string. Also liberal about +%% excess padding and skipping of other illegal (non-base64 alphabet) +%% characters. See section 3.3 of RFC4648 mime_decode_binary(Result, <<0:8,T/bits>>) -> mime_decode_binary(Result, T); mime_decode_binary(Result0, <<C:8,T/bits>>) -> @@ -205,15 +208,27 @@ mime_decode_binary(Result0, <<C:8,T/bits>>) -> Bits when is_integer(Bits) -> mime_decode_binary(<<Result0/bits,Bits:6>>, T); eq -> - case tail_contains_equal(T) of - true -> - Split = byte_size(Result0) - 1, - <<Result:Split/bytes,_:4>> = Result0, - Result; - false -> - Split = byte_size(Result0) - 1, - <<Result:Split/bytes,_:2>> = Result0, - Result + case tail_contains_more(T, false) of + {<<>>, Eq} -> + %% No more valid data. + case bit_size(Result0) rem 8 of + 0 -> + %% '====' is not uncommon. + Result0; + 4 when Eq -> + %% enforce at least one more '=' only ignoring illegals and spacing + Split = byte_size(Result0) - 1, + <<Result:Split/bytes,_:4>> = Result0, + Result; + 2 -> + %% remove 2 bits + Split = byte_size(Result0) - 1, + <<Result:Split/bytes,_:2>> = Result0, + Result + end; + {More, _} -> + %% More valid data, skip the eq as invalid + mime_decode_binary(Result0, More) end; _ -> mime_decode_binary(Result0, T) @@ -262,31 +277,63 @@ strip_ws(<<$\s,T/binary>>) -> strip_ws(T); strip_ws(T) -> T. -strip_illegal([0|Cs], A) -> - strip_illegal(Cs, A); -strip_illegal([C|Cs], A) -> +%% Skipping pad character if not at end of string. Also liberal about +%% excess padding and skipping of other illegal (non-base64 alphabet) +%% characters. See section 3.3 of RFC4648 +strip_illegal([], A, _Cnt) -> + A; +strip_illegal([0|Cs], A, Cnt) -> + strip_illegal(Cs, A, Cnt); +strip_illegal([C|Cs], A, Cnt) -> case element(C, ?DECODE_MAP) of - bad -> strip_illegal(Cs, A); - ws -> strip_illegal(Cs, A); - eq -> strip_illegal_end(Cs, [$=|A]); - _ -> strip_illegal(Cs, [C|A]) - end; -strip_illegal([], A) -> A. + bad -> + strip_illegal(Cs, A, Cnt); + ws -> + strip_illegal(Cs, A, Cnt); + eq -> + case {tail_contains_more(Cs, false), Cnt rem 4} of + {{[], _}, 0} -> + A; %% Ignore extra = + {{[], true}, 2} -> + [$=|[$=|A]]; %% 'XX==' + {{[], _}, 3} -> + [$=|A]; %% 'XXX=' + {{[H|T], _}, _} -> + %% more data, skip equals + strip_illegal(T, [H|A], Cnt+1) + end; + _ -> + strip_illegal(Cs, [C|A], Cnt+1) + end. -strip_illegal_end([0|Cs], A) -> - strip_illegal_end(Cs, A); -strip_illegal_end([C|Cs], A) -> +%% Search the tail for more valid data and remember if we saw +%% another equals along the way. +tail_contains_more([], Eq) -> + {[], Eq}; +tail_contains_more(<<>>, Eq) -> + {<<>>, Eq}; +tail_contains_more([C|T]=More, Eq) -> case element(C, ?DECODE_MAP) of - bad -> strip_illegal(Cs, A); - ws -> strip_illegal(Cs, A); - eq -> [C|A]; - _ -> strip_illegal(Cs, [C|A]) + bad -> + tail_contains_more(T, Eq); + ws -> + tail_contains_more(T, Eq); + eq -> + tail_contains_more(T, true); + _ -> + {More, Eq} end; -strip_illegal_end([], A) -> A. - -tail_contains_equal(<<$=,_/binary>>) -> true; -tail_contains_equal(<<_,T/binary>>) -> tail_contains_equal(T); -tail_contains_equal(<<>>) -> false. +tail_contains_more(<<C:8,T/bits>> =More, Eq) -> + case element(C, ?DECODE_MAP) of + bad -> + tail_contains_more(T, Eq); + ws -> + tail_contains_more(T, Eq); + eq -> + tail_contains_more(T, true); + _ -> + {More, Eq} + end. %% accessors b64e(X) -> diff --git a/lib/stdlib/src/escript.erl b/lib/stdlib/src/escript.erl index 99e454f593..7cb02afb11 100644 --- a/lib/stdlib/src/escript.erl +++ b/lib/stdlib/src/escript.erl @@ -570,9 +570,7 @@ parse_beam(S, File, HeaderSz, CheckOnly) -> forms_or_bin = Bin} end; {error, beam_lib, Reason} when is_tuple(Reason) -> - fatal(element(1, Reason)); - {error, beam_lib, Reason} -> - fatal(Reason) + fatal(element(1, Reason)) end. parse_source(S, File, Fd, StartLine, HeaderSz, CheckOnly) -> diff --git a/lib/stdlib/src/filename.erl b/lib/stdlib/src/filename.erl index e38b8957f2..24abf1e977 100644 --- a/lib/stdlib/src/filename.erl +++ b/lib/stdlib/src/filename.erl @@ -165,8 +165,6 @@ basename1([$/|[]], Tail, DirSep2) -> basename1([], Tail, DirSep2); basename1([$/|Rest], _Tail, DirSep2) -> basename1(Rest, [], DirSep2); -basename1([[_|_]=List|Rest], Tail, DirSep2) -> - basename1(List++Rest, Tail, DirSep2); basename1([DirSep2|Rest], Tail, DirSep2) when is_integer(DirSep2) -> basename1([$/|Rest], Tail, DirSep2); basename1([Char|Rest], Tail, DirSep2) when is_integer(Char) -> @@ -280,8 +278,6 @@ dirname(Name0) -> Name = flatten(Name0), dirname(Name, [], [], separators()). -dirname([[_|_]=List|Rest], Dir, File, Seps) -> - dirname(List++Rest, Dir, File, Seps); dirname([$/|Rest], Dir, File, Seps) -> dirname(Rest, File++Dir, [$/], Seps); dirname([DirSep|Rest], Dir, File, {DirSep,_}=Seps) when is_integer(DirSep) -> @@ -346,8 +342,6 @@ extension(Name) when is_binary(Name) -> [] end, case binary:matches(Name,[<<".">>]) of - nomatch -> % Bug in binary workaround :( - <<>>; [] -> <<>>; List -> @@ -479,6 +473,12 @@ maybe_remove_dirsep(Name, _) -> %% by a previous call to join/{1,2}. -spec append(file:filename(), file:name()) -> file:filename(). +append(Dir, Name) when is_binary(Dir), is_binary(Name) -> + <<Dir/binary,$/:8,Name/binary>>; +append(Dir, Name) when is_binary(Dir) -> + append(Dir,filename_string_to_binary(Name)); +append(Dir, Name) when is_binary(Name) -> + append(filename_string_to_binary(Dir),Name); append(Dir, Name) -> Dir ++ [$/|Name]. @@ -685,8 +685,6 @@ split([$/|Rest], Comp, Components, OsType) -> split(Rest, [], [lists:reverse(Comp)|Components], OsType); split([Char|Rest], Comp, Components, OsType) when is_integer(Char) -> split(Rest, [Char|Comp], Components, OsType); -split([List|Rest], Comp, Components, OsType) when is_list(List) -> - split(List++Rest, Comp, Components, OsType); split([], [], Components, _OsType) -> lists:reverse(Components); split([], Comp, Components, OsType) -> diff --git a/lib/stdlib/src/io.erl b/lib/stdlib/src/io.erl index 1d0f9374bc..78412ab2bc 100644 --- a/lib/stdlib/src/io.erl +++ b/lib/stdlib/src/io.erl @@ -39,6 +39,8 @@ -type device() :: atom() | pid(). -type prompt() :: atom() | string(). +-type error_description() :: term(). % Whatever the io-server sends. +-type request_error() :: {'error',error_description()}. %% XXX: Some uses of line() in this file may need to read erl_scan:location() -type line() :: pos_integer(). @@ -299,32 +301,32 @@ format(Io, Format, Args) -> %% Scanning Erlang code. --spec scan_erl_exprs(prompt()) -> erl_scan:tokens_result(). +-spec scan_erl_exprs(prompt()) -> erl_scan:tokens_result() | request_error(). scan_erl_exprs(Prompt) -> scan_erl_exprs(default_input(), Prompt, 1). --spec scan_erl_exprs(device(), prompt()) -> erl_scan:tokens_result(). +-spec scan_erl_exprs(device(), prompt()) -> erl_scan:tokens_result() | request_error(). scan_erl_exprs(Io, Prompt) -> scan_erl_exprs(Io, Prompt, 1). --spec scan_erl_exprs(device(), prompt(), line()) -> erl_scan:tokens_result(). +-spec scan_erl_exprs(device(), prompt(), line()) -> erl_scan:tokens_result() | request_error(). scan_erl_exprs(Io, Prompt, Pos0) -> request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[Pos0]}). --spec scan_erl_form(prompt()) -> erl_scan:tokens_result(). +-spec scan_erl_form(prompt()) -> erl_scan:tokens_result() | request_error(). scan_erl_form(Prompt) -> scan_erl_form(default_input(), Prompt, 1). --spec scan_erl_form(device(), prompt()) -> erl_scan:tokens_result(). +-spec scan_erl_form(device(), prompt()) -> erl_scan:tokens_result() | request_error(). scan_erl_form(Io, Prompt) -> scan_erl_form(Io, Prompt, 1). --spec scan_erl_form(device(), prompt(), line()) -> erl_scan:tokens_result(). +-spec scan_erl_form(device(), prompt(), line()) -> erl_scan:tokens_result() | request_error(). scan_erl_form(Io, Prompt, Pos0) -> request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[Pos0]}). @@ -335,7 +337,8 @@ scan_erl_form(Io, Prompt, Pos0) -> -type parse_ret() :: {'ok', erl_parse_expr_list(), line()} | {'eof', line()} - | {'error', erl_scan:error_info(), line()}. + | {'error', erl_scan:error_info(), line()} + | request_error(). -spec parse_erl_exprs(prompt()) -> parse_ret(). @@ -364,7 +367,8 @@ parse_erl_exprs(Io, Prompt, Pos0) -> -type parse_form_ret() :: {'ok', erl_parse_absform(), line()} | {'eof', line()} - | {'error', erl_scan:error_info(), line()}. + | {'error', erl_scan:error_info(), line()} + | request_error(). -spec parse_erl_form(prompt()) -> parse_form_ret(). diff --git a/lib/stdlib/src/re.erl b/lib/stdlib/src/re.erl index 296a6b3d23..9642de17b4 100644 --- a/lib/stdlib/src/re.erl +++ b/lib/stdlib/src/re.erl @@ -208,29 +208,25 @@ replace(Subject,RE,Replacement,Options) -> process_repl_params(Options,iodata,false), FlatSubject = to_binary(Subject, Unicode), FlatReplacement = to_binary(Replacement, Unicode), - case do_replace(FlatSubject,Subject,RE,FlatReplacement,NewOpt) of - {error,_Err} -> - throw(badre); - IoList -> - case Convert of - iodata -> - IoList; - binary -> - case Unicode of - false -> - iolist_to_binary(IoList); - true -> - unicode:characters_to_binary(IoList,unicode) - end; - list -> - case Unicode of - false -> - binary_to_list(iolist_to_binary(IoList)); - true -> - unicode:characters_to_list(IoList,unicode) - end - end - end + IoList = do_replace(FlatSubject,Subject,RE,FlatReplacement,NewOpt), + case Convert of + iodata -> + IoList; + binary -> + case Unicode of + false -> + iolist_to_binary(IoList); + true -> + unicode:characters_to_binary(IoList,unicode) + end; + list -> + case Unicode of + false -> + binary_to_list(iolist_to_binary(IoList)); + true -> + unicode:characters_to_list(IoList,unicode) + end + end catch throw:badopt -> erlang:error(badarg,[Subject,RE,Replacement,Options]); diff --git a/lib/stdlib/test/base64_SUITE.erl b/lib/stdlib/test/base64_SUITE.erl index 44742063b3..d8bb2dfb60 100644 --- a/lib/stdlib/test/base64_SUITE.erl +++ b/lib/stdlib/test/base64_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2009. All Rights Reserved. +%% Copyright Ericsson AB 2007-2011. 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 @@ -18,7 +18,6 @@ %% -module(base64_SUITE). --author('[email protected]'). -include("test_server.hrl"). -include("test_server_line.hrl"). @@ -29,7 +28,7 @@ %% Test cases must be exported. -export([base64_encode/1, base64_decode/1, base64_otp_5635/1, base64_otp_6279/1, big/1, illegal/1, mime_decode/1, - roundtrip/1]). + mime_decode_to_string/1, roundtrip/1]). init_per_testcase(_, Config) -> Dog = test_server:timetrap(?t:minutes(2)), @@ -50,7 +49,7 @@ all(doc) -> all(suite) -> [base64_encode, base64_decode, base64_otp_5635, base64_otp_6279, big, illegal, mime_decode, - roundtrip]. + mime_decode_to_string, roundtrip]. %%------------------------------------------------------------------------- @@ -59,7 +58,7 @@ base64_encode(doc) -> base64_encode(suite) -> []; base64_encode(Config) when is_list(Config) -> - %% Two pads + %% Two pads <<"QWxhZGRpbjpvcGVuIHNlc2FtZQ==">> = base64:encode("Aladdin:open sesame"), %% One pad @@ -77,8 +76,8 @@ base64_decode(doc) -> base64_decode(suite) -> []; base64_decode(Config) when is_list(Config) -> - %% Two pads - <<"Aladdin:open sesame">> = + %% Two pads + <<"Aladdin:open sesame">> = base64:decode("QWxhZGRpbjpvcGVuIHNlc2FtZQ=="), %% One pad <<"Hello World">> = base64:decode(<<"SGVsbG8gV29ybGQ=">>), @@ -138,20 +137,85 @@ illegal(Config) when is_list(Config) -> {'EXIT',{function_clause, _}} = (catch base64:decode("()")), ok. %%------------------------------------------------------------------------- +%% mime_decode and mime_decode_to_string have different implementations +%% so test both with the same input separately. Both functions have +%% the same implementation for binary/string arguments. mime_decode(doc) -> ["Test base64:mime_decode/1."]; mime_decode(suite) -> []; mime_decode(Config) when is_list(Config) -> - %% Two pads - <<"Aladdin:open sesame">> = + %% Test correct padding + <<"one">> = base64:mime_decode(<<"b25l">>), + <<"on">> = base64:mime_decode(<<"b24=">>), + <<"o">> = base64:mime_decode(<<"bw==">>), + %% Test 1 extra padding + <<"one">> = base64:mime_decode(<<"b25l= =">>), + <<"on">> = base64:mime_decode(<<"b24== =">>), + <<"o">> = base64:mime_decode(<<"bw=== =">>), + %% Test 2 extra padding + <<"one">> = base64:mime_decode(<<"b25l===">>), + <<"on">> = base64:mime_decode(<<"b24====">>), + <<"o">> = base64:mime_decode(<<"bw=====">>), + %% Test misc embedded padding + <<"one">> = base64:mime_decode(<<"b2=5l===">>), + <<"on">> = base64:mime_decode(<<"b=24====">>), + <<"o">> = base64:mime_decode(<<"b=w=====">>), + %% Test misc white space and illegals with embedded padding + <<"one">> = base64:mime_decode(<<" b~2=\r\n5()l===">>), + <<"on">> = base64:mime_decode(<<"\tb =2\"�4=�= ==">>), + <<"o">> = base64:mime_decode(<<"\nb=w=====">>), + %% Two pads + <<"Aladdin:open sesame">> = base64:mime_decode("QWxhZGRpbjpvc()GVuIHNlc2FtZQ=="), - %% One pad, followed by ignored text - <<"Hello World">> = base64:mime_decode(<<"SGVsb)(G8gV29ybGQ=apa">>), + %% One pad to ignore, followed by more text + <<"Hello World!!">> = base64:mime_decode(<<"SGVsb)(G8gV29ybGQ=h IQ= =">>), + %% No pad + <<"Aladdin:open sesam">> = + base64:mime_decode("QWxhZGRpbjpvcG�\")(VuIHNlc2Ft"), + %% Encoded base 64 strings may be divided by non base 64 chars. + %% In this cases whitespaces. + <<"0123456789!@#0^&*();:<>,. []{}">> = + base64:mime_decode( + <<"MDEy MzQ1Njc4 \tOSFAIzBeJ \nio)(oKTs6 PD4sLi \r\nBbXXt9">>), + ok. + +%%------------------------------------------------------------------------- + +%% Repeat of mime_decode() tests +mime_decode_to_string(doc) -> + ["Test base64:mime_decode_to_string/1."]; +mime_decode_to_string(suite) -> + []; +mime_decode_to_string(Config) when is_list(Config) -> + %% Test correct padding + "one" = base64:mime_decode_to_string(<<"b25l">>), + "on" = base64:mime_decode_to_string(<<"b24=">>), + "o" = base64:mime_decode_to_string(<<"bw==">>), + %% Test 1 extra padding + "one" = base64:mime_decode_to_string(<<"b25l= =">>), + "on" = base64:mime_decode_to_string(<<"b24== =">>), + "o" = base64:mime_decode_to_string(<<"bw=== =">>), + %% Test 2 extra padding + "one" = base64:mime_decode_to_string(<<"b25l===">>), + "on" = base64:mime_decode_to_string(<<"b24====">>), + "o" = base64:mime_decode_to_string(<<"bw=====">>), + %% Test misc embedded padding + "one" = base64:mime_decode_to_string(<<"b2=5l===">>), + "on" = base64:mime_decode_to_string(<<"b=24====">>), + "o" = base64:mime_decode_to_string(<<"b=w=====">>), + %% Test misc white space and illegals with embedded padding + "one" = base64:mime_decode_to_string(<<" b~2=\r\n5()l===">>), + "on" = base64:mime_decode_to_string(<<"\tb =2\"�4=�= ==">>), + "o" = base64:mime_decode_to_string(<<"\nb=w=====">>), + %% Two pads + "Aladdin:open sesame" = + base64:mime_decode_to_string("QWxhZGRpbjpvc()GVuIHNlc2FtZQ=="), + %% One pad to ignore, followed by more text + "Hello World!!" = base64:mime_decode_to_string(<<"SGVsb)(G8gV29ybGQ=h IQ= =">>), %% No pad "Aladdin:open sesam" = base64:mime_decode_to_string("QWxhZGRpbjpvcG�\")(VuIHNlc2Ft"), - %% Encoded base 64 strings may be divided by non base 64 chars. %% In this cases whitespaces. "0123456789!@#0^&*();:<>,. []{}" = @@ -159,6 +223,7 @@ mime_decode(Config) when is_list(Config) -> <<"MDEy MzQ1Njc4 \tOSFAIzBeJ \nio)(oKTs6 PD4sLi \r\nBbXXt9">>), ok. +%%------------------------------------------------------------------------- roundtrip(Config) when is_list(Config) -> Sizes = lists:seq(1, 255) ++ lists:seq(2400-5, 2440), diff --git a/lib/stdlib/test/erl_eval_SUITE.erl b/lib/stdlib/test/erl_eval_SUITE.erl index c60a558fa1..254ce0095d 100644 --- a/lib/stdlib/test/erl_eval_SUITE.erl +++ b/lib/stdlib/test/erl_eval_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% Copyright Ericsson AB 1998-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 @@ -38,7 +38,8 @@ otp_8133/1, funs/1, try_catch/1, - eval_expr_5/1]). + eval_expr_5/1, + zero_width/1]). %% %% Define to run outside of test server @@ -76,7 +77,8 @@ all(suite) -> [guard_1, guard_2, match_pattern, string_plusplus, pattern_expr, match_bin, guard_3, guard_4, lc, simple_cases, unary_plus, apply_atom, otp_5269, otp_6539, otp_6543, - otp_6787, otp_6977, otp_7550, otp_8133, funs, try_catch, eval_expr_5]. + otp_6787, otp_6977, otp_7550, otp_8133, funs, try_catch, eval_expr_5, + zero_width]. guard_1(doc) -> ["(OTP-2405)"]; @@ -1326,6 +1328,14 @@ eval_expr_5(Config) when is_list(Config) -> ok end. +zero_width(Config) when is_list(Config) -> + ?line check(fun() -> + {'EXIT',{badarg,_}} = (catch <<not_a_number:0>>), + ok + end, "begin {'EXIT',{badarg,_}} = (catch <<not_a_number:0>>), " + "ok end.", ok), + ok. + %% Check the string in different contexts: as is; in fun; from compiled code. check(F, String, Result) -> check1(F, String, Result), diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl index 620848003c..4e789790f6 100644 --- a/lib/stdlib/test/ets_SUITE.erl +++ b/lib/stdlib/test/ets_SUITE.erl @@ -5325,7 +5325,25 @@ my_tab_to_list(_Ts,'$end_of_table', Acc) -> lists:reverse(Acc); my_tab_to_list(Ts,Key, Acc) -> my_tab_to_list(Ts,ets:next(Ts,Key),[ets:lookup(Ts, Key)| Acc]). +wait_for_all_schedulers_online_to_execute() -> + PMs = lists:map(fun (Sched) -> + spawn_opt(fun () -> ok end, + [monitor, {scheduler, Sched}]) + end, + lists:seq(1,erlang:system_info(schedulers_online))), + lists:foreach(fun ({P, M}) -> + receive + {'DOWN', M, process, P, _} -> ok + end + end, + PMs), + ok. + etsmem() -> + %% Wait until it is guaranteed that all already scheduled + %% deallocations of DbTable structures have completed. + wait_for_all_schedulers_online_to_execute(), + AllTabs = lists:map(fun(T) -> {T,ets:info(T,name),ets:info(T,size), ets:info(T,memory),ets:info(T,type)} end, ets:all()), |