diff options
Diffstat (limited to 'lib')
73 files changed, 1549 insertions, 474 deletions
diff --git a/lib/asn1/src/asn1rtt_ber.erl b/lib/asn1/src/asn1rtt_ber.erl index 39fa8aaf99..e50b14941c 100644 --- a/lib/asn1/src/asn1rtt_ber.erl +++ b/lib/asn1/src/asn1rtt_ber.erl @@ -739,6 +739,8 @@ encode_named_bit_string([H|_]=Bits, NamedBitList, TagIn) when is_atom(H) -> do_encode_named_bit_string(Bits, NamedBitList, TagIn); encode_named_bit_string([{bit,_}|_]=Bits, NamedBitList, TagIn) -> do_encode_named_bit_string(Bits, NamedBitList, TagIn); +encode_named_bit_string([], _NamedBitList, TagIn) -> + encode_unnamed_bit_string(<<>>, TagIn); encode_named_bit_string(Bits, _NamedBitList, TagIn) when is_bitstring(Bits) -> encode_unnamed_bit_string(Bits, TagIn). @@ -746,6 +748,8 @@ encode_named_bit_string(C, [H|_]=Bits, NamedBitList, TagIn) when is_atom(H) -> do_encode_named_bit_string(C, Bits, NamedBitList, TagIn); encode_named_bit_string(C, [{bit,_}|_]=Bits, NamedBitList, TagIn) -> do_encode_named_bit_string(C, Bits, NamedBitList, TagIn); +encode_named_bit_string(C, [], _NamedBitList, TagIn) -> + encode_unnamed_bit_string(C, <<>>, TagIn); encode_named_bit_string(C, Bits, _NamedBitList, TagIn) when is_bitstring(Bits) -> encode_unnamed_bit_string(C, Bits, TagIn). diff --git a/lib/asn1/test/testPrimStrings.erl b/lib/asn1/test/testPrimStrings.erl index fa778765b1..46793c6bff 100644 --- a/lib/asn1/test/testPrimStrings.erl +++ b/lib/asn1/test/testPrimStrings.erl @@ -123,6 +123,7 @@ bit_string(Rules, Opts) -> %% Bs2 ::= BIT STRING {su(0), mo(1), tu(2), we(3), th(4), fr(5), sa(6) } (SIZE (7)) %%========================================================== + roundtrip('Bs2', []), roundtrip('Bs2', [mo,tu,fr]), bs_roundtrip('Bs2', <<2#0110010:7>>, [mo,tu,fr]), bs_roundtrip('Bs2', <<2#0110011:7>>, [mo,tu,fr,sa]), @@ -131,6 +132,7 @@ bit_string(Rules, Opts) -> %% Bs3 ::= BIT STRING {su(0), mo(1), tu(2), we(3), th(4), fr(5), sa(6) } (SIZE (1..7)) %%========================================================== + roundtrip('Bs3', []), roundtrip('Bs3', [mo,tu,fr]), bs_roundtrip('Bs3', <<2#0110010:7>>, [mo,tu,fr]), bs_roundtrip('Bs3', <<2#0110010:7>>, [mo,tu,fr]), @@ -139,6 +141,13 @@ bit_string(Rules, Opts) -> bs_roundtrip('Bs3', <<2#11:2>>, [su,mo]), %%========================================================== + %% Bs4 ::= BIT STRING {su(0), mo(1), tu(2), we(3), th(4), fr(5), sa(6) } + %%========================================================== + + roundtrip('Bs4', []), + roundtrip('Bs4', [mo,tu,fr,sa]), + + %%========================================================== %% Bs7 ::= BIT STRING (SIZE (24)) %%========================================================== diff --git a/lib/common_test/src/ct_conn_log_h.erl b/lib/common_test/src/ct_conn_log_h.erl index 2dd4fdac05..5239ec1ff8 100644 --- a/lib/common_test/src/ct_conn_log_h.erl +++ b/lib/common_test/src/ct_conn_log_h.erl @@ -104,18 +104,26 @@ terminate(_,#state{logs=Logs}) -> %%%----------------------------------------------------------------- %%% Writing reports write_report(_Time,#conn_log{header=false,module=ConnMod}=Info,Data,GL,State) -> - {LogType,Fd} = get_log(Info,GL,State), - io:format(Fd,"~n~ts",[format_data(ConnMod,LogType,Data)]); + case get_log(Info,GL,State) of + {silent,_} -> + ok; + {LogType,Fd} -> + io:format(Fd,"~n~ts",[format_data(ConnMod,LogType,Data)]) + end; write_report(Time,#conn_log{module=ConnMod}=Info,Data,GL,State) -> - {LogType,Fd} = get_log(Info,GL,State), - io:format(Fd,"~n~ts~ts~ts",[format_head(ConnMod,LogType,Time), - format_title(LogType,Info), - format_data(ConnMod,LogType,Data)]). + case get_log(Info,GL,State) of + {silent,_} -> + ok; + {LogType,Fd} -> + io:format(Fd,"~n~ts~ts~ts",[format_head(ConnMod,LogType,Time), + format_title(LogType,Info), + format_data(ConnMod,LogType,Data)]) + end. write_error(Time,#conn_log{module=ConnMod}=Info,Report,GL,State) -> case get_log(Info,GL,State) of - {html,_} -> + {LogType,_} when LogType==html; LogType==silent -> %% The error will anyway be written in the html log by the %% sasl error handler, so don't write it again. ok; diff --git a/lib/common_test/src/ct_netconfc.erl b/lib/common_test/src/ct_netconfc.erl index 0de7bf03af..05977e5649 100644 --- a/lib/common_test/src/ct_netconfc.erl +++ b/lib/common_test/src/ct_netconfc.erl @@ -1272,6 +1272,14 @@ set_request_timer(T) -> {ok,TRef} = timer:send_after(T,{Ref,timeout}), {Ref,TRef}. +%%%----------------------------------------------------------------- +cancel_request_timer(undefined,undefined) -> + ok; +cancel_request_timer(Ref,TRef) -> + _ = timer:cancel(TRef), + receive {Ref,timeout} -> ok + after 0 -> ok + end. %%%----------------------------------------------------------------- client_hello(Options) when is_list(Options) -> @@ -1404,9 +1412,9 @@ handle_error(Reason, State) -> Pending -> %% Assuming the first request gets the %% first answer - P=#pending{tref=TRef,caller=Caller} = + P=#pending{tref=TRef,ref=Ref,caller=Caller} = lists:last(Pending), - _ = timer:cancel(TRef), + cancel_request_timer(Ref,TRef), Reason1 = {failed_to_parse_received_data,Reason}, ct_gen_conn:return(Caller,{error,Reason1}), lists:delete(P,Pending) @@ -1492,8 +1500,8 @@ decode({Tag,Attrs,_}=E, #state{connection=Connection,pending=Pending}=State) -> {error,Reason} -> {noreply,State#state{hello_status = {error,Reason}}} end; - #pending{tref=TRef,caller=Caller} -> - _ = timer:cancel(TRef), + #pending{tref=TRef,ref=Ref,caller=Caller} -> + cancel_request_timer(Ref,TRef), case decode_hello(E) of {ok,SessionId,Capabilities} -> ct_gen_conn:return(Caller,ok), @@ -1519,9 +1527,8 @@ decode({Tag,Attrs,_}=E, #state{connection=Connection,pending=Pending}=State) -> %% there is just one pending that matches (i.e. has %% undefined msg_id and op) case [P || P = #pending{msg_id=undefined,op=undefined} <- Pending] of - [#pending{tref=TRef, - caller=Caller}] -> - _ = timer:cancel(TRef), + [#pending{tref=TRef,ref=Ref,caller=Caller}] -> + cancel_request_timer(Ref,TRef), ct_gen_conn:return(Caller,E), {noreply,State#state{pending=[]}}; _ -> @@ -1542,8 +1549,8 @@ get_msg_id(Attrs) -> decode_rpc_reply(MsgId,{_,Attrs,Content0}=E,#state{pending=Pending} = State) -> case lists:keytake(MsgId,#pending.msg_id,Pending) of - {value, #pending{tref=TRef,op=Op,caller=Caller}, Pending1} -> - _ = timer:cancel(TRef), + {value, #pending{tref=TRef,ref=Ref,op=Op,caller=Caller}, Pending1} -> + cancel_request_timer(Ref,TRef), Content = forward_xmlns_attr(Attrs,Content0), {CallerReply,{ServerReply,State2}} = do_decode_rpc_reply(Op,Content,State#state{pending=Pending1}), @@ -1555,10 +1562,11 @@ decode_rpc_reply(MsgId,{_,Attrs,Content0}=E,#state{pending=Pending} = State) -> %% pending that matches (i.e. has undefined msg_id and op) case [P || P = #pending{msg_id=undefined,op=undefined} <- Pending] of [#pending{tref=TRef, + ref=Ref, msg_id=undefined, op=undefined, caller=Caller}] -> - _ = timer:cancel(TRef), + cancel_request_timer(Ref,TRef), ct_gen_conn:return(Caller,E), {noreply,State#state{pending=[]}}; _ -> diff --git a/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl index 64ebfbc463..aaa0723488 100644 --- a/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl +++ b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl @@ -73,6 +73,7 @@ all() -> timeout_close_session, get, timeout_get, + flush_timeout_get, get_xpath, get_config, get_config_xpath, @@ -360,6 +361,28 @@ timeout_get(Config) -> ?ok = ct_netconfc:close_session(Client), ok. +%% Test OTP-13008 "ct_netconfc crash when receiving unknown timeout" +%% If the timer expires "at the same time" as the rpc reply is +%% received, the timeout message might already be sent when the timer +%% is cancelled. This test checks that the timeout message is flushed +%% from the message queue. If it isn't, the client crashes and the +%% session can not be closed afterwards. +%% Note that we can only hope that the test case triggers the problem +%% every now and then, as it is very timing dependent... +flush_timeout_get(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + Data = [{server,[{xmlns,"myns"}],[{name,[],["myserver"]}]}], + ?NS:expect_reply('get',{data,Data}), + timer:sleep(1000), + case ct_netconfc:get(Client,{server,[{xmlns,"myns"}],[]},1) of + {error,timeout} -> ok; % problem not triggered + {ok,Data} -> ok % problem possibly triggered + end, + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + ok. + get_xpath(Config) -> DataDir = ?config(data_dir,Config), {ok,Client} = open_success(DataDir), diff --git a/lib/erl_docgen/priv/dtd/cites.dtd b/lib/erl_docgen/priv/dtd/cites.dtd index 73931af009..4558947db0 100644 --- a/lib/erl_docgen/priv/dtd/cites.dtd +++ b/lib/erl_docgen/priv/dtd/cites.dtd @@ -30,7 +30,7 @@ <!ELEMENT cite (id, shortdef, def, resp?) > <!ELEMENT id (#PCDATA) > <!ELEMENT shortdef (#PCDATA) > -<!ELEMENT def (#PCDATA|c|em)* > +<!ELEMENT def (#PCDATA|c|i|em)* > <!ELEMENT resp (#PCDATA) > <!ELEMENT c (#PCDATA) > <!ELEMENT em (#PCDATA|c)* > diff --git a/lib/erl_docgen/priv/dtd/common.dtd b/lib/erl_docgen/priv/dtd/common.dtd index ded16d308f..a29fc233fa 100644 --- a/lib/erl_docgen/priv/dtd/common.dtd +++ b/lib/erl_docgen/priv/dtd/common.dtd @@ -24,21 +24,23 @@ <!ENTITY % block "p|pre|code|list|taglist|codeinclude| erleval" > -<!ENTITY % inline "#PCDATA|c|em|term|cite|br|path|seealso| - url|marker" > +<!ENTITY % inline "#PCDATA|c|i|em|term|cite|br|path|seealso| + url|marker|anno" > <!-- XXX --> <!ELEMENT p (%inline;)* > -<!ELEMENT pre (#PCDATA|seealso|url|input)* > -<!ELEMENT input (#PCDATA|seealso|url)* > -<!ELEMENT code (#PCDATA) > +<!ELEMENT pre (#PCDATA|seealso|url|input|anno)* > +<!ELEMENT input (#PCDATA|seealso|url|anno)* > +<!ELEMENT code (#PCDATA|anno)* > <!ATTLIST code type (erl|c|none) "none" > <!ELEMENT quote (p)* > <!ELEMENT warning (%block;|quote|br|marker)* > <!ELEMENT note (%block;|quote|br|marker)* > <!ELEMENT dont (%block;|quote|br|marker)* > <!ELEMENT do (%block;|quote|br|marker)* > -<!ELEMENT c (#PCDATA) > -<!ELEMENT em (#PCDATA|c)* > +<!ELEMENT c (#PCDATA|anno)* > +<!ELEMENT i (#PCDATA|c|anno)* > +<!ELEMENT em (#PCDATA|c|anno)* > +<!ELEMENT anno (#PCDATA) > <!-- XXX --> <!ELEMENT term (termdef?) > @@ -64,13 +66,13 @@ <!ELEMENT list (item+) > <!ATTLIST list type (ordered|bulleted) "bulleted" > -<!ELEMENT taglist (tag,item)+ > -<!ELEMENT tag (#PCDATA|c|em|seealso|url|marker)* > +<!ELEMENT taglist (tag,item+)+ > +<!ELEMENT tag (#PCDATA|c|i|em|br|seealso|url|marker|anno)* > <!ELEMENT item (%inline;|%block;)* > <!-- References --> -<!ELEMENT seealso (#PCDATA|c|em)* > +<!ELEMENT seealso (#PCDATA|c|i|em|anno)* > <!ATTLIST seealso marker CDATA #REQUIRED > <!ELEMENT url (#PCDATA) > <!ATTLIST url href CDATA #REQUIRED > diff --git a/lib/erl_docgen/priv/dtd/common.header.dtd b/lib/erl_docgen/priv/dtd/common.header.dtd index 71a8662572..eb27dc8f97 100644 --- a/lib/erl_docgen/priv/dtd/common.header.dtd +++ b/lib/erl_docgen/priv/dtd/common.header.dtd @@ -18,8 +18,8 @@ $Id$ --> <!ELEMENT header (copyright?,legalnotice?,title,shorttitle?, - prepared,responsible?,docno,approved?, - checked?,date,rev,file?) > + prepared?,responsible?,docno?,approved?, + checked?,date?,rev?,file?) > <!-- The titlestyle attribute is only defined to make all the book.xml files diff --git a/lib/erl_docgen/priv/dtd/common.refs.dtd b/lib/erl_docgen/priv/dtd/common.refs.dtd index f57e46a624..6889e990de 100644 --- a/lib/erl_docgen/priv/dtd/common.refs.dtd +++ b/lib/erl_docgen/priv/dtd/common.refs.dtd @@ -27,15 +27,16 @@ <!ELEMENT description (%block;|quote|br|marker|warning|note|dont|do)* > <!ELEMENT funcs (func)+ > -<!ELEMENT func (name+,type_desc*,fsummary,type?,desc?) > +<!ELEMENT func (name+,fsummary,(type|type_desc)*,desc?) > <!-- ELEMENT name is defined in each ref dtd --> -<!ELEMENT fsummary (#PCDATA|c|em)* > +<!ELEMENT fsummary (#PCDATA|c|i|em|anno)* > <!ELEMENT type (v,d?)* > <!ATTLIST type variable CDATA #IMPLIED + name CDATA #IMPLIED name_i CDATA #IMPLIED> -<!ELEMENT v (#PCDATA) > -<!ELEMENT d (#PCDATA|c|em)* > -<!ELEMENT desc (%block;|quote|br|marker|warning|note|dont|do|anno)* > +<!ELEMENT v (#PCDATA|seealso)* > +<!ELEMENT d (#PCDATA|c|i|em)* > +<!ELEMENT desc (%block;|quote|br|marker|warning|note|dont|do)* > <!ELEMENT authors (aname,email)+ > <!ELEMENT aname (#PCDATA) > <!ELEMENT email (#PCDATA) > @@ -43,5 +44,6 @@ warning|note|dont|do)*) > <!ELEMENT datatypes (datatype)+ > <!ELEMENT datatype (name+,desc?) > -<!ELEMENT type_desc (#PCDATA) > -<!ATTLIST type_desc variable CDATA #REQUIRED> +<!ELEMENT type_desc (#PCDATA|anno|c|seealso)* > +<!ATTLIST type_desc variable CDATA #IMPLIED + name CDATA #IMPLIED> diff --git a/lib/erl_docgen/priv/dtd/erlref.dtd b/lib/erl_docgen/priv/dtd/erlref.dtd index d62e2a5fcb..835407520a 100644 --- a/lib/erl_docgen/priv/dtd/erlref.dtd +++ b/lib/erl_docgen/priv/dtd/erlref.dtd @@ -32,4 +32,5 @@ <!ELEMENT name (#PCDATA) > <!ATTLIST name name CDATA #IMPLIED arity CDATA #IMPLIED - clause_i CDATA #IMPLIED> + clause_i CDATA #IMPLIED + n_vars CDATA #IMPLIED> diff --git a/lib/erl_docgen/priv/dtd/terms.dtd b/lib/erl_docgen/priv/dtd/terms.dtd index fd160b5c02..c2965eb61c 100644 --- a/lib/erl_docgen/priv/dtd/terms.dtd +++ b/lib/erl_docgen/priv/dtd/terms.dtd @@ -30,7 +30,7 @@ <!ELEMENT term (id, shortdef, def, resp?) > <!ELEMENT id (#PCDATA) > <!ELEMENT shortdef (#PCDATA) > -<!ELEMENT def (#PCDATA|c|em)* > +<!ELEMENT def (#PCDATA|c|i|em)* > <!ELEMENT resp (#PCDATA) > <!ELEMENT c (#PCDATA) > <!ELEMENT em (#PCDATA|c)* > diff --git a/lib/erl_docgen/priv/xsl/db_html.xsl b/lib/erl_docgen/priv/xsl/db_html.xsl index f5ddd364d3..c2325fbee9 100644 --- a/lib/erl_docgen/priv/xsl/db_html.xsl +++ b/lib/erl_docgen/priv/xsl/db_html.xsl @@ -990,12 +990,15 @@ </p> </xsl:template> - <!-- Inline elements --> <xsl:template match="b"> <strong><xsl:apply-templates/></strong> </xsl:template> + <xsl:template match="i"> + <i><xsl:apply-templates/></i> + </xsl:template> + <xsl:template match="br"> <br/> </xsl:template> diff --git a/lib/erl_docgen/priv/xsl/db_man.xsl b/lib/erl_docgen/priv/xsl/db_man.xsl index 120bf9880d..5201465e42 100644 --- a/lib/erl_docgen/priv/xsl/db_man.xsl +++ b/lib/erl_docgen/priv/xsl/db_man.xsl @@ -595,6 +595,12 @@ <xsl:text>\fR\& </xsl:text> </xsl:template> + <xsl:template match="i"> + <xsl:text>\fI</xsl:text> + <xsl:apply-templates/> + <xsl:text>\fR\& </xsl:text> + </xsl:template> + <xsl:template match="br"> <xsl:choose> <xsl:when test="ancestor::head"> diff --git a/lib/erl_docgen/priv/xsl/db_pdf.xsl b/lib/erl_docgen/priv/xsl/db_pdf.xsl index 53e202d52c..37a2d55274 100644 --- a/lib/erl_docgen/priv/xsl/db_pdf.xsl +++ b/lib/erl_docgen/priv/xsl/db_pdf.xsl @@ -1186,6 +1186,12 @@ </fo:inline> </xsl:template> + <xsl:template match="i"> + <fo:inline font-weight="italic"> + <xsl:apply-templates/> + </fo:inline> + </xsl:template> + <xsl:template match="br"> <fo:block/> </xsl:template> diff --git a/lib/erl_docgen/src/docgen_edoc_xml_cb.erl b/lib/erl_docgen/src/docgen_edoc_xml_cb.erl index 03fc161c5a..0ac7985a48 100644 --- a/lib/erl_docgen/src/docgen_edoc_xml_cb.erl +++ b/lib/erl_docgen/src/docgen_edoc_xml_cb.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2012. All Rights Reserved. +%% Copyright Ericsson AB 2001-2015. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -352,8 +352,8 @@ otp_xmlify_e(#xmlElement{name=code} = E) -> % 4) end; otp_xmlify_e(#xmlElement{name=Tag} = E) % 5a when Tag==h1; Tag==h2; Tag==h3; Tag==h4; Tag==h5 -> - Content = text_and_a_name_only(E#xmlElement.content), - [E#xmlElement{name=b, content=Content}]; + {Name, Text} = text_and_a_name_only(E#xmlElement.content), + [Name, E#xmlElement{name=b, content=Text}]; otp_xmlify_e(#xmlElement{name=Tag} = E) % 5b-c) when Tag==center; Tag==font -> @@ -1190,17 +1190,13 @@ get_text(#xmlElement{content=[#xmlText{value=Text}]}) -> get_text(#xmlElement{content=[E]}) -> get_text(E). -%% text_and_name_only(Es) -> Ts -text_and_a_name_only([#xmlElement{ - name = a, - attributes = [#xmlAttribute{name=name}]} = Name|Es]) -> - [Name|text_and_a_name_only(Es)]; -text_and_a_name_only([#xmlElement{content = Content}|Es]) -> - text_and_a_name_only(Content) ++ text_and_a_name_only(Es); -text_and_a_name_only([#xmlText{} = E |Es]) -> - [E | text_and_a_name_only(Es)]; -text_and_a_name_only([]) -> - []. +%% text_and_name_only(Es) -> {N, Ts} +text_and_a_name_only(Es) -> + [Name|_] = [Name || + #xmlElement{ + name = a, + attributes = [#xmlAttribute{name=name}]}=Name <- Es], + {Name#xmlElement{content = []}, text_only(Es)}. %% text_only(Es) -> Ts %% Takes a list of xmlElement and xmlText and return a lists of xmlText. diff --git a/lib/erl_docgen/vsn.mk b/lib/erl_docgen/vsn.mk index 2abd3d2b7e..43f5a570d7 100644 --- a/lib/erl_docgen/vsn.mk +++ b/lib/erl_docgen/vsn.mk @@ -1 +1 @@ -ERL_DOCGEN_VSN = 0.4 +ERL_DOCGEN_VSN = 0.4.1 diff --git a/lib/eunit/include/eunit.hrl b/lib/eunit/include/eunit.hrl index 88e9d6c19b..8f678b0290 100644 --- a/lib/eunit/include/eunit.hrl +++ b/lib/eunit/include/eunit.hrl @@ -135,7 +135,6 @@ -define(_assertThrow(Term, Expr), ?_assertException(throw, Term, Expr)). -define(_assertNotException(Class, Term, Expr), ?_test(?assertNotException(Class, Term, Expr))). --define(_assertReceive(Guard, Expr), ?_test(?assertReceive(Guard, Expr))). %% Macros for running operating system commands. (Note that these %% require EUnit to be present at runtime, or at least eunit_lib.) diff --git a/lib/inets/test/httpd_SUITE_data/Makefile.src b/lib/inets/test/httpd_SUITE_data/Makefile.src index b0fdb43d8d..cea40dd8cb 100644 --- a/lib/inets/test/httpd_SUITE_data/Makefile.src +++ b/lib/inets/test/httpd_SUITE_data/Makefile.src @@ -10,5 +10,10 @@ all: $(PROGS) cgi_echo@exe@: cgi_echo@obj@ $(LD) $(CROSSLDFLAGS) -o cgi_echo cgi_echo@obj@ @LIBS@ +@IFEQ@ (@CC@, cl -nologo) +cgi_echo@obj@: cgi_echo.c + $(CC) /c /Focgi_echo@obj@ $(CFLAGS) cgi_echo.c +@ELSE@ cgi_echo@obj@: cgi_echo.c $(CC) -c -o cgi_echo@obj@ $(CFLAGS) cgi_echo.c +@ENDIF@ diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java index 2830a7842e..4faae2a157 100644 --- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java +++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java @@ -922,8 +922,22 @@ public class OtpOutputStream extends ByteArrayOutputStream { oos.writeTo(dos); dos.close(); // note: closes this, too! } catch (final IllegalArgumentException e) { - // discard further un-compressed data - // -> if not called, there may be memory leaks! + /* + * Discard further un-compressed data (if not called, there may + * be memory leaks). + * + * After calling java.util.zip.Deflater.end(), the deflater + * should not be used anymore, not even the close() method of + * dos. Calling dos.close() before def.end() is prevented since + * an unfinished DeflaterOutputStream will try to deflate its + * unprocessed data to the (fixed) byte array which is prevented + * by ensureCapacity() and would also unnecessarily process + * further data that is discarded anyway. + * + * Since we are re-using the byte array of this object below, we + * must not call close() in e.g. a finally block either (with or + * without a call to def.end()). + */ def.end(); // could not make the value smaller than originally // -> reset to starting count, write uncompressed @@ -942,11 +956,6 @@ public class OtpOutputStream extends ByteArrayOutputStream { "Intermediate stream failed for Erlang object " + o); } finally { fixedSize = Integer.MAX_VALUE; - try { - dos.close(); - } catch (final IOException e) { - // ignore - } } } } diff --git a/lib/jinterface/test/nc_SUITE.erl b/lib/jinterface/test/nc_SUITE.erl index 9679b90a0d..c5f3198c21 100644 --- a/lib/jinterface/test/nc_SUITE.erl +++ b/lib/jinterface/test/nc_SUITE.erl @@ -215,6 +215,7 @@ decompress_roundtrip(Config) when is_list(Config) -> 0.0, math:sqrt(2), <<1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,31:5>>, + "{}", RandomBin1k, RandomBin1M, RandomBin10M, @@ -244,6 +245,7 @@ compress_roundtrip(Config) when is_list(Config) -> 0.0, math:sqrt(2), <<1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,31:5>>, + "{}", RandomBin1k, RandomBin1M, RandomBin10M, diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml index 268a8404f1..d02c3e8ad2 100644 --- a/lib/kernel/doc/src/notes.xml +++ b/lib/kernel/doc/src/notes.xml @@ -185,6 +185,22 @@ </section> +<section><title>Kernel 3.2.0.1</title> + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p>The 'raw' socket option could not be used multiple times + in one call to any e.g gen_tcp function because only one + of the occurrences were used. This bug has been fixed, + and also a small bug concerning propagating error codes + from within inet:setopts/2.</p> + <p>Own Id: OTP-11482 Aux Id: seq12872 </p> + </item> + </list> + </section> + </section> + + <section><title>Kernel 3.2</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/kernel/src/file_io_server.erl b/lib/kernel/src/file_io_server.erl index bf8b9e2747..deb7b315b1 100644 --- a/lib/kernel/src/file_io_server.erl +++ b/lib/kernel/src/file_io_server.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2013. All Rights Reserved. +%% Copyright Ericsson AB 2000-2015. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -206,8 +206,8 @@ io_reply(From, ReplyAs, Reply) -> file_request({advise,Offset,Length,Advise}, #state{handle=Handle}=State) -> case ?PRIM_FILE:advise(Handle, Offset, Length, Advise) of - {error,_}=Reply -> - {stop,normal,Reply,State}; + {error,Reason}=Reply -> + {stop,Reason,Reply,State}; Reply -> {reply,Reply,State} end; @@ -215,62 +215,91 @@ file_request({allocate, Offset, Length}, #state{handle = Handle} = State) -> Reply = ?PRIM_FILE:allocate(Handle, Offset, Length), {reply, Reply, State}; +file_request({pread,At,Sz}, State) + when At =:= cur; + At =:= {cur,0} -> + case get_chars(Sz, latin1, State) of + {reply,Reply,NewState} + when is_list(Reply); + is_binary(Reply) -> + {reply,{ok,Reply},NewState}; + Other -> + Other + end; file_request({pread,At,Sz}, - #state{handle=Handle,buf=Buf,read_mode=ReadMode}=State) -> + #state{handle=Handle,buf=Buf}=State) -> case position(Handle, At, Buf) of - {ok,_Offs} -> - case ?PRIM_FILE:read(Handle, Sz) of - {ok,Bin} when ReadMode =:= list -> - std_reply({ok,binary_to_list(Bin)}, State); - Reply -> - std_reply(Reply, State) - end; - Reply -> - std_reply(Reply, State) + {error,_} = Reply -> + {error,Reply,State}; + _ -> + case get_chars(Sz, latin1, State#state{buf= <<>>}) of + {reply,Reply,NewState} + when is_list(Reply); + is_binary(Reply) -> + {reply,{ok,Reply},NewState}; + Other -> + Other + end end; +file_request({pwrite,At,Data}, + #state{buf= <<>>}=State) + when At =:= cur; + At =:= {cur,0} -> + put_chars(Data, latin1, State); file_request({pwrite,At,Data}, #state{handle=Handle,buf=Buf}=State) -> case position(Handle, At, Buf) of - {ok,_Offs} -> - std_reply(?PRIM_FILE:write(Handle, Data), State); - Reply -> - std_reply(Reply, State) + {error,_} = Reply -> + {error,Reply,State}; + _ -> + put_chars(Data, latin1, State) end; file_request(datasync, #state{handle=Handle}=State) -> case ?PRIM_FILE:datasync(Handle) of - {error,_}=Reply -> - {stop,normal,Reply,State}; + {error,Reason}=Reply -> + {stop,Reason,Reply,State}; Reply -> {reply,Reply,State} end; file_request(sync, #state{handle=Handle}=State) -> case ?PRIM_FILE:sync(Handle) of - {error,_}=Reply -> - {stop,normal,Reply,State}; + {error,Reason}=Reply -> + {stop,Reason,Reply,State}; Reply -> {reply,Reply,State} end; file_request(close, #state{handle=Handle}=State) -> - {stop,normal,?PRIM_FILE:close(Handle),State#state{buf= <<>>}}; + case ?PRIM_FILE:close(Handle) of + {error,Reason}=Reply -> + {stop,Reason,Reply,State#state{buf= <<>>}}; + Reply -> + {stop,normal,Reply,State#state{buf= <<>>}} + end; file_request({position,At}, #state{handle=Handle,buf=Buf}=State) -> - std_reply(position(Handle, At, Buf), State); + case position(Handle, At, Buf) of + {error,_} = Reply -> + {error,Reply,State}; + Reply -> + std_reply(Reply, State) + end; file_request(truncate, #state{handle=Handle}=State) -> case ?PRIM_FILE:truncate(Handle) of - {error,_Reason}=Reply -> - {stop,normal,Reply,State#state{buf= <<>>}}; + {error,Reason}=Reply -> + {stop,Reason,Reply,State#state{buf= <<>>}}; Reply -> - {reply,Reply,State} + std_reply(Reply, State) end; file_request(Unknown, #state{}=State) -> Reason = {request, Unknown}, {error,{error,Reason},State}. +%% Standard reply and clear buffer std_reply({error,_}=Reply, State) -> {error,Reply,State#state{buf= <<>>}}; std_reply(Reply, State) -> @@ -286,8 +315,8 @@ io_request({put_chars, Enc, Chars}, io_request({put_chars, Enc, Chars}, #state{handle=Handle,buf=Buf}=State) -> case position(Handle, cur, Buf) of - {error,_}=Reply -> - {stop,normal,Reply,State#state{buf= <<>>}}; + {error,Reason}=Reply -> + {stop,Reason,Reply,State}; _ -> put_chars(Chars, Enc, State#state{buf= <<>>}) end; @@ -368,23 +397,27 @@ io_request_loop([Request|Tail], %% I/O request put_chars %% put_chars(Chars, latin1, #state{handle=Handle, unic=latin1}=State) -> + NewState = State#state{buf = <<>>}, case ?PRIM_FILE:write(Handle, Chars) of - {error,_}=Reply -> - {stop,normal,Reply,State}; + {error,Reason}=Reply -> + {stop,Reason,Reply,NewState}; Reply -> - {reply,Reply,State} + {reply,Reply,NewState} end; put_chars(Chars, InEncoding, #state{handle=Handle, unic=OutEncoding}=State) -> + NewState = State#state{buf = <<>>}, case unicode:characters_to_binary(Chars,InEncoding,OutEncoding) of Bin when is_binary(Bin) -> case ?PRIM_FILE:write(Handle, Bin) of - {error,_}=Reply -> - {stop,normal,Reply,State}; + {error,Reason}=Reply -> + {stop,Reason,Reply,NewState}; Reply -> - {reply,Reply,State} + {reply,Reply,NewState} end; {error,_,_} -> - {stop,normal,{error,{no_translation, InEncoding, OutEncoding}},State} + {stop,no_translation, + {error,{no_translation, InEncoding, OutEncoding}}, + NewState} end. get_line(S, {<<>>, Cont}, OutEnc, @@ -884,11 +917,14 @@ cbv({utf32,little},_) -> %% Compensates ?PRIM_FILE:position/2 for the number of bytes %% we have buffered - -position(Handle, cur, Buf) -> - position(Handle, {cur, 0}, Buf); -position(Handle, {cur, Offs}, Buf) when is_binary(Buf) -> - ?PRIM_FILE:position(Handle, {cur, Offs-byte_size(Buf)}); -position(Handle, At, _Buf) -> - ?PRIM_FILE:position(Handle, At). - +position(Handle, At, Buf) -> + ?PRIM_FILE:position( + Handle, + case At of + cur -> + {cur, -byte_size(Buf)}; + {cur, Offs} -> + {cur, Offs-byte_size(Buf)}; + _ -> + At + end). diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl index 855c6377a3..b573112445 100644 --- a/lib/kernel/src/inet.erl +++ b/lib/kernel/src/inet.erl @@ -692,6 +692,7 @@ connect_options(Opts, Family) -> case con_opt(Opts, BaseOpts, connect_options()) of {ok, R} -> {ok, R#connect_opts { + opts = lists:reverse(R#connect_opts.opts), ifaddr = translate_ip(R#connect_opts.ifaddr, Family) }}; Error -> Error @@ -762,6 +763,7 @@ listen_options(Opts, Family) -> case list_opt(Opts, BaseOpts, listen_options()) of {ok, R} -> {ok, R#listen_opts { + opts = lists:reverse(R#listen_opts.opts), ifaddr = translate_ip(R#listen_opts.ifaddr, Family) }}; Error -> Error @@ -820,6 +822,7 @@ udp_options(Opts, Family) -> case udp_opt(Opts, #udp_opts { }, udp_options()) of {ok, R} -> {ok, R#udp_opts { + opts = lists:reverse(R#udp_opts.opts), ifaddr = translate_ip(R#udp_opts.ifaddr, Family) }}; Error -> Error @@ -893,9 +896,12 @@ sctp_options() -> sctp_options(Opts, Mod) -> case sctp_opt(Opts, Mod, #sctp_opts{}, sctp_options()) of {ok,#sctp_opts{ifaddr=undefined}=SO} -> - {ok,SO#sctp_opts{ifaddr=Mod:translate_ip(?SCTP_DEF_IFADDR)}}; - {ok,_}=OK -> - OK; + {ok, + SO#sctp_opts{ + opts=lists:reverse(SO#sctp_opts.opts), + ifaddr=Mod:translate_ip(?SCTP_DEF_IFADDR)}}; + {ok,SO} -> + {ok,SO#sctp_opts{opts=lists:reverse(SO#sctp_opts.opts)}}; Error -> Error end. @@ -967,6 +973,8 @@ add_opt(Name, Val, Opts, As) -> case lists:member(Name, As) of true -> case prim_inet:is_sockopt_val(Name, Val) of + true when Name =:= raw -> + {ok, [{Name,Val} | Opts]}; true -> Opts1 = lists:keydelete(Name, 1, Opts), {ok, [{Name,Val} | Opts1]}; diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl index 8856be31c2..09d9a45197 100644 --- a/lib/kernel/test/file_SUITE.erl +++ b/lib/kernel/test/file_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2013. All Rights Reserved. +%% Copyright Ericsson AB 1996-2015. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -48,7 +48,7 @@ -export([cur_dir_0/1, cur_dir_1/1, make_del_dir/1, list_dir/1,list_dir_error/1, untranslatable_names/1, untranslatable_names_error/1, - pos1/1, pos2/1]). + pos1/1, pos2/1, pos3/1]). -export([close/1, consult1/1, path_consult/1, delete/1]). -export([ eval1/1, path_eval/1, script1/1, path_script/1, open1/1, @@ -80,6 +80,7 @@ -export([interleaved_read_write/1]). +-export([unicode/1]). -export([altname/1]). -export([large_file/1, large_write/1]). @@ -114,7 +115,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [altname, read_write_file, {group, dirs}, + [unicode, altname, read_write_file, {group, dirs}, {group, files}, delete, rename, names, {group, errors}, {group, compression}, {group, links}, copy, delayed_write, read_ahead, segment_read, segment_write, @@ -136,7 +137,7 @@ groups() -> [open1, old_modes, new_modes, path_open, close, access, read_write, pread_write, append, open_errors, exclusive]}, - {pos, [], [pos1, pos2]}, + {pos, [], [pos1, pos2, pos3]}, {file_info, [], [file_info_basic_file, file_info_basic_directory, file_info_bad, file_info_times, file_write_file_info]}, @@ -1370,6 +1371,27 @@ pos2(Config) when is_list(Config) -> ?line test_server:timetrap_cancel(Dog), ok. +pos3(suite) -> []; +pos3(doc) -> ["When it does not use raw mode, file:position had a bug."]; +pos3(Config) when is_list(Config) -> + ?line Dog = test_server:timetrap(test_server:seconds(5)), + ?line RootDir = ?config(data_dir, Config), + ?line Name = filename:join(RootDir, "realmen.html.gz"), + + ?line {ok, Fd} = ?FILE_MODULE:open(Name, [read, binary]), + ?line {ok, _} = ?FILE_MODULE:read(Fd, 5), + ?line {error, einval} = ?FILE_MODULE:position(Fd, {bof, -1}), + + %% Here ok had returned =( + ?line {error, einval} = ?FILE_MODULE:position(Fd, {cur, -10}), + %% That test is actually questionable since file:position/2 + %% is documented to leave the file position undefined after + %% it has returned an error. But on Posix systems the position + %% is guaranteed to be unchanged after an error return. On e.g + %% Windows there is nothing stated about this in the documentation. + + ?line test_server:timetrap_cancel(Dog), + ok. file_info_basic_file(suite) -> []; file_info_basic_file(doc) -> []; @@ -2761,6 +2783,40 @@ compress_async_crash_loop(N, Path, ExpectedData) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +unicode(Config) when is_list(Config) -> + Dir = ?config(priv_dir, Config), + Name = filename:join(Dir, "data-utf8.txt"), + Txt = lists:seq(128, 255), + D = unicode:characters_to_binary(Txt, latin1, latin1), + {ok,Fd1} = + ?FILE_MODULE:open(Name, [write,read,binary,{encoding,unicode}]), + ok = ?FILE_MODULE:truncate(Fd1), + ok = ?FILE_MODULE:write(Fd1, Txt), + {ok,0} = ?FILE_MODULE:position(Fd1, bof), + {ok,D} = ?FILE_MODULE:read(Fd1, 129), + {ok,0} = ?FILE_MODULE:position(Fd1, bof), + {ok,D1} = ?FILE_MODULE:read(Fd1, 64), + {ok,Pos} = ?FILE_MODULE:position(Fd1, cur), + {ok,D2} = ?FILE_MODULE:pread(Fd1, {cur,0}, 65), + D = <<D1/binary, D2/binary>>, + {ok,D1} = ?FILE_MODULE:pread(Fd1, bof, 64), + {ok,Pos} = ?FILE_MODULE:position(Fd1, Pos), + {ok,D2} = ?FILE_MODULE:read(Fd1, 64), + ok = ?FILE_MODULE:close(Fd1), + %% + RawD = unicode:characters_to_binary(Txt, latin1, unicode), + {ok,RawD} = ?FILE_MODULE:read_file(Name), + %% + {ok,Fd2} = ?FILE_MODULE:open(Name, [read,{encoding,unicode}]), + {ok,Txt} = ?FILE_MODULE:read(Fd2, 129), + {Txt1,Txt2} = lists:split(64, Txt), + {ok,Txt2} = ?FILE_MODULE:pread(Fd2, Pos, 65), + {ok,0} = ?FILE_MODULE:position(Fd2, bof), + {ok,Txt1} = ?FILE_MODULE:read(Fd2, 64), + ok = ?FILE_MODULE:close(Fd2). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + altname(doc) -> "Test the file:altname/1 function"; altname(suite) -> diff --git a/lib/kernel/test/inet_sockopt_SUITE.erl b/lib/kernel/test/inet_sockopt_SUITE.erl index 1262f36fae..cb522c8abe 100644 --- a/lib/kernel/test/inet_sockopt_SUITE.erl +++ b/lib/kernel/test/inet_sockopt_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2013. All Rights Reserved. +%% Copyright Ericsson AB 2007-2015. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -52,6 +52,7 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, simple/1, loop_all/1, simple_raw/1, simple_raw_getbin/1, + multiple_raw/1, multiple_raw_getbin/1, doc_examples_raw/1,doc_examples_raw_getbin/1, large_raw/1,large_raw_getbin/1,combined/1,combined_getbin/1, ipv6_v6only_udp/1, ipv6_v6only_tcp/1, ipv6_v6only_sctp/1, @@ -65,6 +66,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [simple, loop_all, simple_raw, simple_raw_getbin, + multiple_raw, multiple_raw_getbin, doc_examples_raw, doc_examples_raw_getbin, large_raw, large_raw_getbin, combined, combined_getbin, ipv6_v6only_udp, ipv6_v6only_tcp, ipv6_v6only_sctp, @@ -185,6 +187,84 @@ nintbin2int(<<Int:16/native>>) -> Int; nintbin2int(<<Int:8/native>>) -> Int; nintbin2int(<<>>) -> 0. + + +multiple_raw(suite) -> []; +multiple_raw(doc) -> "Test setopt/getopt of multiple raw options."; +multiple_raw(Config) when is_list(Config) -> + do_multiple_raw(Config,false). +multiple_raw_getbin(suite) -> []; +multiple_raw_getbin(doc) -> "Test setopt/getopt of multiple raw options, " + "with binaries in getopt."; +multiple_raw_getbin(Config) when is_list(Config) -> + do_multiple_raw(Config,true). + +do_multiple_raw(Config, Binary) -> + Port = start_helper(Config), + SolSocket = ask_helper(Port, ?C_GET_SOL_SOCKET), + SoKeepalive = ask_helper(Port, ?C_GET_SO_KEEPALIVE), + SoKeepaliveTrue = {raw,SolSocket,SoKeepalive,<<1:32/native>>}, + SoKeepaliveFalse = {raw,SolSocket,SoKeepalive,<<0:32/native>>}, + SoReuseaddr = ask_helper(Port, ?C_GET_SO_REUSEADDR), + SoReuseaddrTrue = {raw,SolSocket,SoReuseaddr,<<1:32/native>>}, + SoReuseaddrFalse = {raw,SolSocket,SoReuseaddr,<<0:32/native>>}, + {S1,S2} = + create_socketpair( + [SoReuseaddrFalse,SoKeepaliveTrue], + [SoKeepaliveFalse,SoReuseaddrTrue]), + {ok,[{reuseaddr,false},{keepalive,true}]} = + inet:getopts(S1, [reuseaddr,keepalive]), + {ok, + [{raw,SolSocket,SoReuseaddr,S1R1}, + {raw,SolSocket,SoKeepalive,S1K1}]} = + inet:getopts( + S1, + [{raw,SolSocket,SoReuseaddr,binarify(4, Binary)}, + {raw,SolSocket,SoKeepalive,binarify(4, Binary)}]), + true = nintbin2int(S1R1) =:= 0, + true = nintbin2int(S1K1) =/= 0, + {ok,[{keepalive,false},{reuseaddr,true}]} = + inet:getopts(S2, [keepalive,reuseaddr]), + {ok, + [{raw,SolSocket,SoKeepalive,S2K1}, + {raw,SolSocket,SoReuseaddr,S2R1}]} = + inet:getopts( + S2, + [{raw,SolSocket,SoKeepalive,binarify(4, Binary)}, + {raw,SolSocket,SoReuseaddr,binarify(4, Binary)}]), + true = nintbin2int(S2K1) =:= 0, + true = nintbin2int(S2R1) =/= 0, + %% + ok = inet:setopts( + S1, [SoReuseaddrTrue,SoKeepaliveFalse]), + ok = inet:setopts( + S2, [SoKeepaliveTrue,SoReuseaddrFalse]), + {ok, + [{raw,SolSocket,SoReuseaddr,S1R2}, + {raw,SolSocket,SoKeepalive,S1K2}]} = + inet:getopts( + S1, + [{raw,SolSocket,SoReuseaddr,binarify(4, Binary)}, + {raw,SolSocket,SoKeepalive,binarify(4, Binary)}]), + true = nintbin2int(S1R2) =/= 0, + true = nintbin2int(S1K2) =:= 0, + {ok, + [{raw,SolSocket,SoKeepalive,S2K2}, + {raw,SolSocket,SoReuseaddr,S2R2}]} = + inet:getopts( + S2, + [{raw,SolSocket,SoKeepalive,binarify(4, Binary)}, + {raw,SolSocket,SoReuseaddr,binarify(4, Binary)}]), + true = nintbin2int(S2K2) =/= 0, + true = nintbin2int(S2R2) =:= 0, + %% + gen_tcp:close(S1), + gen_tcp:close(S2), + stop_helper(Port), + ok. + + + doc_examples_raw(suite) -> []; doc_examples_raw(doc) -> "Test that the example code from the documentation " "works"; diff --git a/lib/ssh/doc/src/ssh.xml b/lib/ssh/doc/src/ssh.xml index 1e9acf4a99..b3f850fc38 100644 --- a/lib/ssh/doc/src/ssh.xml +++ b/lib/ssh/doc/src/ssh.xml @@ -85,6 +85,15 @@ <item><p><c>atom()</c> - Name of the Erlang module implementing the subsystem using the <c>ssh_channel</c> behavior, see <seealso marker="ssh_channel">ssh_channel(3)</seealso></p></item> + <tag><c>key_cb() =</c></tag> + <item> + <p><c>atom() | {atom(), list()}</c></p> + <p><c>atom()</c> - Name of the erlang module implementing the behaviours + <seealso marker="ssh_client_key_api">ssh_client_key_api</seealso> or + <seealso marker="ssh_client_key_api">ssh_client_key_api</seealso> as the + case maybe.</p> + <p><c>list()</c> - List of options that can be passed to the callback module.</p> + </item> <tag><c>channel_init_args() =</c></tag> <item><p><c>list()</c></p></item> @@ -197,26 +206,25 @@ <tag><c><![CDATA[{public_key_alg, 'ssh-rsa' | 'ssh-dss'}]]></c></tag> <item> <note> - <p>This option is kept for compatibility. It is ignored if the <c>preferred_algorithms</c> - option is used. The equivalence of <c>{public_key_alg,'ssh-dss'}</c> is - <c>{preferred_algorithms, [{public_key,['ssh-dss','ssh-rsa']}]}</c>.</p> + <p>This option will be removed in OTP 20, but is kept for compatibility. It is ignored if + the preferred <c>pref_public_key_algs</c> option is used.</p> </note> <p>Sets the preferred public key algorithm to use for user authentication. If the preferred algorithm fails, - the other algorithm is tried. The default is - to try <c><![CDATA['ssh-rsa']]></c> first.</p> + the other algorithm is tried. If <c>{public_key_alg, 'ssh-rsa'}</c> is set, it is translated + to <c>{pref_public_key_algs, ['ssh-rsa','ssh-dss']}</c>. If it is + <c>{public_key_alg, 'ssh-dss'}</c>, it is translated + to <c>{pref_public_key_algs, ['ssh-dss','ssh-rsa']}</c>. + </p> </item> <tag><c><![CDATA[{pref_public_key_algs, list()}]]></c></tag> <item> - <note> - <p>This option is kept for compatibility. It is ignored if the <c>preferred_algorithms</c> - option is used. The equivalence of <c>{pref_public_key_algs,['ssh-dss']}</c> is - <c>{preferred_algorithms, [{public_key,['ssh-dss']}]}</c>.</p> - </note> - <p>List of public key algorithms to try to use. - <c>'ssh-rsa'</c> and <c>'ssh-dss'</c> are available. - Overrides <c><![CDATA[{public_key_alg, 'ssh-rsa' | 'ssh-dss'}]]></c></p> + <p>List of user (client) public key algorithms to try to use.</p> + <p>The default value is + <c><![CDATA[['ssh-rsa','ssh-dss','ecdsa-sha2-nistp256','ecdsa-sha2-nistp384','ecdsa-sha2-nistp521'] ]]></c> + </p> + <p>If there is no public key of a specified type available, the corresponding entry is ignored.</p> </item> <tag><c><![CDATA[{preferred_algorithms, algs_list()}]]></c></tag> @@ -224,6 +232,7 @@ <p>List of algorithms to use in the algorithm negotiation. The default <c>algs_list()</c> can be obtained from <seealso marker="#default_algorithms/0">default_algorithms/0</seealso>. </p> + <p>If an alg_entry() is missing in the algs_list(), the default value is used for that entry.</p> <p>Here is an example of this option:</p> <code> {preferred_algorithms, @@ -234,9 +243,9 @@ {compression,[none,zlib]} } </code> - <p>The example specifies different algorithms in the two directions (client2server and server2client), for cipher but specifies the same -algorithms for mac and compression in both directions. The kex (key exchange) and public key algorithms are set to their default values, -kex is implicit but public_key is set explicitly.</p> + <p>The example specifies different algorithms in the two directions (client2server and server2client), + for cipher but specifies the same algorithms for mac and compression in both directions. + The kex (key exchange) is implicit but public_key is set explicitly.</p> <warning> <p>Changing the values can make a connection less secure. Do not change unless you @@ -272,11 +281,13 @@ kex is implicit but public_key is set explicitly.</p> password, if the password authentication method is attempted.</p> </item> - <tag><c><![CDATA[{key_cb, atom()}]]></c></tag> + <tag><c><![CDATA[{key_cb, key_cb()}]]></c></tag> <item> - <p>Module implementing the behaviour - <seealso marker="ssh_client_key_api">ssh_client_key_api</seealso>. - Can be used to customize the handling of public keys. + <p>Module implementing the behaviour <seealso + marker="ssh_client_key_api">ssh_client_key_api</seealso>. Can be used to + customize the handling of public keys. If callback options are provided + along with the module name, they are made available to the callback + module via the options passed to it under the key 'key_cb_private'. </p> </item> <tag><c><![CDATA[{quiet_mode, atom() = boolean()}]]></c></tag> @@ -440,6 +451,7 @@ kex is implicit but public_key is set explicitly.</p> <p>List of algorithms to use in the algorithm negotiation. The default <c>algs_list()</c> can be obtained from <seealso marker="#default_algorithms/0">default_algorithms/0</seealso>. </p> + <p>If an alg_entry() is missing in the algs_list(), the default value is used for that entry.</p> <p>Here is an example of this option:</p> <code> {preferred_algorithms, @@ -450,9 +462,9 @@ kex is implicit but public_key is set explicitly.</p> {compression,[none,zlib]} } </code> - <p>The example specifies different algorithms in the two directions (client2server and server2client), for cipher but specifies the same -algorithms for mac and compression in both directions. The kex (key exchange) and public key algorithms are set to their default values, -kex is implicit but public_key is set explicitly.</p> + <p>The example specifies different algorithms in the two directions (client2server and server2client), + for cipher but specifies the same algorithms for mac and compression in both directions. + The kex (key exchange) is implicit but public_key is set explicitly.</p> <warning> <p>Changing the values can make a connection less secure. Do not change unless you @@ -607,11 +619,13 @@ kex is implicit but public_key is set explicitly.</p> </p> </item> - <tag><c><![CDATA[{key_cb, atom()}]]></c></tag> + <tag><c><![CDATA[{key_cb, key_cb()}]]></c></tag> <item> - <p>Module implementing the behaviour - <seealso marker="ssh_server_key_api">ssh_server_key_api</seealso>. - Can be used to customize the handling of public keys. + <p>Module implementing the behaviour <seealso + marker="ssh_server_key_api">ssh_server_key_api</seealso>. Can be used to + customize the handling of public keys. If callback options are provided + along with the module name, they are made available to the callback + module via the options passed to it under the key 'key_cb_private'. </p> </item> diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl index bb50e436a3..54f94acbdc 100644 --- a/lib/ssh/src/ssh.erl +++ b/lib/ssh/src/ssh.erl @@ -297,13 +297,6 @@ find_hostport(Fd) -> ok = inet:close(S), HostPort. -%% find_port(Fd) -> -%% %% Hack.... -%% {ok,TmpSock} = gen_tcp:listen(0,[{fd,Fd}]), -%% {ok, {_,ThePort}} = inet:sockname(TmpSock), -%% gen_tcp:close(TmpSock), -%% ThePort. - handle_options(Opts) -> try handle_option(algs_compatibility(proplists:unfold(Opts)), [], []) of @@ -315,32 +308,27 @@ handle_options(Opts) -> end. -algs_compatibility(Os) -> +algs_compatibility(Os0) -> %% Take care of old options 'public_key_alg' and 'pref_public_key_algs' - comp_pk(proplists:get_value(preferred_algorithms,Os), - proplists:get_value(pref_public_key_algs,Os), - proplists:get_value(public_key_alg, Os), - [{K,V} || {K,V} <- Os, - K =/= public_key_alg, - K =/= pref_public_key_algs] - ). - -comp_pk(undefined, undefined, undefined, Os) -> Os; -comp_pk( PrefAlgs, _, _, Os) when PrefAlgs =/= undefined -> Os; - -comp_pk(undefined, undefined, ssh_dsa, Os) -> comp_pk(undefined, undefined, 'ssh-dss', Os); -comp_pk(undefined, undefined, ssh_rsa, Os) -> comp_pk(undefined, undefined, 'ssh-rsa', Os); -comp_pk(undefined, undefined, PK, Os) -> - PKs = [PK | ssh_transport:supported_algorithms(public_key)--[PK]], - [{preferred_algorithms, [{public_key,PKs}] } | Os]; - -comp_pk(undefined, PrefPKs, _, Os) when PrefPKs =/= undefined -> - PKs = [case PK of - ssh_dsa -> 'ssh-dss'; - ssh_rsa -> 'ssh-rsa'; - _ -> PK - end || PK <- PrefPKs], - [{preferred_algorithms, [{public_key,PKs}]} | Os]. + case proplists:get_value(public_key_alg, Os0) of + undefined -> + Os0; + A when is_atom(A) -> + %% Skip public_key_alg if pref_public_key_algs is defined: + Os = lists:keydelete(public_key_alg, 1, Os0), + case proplists:get_value(pref_public_key_algs,Os) of + undefined when A == 'ssh-rsa' ; A==ssh_rsa -> + [{pref_public_key_algs,['ssh-rsa','ssh-dss']} | Os]; + undefined when A == 'ssh-dss' ; A==ssh_dsa -> + [{pref_public_key_algs,['ssh-dss','ssh-rsa']} | Os]; + undefined -> + throw({error, {eoptions, {public_key_alg,A} }}); + _ -> + Os + end; + V -> + throw({error, {eoptions, {public_key_alg,V} }}) + end. handle_option([], SocketOptions, SshOptions) -> @@ -369,8 +357,12 @@ handle_option([{user_passwords, _} = Opt | Rest], SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); handle_option([{pwdfun, _} = Opt | Rest], SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); -handle_option([{key_cb, _} = Opt | Rest], SocketOptions, SshOptions) -> - handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); +handle_option([{key_cb, {Module, Options}} | Rest], SocketOptions, SshOptions) -> + handle_option(Rest, SocketOptions, [handle_ssh_option({key_cb, Module}), + handle_ssh_priv_option({key_cb_private, Options}) | + SshOptions]); +handle_option([{key_cb, Module} | Rest], SocketOptions, SshOptions) -> + handle_option([{key_cb, {Module, []}} | Rest], SocketOptions, SshOptions); handle_option([{keyboard_interact_fun, _} = Opt | Rest], SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); %%Backwards compatibility @@ -407,6 +399,8 @@ handle_option([{auth_methods, _} = Opt | Rest], SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); handle_option([{auth_method_kb_interactive_data, _} = Opt | Rest], SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); +handle_option([{pref_public_key_algs, _} = Opt | Rest], SocketOptions, SshOptions) -> + handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); handle_option([{preferred_algorithms,_} = Opt | Rest], SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); handle_option([{dh_gex_groups,_} = Opt | Rest], SocketOptions, SshOptions) -> @@ -518,6 +512,13 @@ handle_ssh_option({dh_gex_limits,{Min,I,Max}} = Opt) when is_integer(Min), Min>0 is_integer(Max), Max>=I -> %% Client Opt; +handle_ssh_option({pref_public_key_algs, Value} = Opt) when is_list(Value), length(Value) >= 1 -> + case handle_user_pref_pubkey_algs(Value, []) of + {true, NewOpts} -> + {pref_public_key_algs, NewOpts}; + _ -> + throw({error, {eoptions, Opt}}) + end; handle_ssh_option({connect_timeout, Value} = Opt) when is_integer(Value); Value == infinity -> Opt; handle_ssh_option({max_sessions, Value} = Opt) when is_integer(Value), Value>0 -> @@ -544,6 +545,9 @@ handle_ssh_option({pwdfun, Value} = Opt) when is_function(Value,4) -> Opt; handle_ssh_option({key_cb, Value} = Opt) when is_atom(Value) -> Opt; +handle_ssh_option({key_cb, {CallbackMod, CallbackOptions}} = Opt) when is_atom(CallbackMod), + is_list(CallbackOptions) -> + Opt; handle_ssh_option({keyboard_interact_fun, Value} = Opt) when is_function(Value,3) -> Opt; handle_ssh_option({compression, Value} = Opt) when is_atom(Value) -> @@ -610,6 +614,9 @@ handle_ssh_option({profile, Value} = Opt) when is_atom(Value) -> handle_ssh_option(Opt) -> throw({error, {eoptions, Opt}}). +handle_ssh_priv_option({key_cb_private, Value} = Opt) when is_list(Value) -> + Opt. + handle_inet_option({active, _} = Opt) -> throw({error, {{eoptions, Opt}, "SSH has built in flow control, " "and active is handled internally, user is not allowed" @@ -770,3 +777,16 @@ read_moduli_file(D, I, Acc) -> end end. +handle_user_pref_pubkey_algs([], Acc) -> + {true, lists:reverse(Acc)}; +handle_user_pref_pubkey_algs([H|T], Acc) -> + case lists:member(H, ?SUPPORTED_USER_KEYS) of + true -> + handle_user_pref_pubkey_algs(T, [H| Acc]); + + false when H==ssh_dsa -> handle_user_pref_pubkey_algs(T, ['ssh-dss'| Acc]); + false when H==ssh_rsa -> handle_user_pref_pubkey_algs(T, ['ssh-rsa'| Acc]); + + false -> + false + end. diff --git a/lib/ssh/src/ssh.hrl b/lib/ssh/src/ssh.hrl index 8efc743b67..f88098819d 100644 --- a/lib/ssh/src/ssh.hrl +++ b/lib/ssh/src/ssh.hrl @@ -33,6 +33,9 @@ -define(REKEY_DATA_TIMOUT, 60000). -define(DEFAULT_PROFILE, default). +-define(SUPPORTED_AUTH_METHODS, "publickey,keyboard-interactive,password"). +-define(SUPPORTED_USER_KEYS, ['ssh-rsa','ssh-dss','ecdsa-sha2-nistp256','ecdsa-sha2-nistp384','ecdsa-sha2-nistp521']). + -define(FALSE, 0). -define(TRUE, 1). %% basic binary constructors diff --git a/lib/ssh/src/ssh_auth.erl b/lib/ssh/src/ssh_auth.erl index 4967a2e4cd..fdbb5c152a 100644 --- a/lib/ssh/src/ssh_auth.erl +++ b/lib/ssh/src/ssh_auth.erl @@ -118,11 +118,16 @@ init_userauth_request_msg(#ssh{opts = Opts} = Ssh) -> service = "ssh-connection", method = "none", data = <<>>}, + Algs0 = proplists:get_value(pref_public_key_algs, Opts, ?SUPPORTED_USER_KEYS), + %% The following line is not strictly correct. The call returns the + %% supported HOST key types while we are interested in USER keys. However, + %% they "happens" to be the same (for now). This could change.... + %% There is no danger as long as the set of user keys is a subset of the set + %% of host keys. + CryptoSupported = ssh_transport:supported_algorithms(public_key), + Algs = [A || A <- Algs0, + lists:member(A, CryptoSupported)], - - Algs = proplists:get_value(public_key, - proplists:get_value(preferred_algorithms, Opts, []), - ssh_transport:default_algorithms(public_key)), Prefs = method_preference(Algs), ssh_transport:ssh_packet(Msg, Ssh#ssh{user = User, userauth_preference = Prefs, diff --git a/lib/ssh/src/ssh_auth.hrl b/lib/ssh/src/ssh_auth.hrl index 5197a42fa4..449bc4fa45 100644 --- a/lib/ssh/src/ssh_auth.hrl +++ b/lib/ssh/src/ssh_auth.hrl @@ -22,7 +22,6 @@ %%% Description: Ssh User Authentication Protocol --define(SUPPORTED_AUTH_METHODS, "publickey,keyboard-interactive,password"). -define(SSH_MSG_USERAUTH_REQUEST, 50). -define(SSH_MSG_USERAUTH_FAILURE, 51). diff --git a/lib/ssh/test/Makefile b/lib/ssh/test/Makefile index 96c74c6c8a..781a876723 100644 --- a/lib/ssh/test/Makefile +++ b/lib/ssh/test/Makefile @@ -47,6 +47,8 @@ MODULES= \ ssh_to_openssh_SUITE \ ssh_upgrade_SUITE \ ssh_test_lib \ + ssh_key_cb \ + ssh_key_cb_options \ ssh_trpt_test_lib \ ssh_echo_server \ ssh_peername_sockname_server \ diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl index 0a5964c560..85a6bac972 100644 --- a/lib/ssh/test/ssh_basic_SUITE.erl +++ b/lib/ssh/test/ssh_basic_SUITE.erl @@ -41,6 +41,10 @@ double_close/1, exec/1, exec_compressed/1, + exec_key_differs1/1, + exec_key_differs2/1, + exec_key_differs3/1, + exec_key_differs_fail/1, idle_time/1, inet6_option/1, inet_option/1, @@ -54,8 +58,10 @@ send/1, shell/1, shell_no_unicode/1, - shell_unicode_string/1, - ssh_info_print/1 + shell_unicode_string/1, + ssh_info_print/1, + key_callback/1, + key_callback_options/1 ]). %%% Common test callbacks @@ -84,6 +90,8 @@ all() -> {group, ecdsa_sha2_nistp521_key}, {group, dsa_pass_key}, {group, rsa_pass_key}, + {group, host_user_key_differs}, + {group, key_cb}, {group, internal_error}, daemon_already_started, double_close, @@ -99,8 +107,13 @@ groups() -> {ecdsa_sha2_nistp256_key, [], basic_tests()}, {ecdsa_sha2_nistp384_key, [], basic_tests()}, {ecdsa_sha2_nistp521_key, [], basic_tests()}, + {host_user_key_differs, [], [exec_key_differs1, + exec_key_differs2, + exec_key_differs3, + exec_key_differs_fail]}, {dsa_pass_key, [], [pass_phrase]}, {rsa_pass_key, [], [pass_phrase]}, + {key_cb, [], [key_callback, key_callback_options]}, {internal_error, [], [internal_error]} ]. @@ -180,6 +193,26 @@ init_per_group(dsa_pass_key, Config) -> PrivDir = ?config(priv_dir, Config), ssh_test_lib:setup_dsa_pass_pharse(DataDir, PrivDir, "Password"), [{pass_phrase, {dsa_pass_phrase, "Password"}}| Config]; +init_per_group(host_user_key_differs, Config) -> + Data = ?config(data_dir, Config), + Sys = filename:join(?config(priv_dir, Config), system_rsa), + SysUsr = filename:join(Sys, user), + Usr = filename:join(?config(priv_dir, Config), user_ecdsa_256), + file:make_dir(Sys), + file:make_dir(SysUsr), + file:make_dir(Usr), + file:copy(filename:join(Data, "ssh_host_rsa_key"), filename:join(Sys, "ssh_host_rsa_key")), + file:copy(filename:join(Data, "ssh_host_rsa_key.pub"), filename:join(Sys, "ssh_host_rsa_key.pub")), + file:copy(filename:join(Data, "id_ecdsa256"), filename:join(Usr, "id_ecdsa")), + file:copy(filename:join(Data, "id_ecdsa256.pub"), filename:join(Usr, "id_ecdsa.pub")), + ssh_test_lib:setup_ecdsa_auth_keys("256", Usr, SysUsr), + ssh_test_lib:setup_rsa_known_host(Sys, Usr), + Config; +init_per_group(key_cb, Config) -> + DataDir = ?config(data_dir, Config), + PrivDir = ?config(priv_dir, Config), + ssh_test_lib:setup_dsa(DataDir, PrivDir), + Config; init_per_group(internal_error, Config) -> DataDir = ?config(data_dir, Config), PrivDir = ?config(priv_dir, Config), @@ -247,6 +280,10 @@ end_per_group(rsa_pass_key, Config) -> PrivDir = ?config(priv_dir, Config), ssh_test_lib:clean_rsa(PrivDir), Config; +end_per_group(key_cb, Config) -> + PrivDir = ?config(priv_dir, Config), + ssh_test_lib:clean_dsa(PrivDir), + Config; end_per_group(internal_error, Config) -> PrivDir = ?config(priv_dir, Config), ssh_test_lib:clean_dsa(PrivDir), @@ -478,6 +515,80 @@ shell(Config) when is_list(Config) -> end. %%-------------------------------------------------------------------- +%%% Test that we could user different types of host pubkey and user pubkey +exec_key_differs1(Config) -> exec_key_differs(Config, ['ecdsa-sha2-nistp256']). + +exec_key_differs2(Config) -> exec_key_differs(Config, ['ssh-dss','ecdsa-sha2-nistp256']). + +exec_key_differs3(Config) -> exec_key_differs(Config, ['ecdsa-sha2-nistp384','ecdsa-sha2-nistp256']). + + + +exec_key_differs(Config, UserPKAlgs) -> + case lists:usort(['ssh-rsa'|UserPKAlgs]) + -- ssh_transport:supported_algorithms(public_key) + of + [] -> + process_flag(trap_exit, true), + SystemDir = filename:join(?config(priv_dir, Config), system_rsa), + SystemUserDir = filename:join(SystemDir, user), + UserDir = filename:join(?config(priv_dir, Config), user_ecdsa_256), + + {_Pid, _Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, + {user_dir, SystemUserDir}, + {preferred_algorithms, + [{public_key,['ssh-rsa']}]}]), + ct:sleep(500), + + IO = ssh_test_lib:start_io_server(), + Shell = ssh_test_lib:start_shell(Port, IO, UserDir, + [{preferred_algorithms,[{public_key,['ssh-rsa']}]}, + {pref_public_key_algs,UserPKAlgs} + ]), + + + receive + {'EXIT', _, _} -> + ct:fail(no_ssh_connection); + ErlShellStart -> + ct:log("Erlang shell start: ~p~n", [ErlShellStart]), + do_shell(IO, Shell) + after + 30000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) + end; + + UnsupportedPubKeys -> + {skip, io_lib:format("~p unsupported",[UnsupportedPubKeys])} + end. + +%%-------------------------------------------------------------------- +exec_key_differs_fail(Config) when is_list(Config) -> + process_flag(trap_exit, true), + SystemDir = filename:join(?config(priv_dir, Config), system_rsa), + SystemUserDir = filename:join(SystemDir, user), + UserDir = filename:join(?config(priv_dir, Config), user_ecdsa_256), + + {_Pid, _Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, + {user_dir, SystemUserDir}, + {preferred_algorithms, + [{public_key,['ssh-rsa']}]}]), + ct:sleep(500), + + IO = ssh_test_lib:start_io_server(), + ssh_test_lib:start_shell(Port, IO, UserDir, + [{preferred_algorithms,[{public_key,['ssh-rsa']}]}, + {pref_public_key_algs,['ssh-dss']}]), + receive + {'EXIT', _, _} -> + ok; + ErlShellStart -> + ct:log("Erlang shell start: ~p~n", [ErlShellStart]), + ct:fail(connection_not_rejected) + after + 30000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) + end. + +%%-------------------------------------------------------------------- cli(Config) when is_list(Config) -> process_flag(trap_exit, true), SystemDir = filename:join(?config(priv_dir, Config), system), @@ -575,6 +686,56 @@ pass_phrase(Config) when is_list(Config) -> {ok, _ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity), ssh:stop_daemon(Pid). +%%-------------------------------------------------------------------- +%%% Test that we can use key callback +key_callback(Config) when is_list(Config) -> + process_flag(trap_exit, true), + SystemDir = filename:join(?config(priv_dir, Config), system), + UserDir = ?config(priv_dir, Config), + NoPubKeyDir = filename:join(UserDir, "nopubkey"), + file:make_dir(NoPubKeyDir), + + {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, + {user_dir, UserDir}, + {failfun, fun ssh_test_lib:failfun/2}]), + + ConnectOpts = [{silently_accept_hosts, true}, + {user_dir, NoPubKeyDir}, + {user_interaction, false}, + {key_cb, ssh_key_cb}], + + ConnectionRef = ssh_test_lib:connect(Host, Port, ConnectOpts), + + {ok, _ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity), + ssh:stop_daemon(Pid). + + +%%-------------------------------------------------------------------- +%%% Test that we can use key callback with callback options +key_callback_options(Config) when is_list(Config) -> + process_flag(trap_exit, true), + SystemDir = filename:join(?config(priv_dir, Config), system), + UserDir = ?config(priv_dir, Config), + + NoPubKeyDir = filename:join(UserDir, "nopubkey"), + file:make_dir(NoPubKeyDir), + + {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, + {user_dir, UserDir}, + {failfun, fun ssh_test_lib:failfun/2}]), + + {ok, PrivKey} = file:read_file(filename:join(UserDir, "id_dsa")), + + ConnectOpts = [{silently_accept_hosts, true}, + {user_dir, NoPubKeyDir}, + {user_interaction, false}, + {key_cb, {ssh_key_cb_options, [{priv_key, PrivKey}]}}], + + ConnectionRef = ssh_test_lib:connect(Host, Port, ConnectOpts), + + {ok, _ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity), + ssh:stop_daemon(Pid). + %%-------------------------------------------------------------------- %%% Test that client does not hang if disconnects due to internal error diff --git a/lib/ssh/test/ssh_key_cb.erl b/lib/ssh/test/ssh_key_cb.erl new file mode 100644 index 0000000000..388ec2ecc1 --- /dev/null +++ b/lib/ssh/test/ssh_key_cb.erl @@ -0,0 +1,45 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2015. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +%% +%%---------------------------------------------------------------------- + +%% Note: This module is used by ssh_basic_SUITE + +-module(ssh_key_cb). +-behaviour(ssh_client_key_api). +-compile(export_all). + +add_host_key(_, _, _) -> + ok. + +is_host_key(_, _, _, _) -> + true. + +user_key('ssh-dss', Opts) -> + UserDir = proplists:get_value(user_dir, Opts), + KeyFile = filename:join(filename:dirname(UserDir), "id_dsa"), + {ok, KeyBin} = file:read_file(KeyFile), + [Entry] = public_key:pem_decode(KeyBin), + Key = public_key:pem_entry_decode(Entry), + {ok, Key}; + +user_key(_Alg, _Opt) -> + {error, "Not Supported"}. diff --git a/lib/ssh/test/ssh_key_cb_options.erl b/lib/ssh/test/ssh_key_cb_options.erl new file mode 100644 index 0000000000..afccb34f0f --- /dev/null +++ b/lib/ssh/test/ssh_key_cb_options.erl @@ -0,0 +1,44 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2015. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +%% +%%---------------------------------------------------------------------- + +%% Note: This module is used by ssh_basic_SUITE + +-module(ssh_key_cb_options). +-behaviour(ssh_client_key_api). +-compile(export_all). + +add_host_key(_, _, _) -> + ok. + +is_host_key(_, _, _, _) -> + true. + +user_key('ssh-dss', Opts) -> + KeyCbOpts = proplists:get_value(key_cb_private, Opts), + KeyBin = proplists:get_value(priv_key, KeyCbOpts), + [Entry] = public_key:pem_decode(KeyBin), + Key = public_key:pem_entry_decode(Entry), + {ok, Key}; + +user_key(_Alg, _Opt) -> + {error, "Not Supported"}. diff --git a/lib/ssh/test/ssh_renegotiate_SUITE.erl b/lib/ssh/test/ssh_renegotiate_SUITE.erl index 227dfcddcd..e5cfa58bad 100644 --- a/lib/ssh/test/ssh_renegotiate_SUITE.erl +++ b/lib/ssh/test/ssh_renegotiate_SUITE.erl @@ -57,9 +57,15 @@ end_per_suite(_Config) -> %%-------------------------------------------------------------------- init_per_group(aes_gcm, Config) -> - [{preferred_algorithms, [{cipher,[{client2server,['[email protected]']}, - {server2client,['[email protected]']}]}]} - | Config]; + case lists:member({client2server,['[email protected]']}, + ssh_transport:supported_algorithms(cipher)) of + true -> + [{preferred_algorithms, [{cipher,[{client2server,['[email protected]']}, + {server2client,['[email protected]']}]}]} + | Config]; + false -> + {skip, "aes_gcm not supported"} + end; init_per_group(_, Config) -> [{preferred_algorithms, ssh:default_algorithms()} | Config]. @@ -107,7 +113,9 @@ rekey_limit(Config) -> UserDir = ?config(priv_dir, Config), DataFile = filename:join(UserDir, "rekey.data"), - {Pid, Host, Port} = ssh_test_lib:std_daemon(Config,[{max_random_length_padding,0}]), + Algs = ?config(preferred_algorithms, Config), + {Pid, Host, Port} = ssh_test_lib:std_daemon(Config,[{max_random_length_padding,0}, + {preferred_algorithms,Algs}]), ConnectionRef = ssh_test_lib:std_connect(Config, Host, Port, [{rekey_limit, 6000}, {max_random_length_padding,0}]), @@ -151,7 +159,9 @@ renegotiate1(Config) -> UserDir = ?config(priv_dir, Config), DataFile = filename:join(UserDir, "renegotiate1.data"), - {Pid, Host, DPort} = ssh_test_lib:std_daemon(Config,[{max_random_length_padding,0}]), + Algs = ?config(preferred_algorithms, Config), + {Pid, Host, DPort} = ssh_test_lib:std_daemon(Config,[{max_random_length_padding,0}, + {preferred_algorithms,Algs}]), RPort = ssh_test_lib:inet_port(), {ok,RelayPid} = ssh_relay:start_link({0,0,0,0}, RPort, Host, DPort), @@ -189,7 +199,9 @@ renegotiate2(Config) -> UserDir = ?config(priv_dir, Config), DataFile = filename:join(UserDir, "renegotiate2.data"), - {Pid, Host, DPort} = ssh_test_lib:std_daemon(Config,[{max_random_length_padding,0}]), + Algs = ?config(preferred_algorithms, Config), + {Pid, Host, DPort} = ssh_test_lib:std_daemon(Config,[{max_random_length_padding,0}, + {preferred_algorithms,Algs}]), RPort = ssh_test_lib:inet_port(), {ok,RelayPid} = ssh_relay:start_link({0,0,0,0}, RPort, Host, DPort), diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml index 6faa3d5f9a..4d4a219b4f 100644 --- a/lib/ssl/doc/src/notes.xml +++ b/lib/ssl/doc/src/notes.xml @@ -26,8 +26,9 @@ <file>notes.xml</file> </header> <p>This document describes the changes made to the SSL application.</p> - <section><title>SSL 7.1</title> + +<section><title>SSL 7.1</title> <section><title>Fixed Bugs and Malfunctions</title> <list> <item> @@ -107,12 +108,6 @@ <p> Own Id: OTP-12815</p> </item> - <item> - <p> - Gracefully ignore proprietary hash_sign algorithms</p> - <p> - Own Id: OTP-12829</p> - </item> </list> </section> @@ -163,6 +158,20 @@ </section> +<section><title>SSL 6.0.1.1</title> + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Gracefully ignore proprietary hash_sign algorithms</p> + <p> + Own Id: OTP-12829</p> + </item> + </list> + </section> +</section> + + <section><title>SSL 6.0.1</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml index 22ac98c24e..3a541ed162 100644 --- a/lib/ssl/doc/src/ssl.xml +++ b/lib/ssl/doc/src/ssl.xml @@ -84,7 +84,7 @@ <seealso marker="kernel:gen_tcp">gen_tcp(3)</seealso> manual pages in Kernel.</p></item> - <tag><marker id="type-ssloption"></marker><c>ssloption() =</c></tag> + <tag><marker id="type-ssloption"/><c>ssloption() =</c></tag> <item> <p><c>{verify, verify_type()}</c></p> <p><c>| {verify_fun, {fun(), term()}}</c></p> @@ -160,7 +160,7 @@ <tag><c>sslsocket() =</c></tag> <item><p>opaque()</p></item> - <tag><c>protocol() =</c></tag> + <tag><marker id="type-protocol"/><c>protocol() =</c></tag> <item><p><c>sslv3 | tlsv1 | 'tlsv1.1' | 'tlsv1.2'</c></p></item> <tag><c>ciphers() =</c></tag> diff --git a/lib/ssl/doc/src/ssl_app.xml b/lib/ssl/doc/src/ssl_app.xml index 51ce0cedf1..24b0f5300e 100644 --- a/lib/ssl/doc/src/ssl_app.xml +++ b/lib/ssl/doc/src/ssl_app.xml @@ -58,7 +58,7 @@ <p><c>erl -ssl protocol_version "['tlsv1.2', 'tlsv1.1']"</c></p> <taglist> - <tag><c><![CDATA[protocol_version = <seealso marker="kernel:error_logger">ssl:protocol()</seealso> <optional>]]></c>.</tag> + <tag><c> protocol_version = <seealso marker="ssl#type-protocol">ssl:protocol()</seealso> <![CDATA[<optional>]]></c></tag> <item><p>Protocol supported by started clients and servers. If this option is not set, it defaults to all protocols currently supported by the SSL application. @@ -66,17 +66,24 @@ to <c>ssl:connect/[2,3]</c> and <c>ssl:listen/2</c>.</p></item> <tag><c><![CDATA[session_lifetime = integer() <optional>]]></c></tag> - <item><p>Lifetime of the session data in seconds.</p></item> + <item><p>Maximum lifetime of the session data in seconds.</p></item> <tag><c><![CDATA[session_cb = atom() <optional>]]></c></tag> <item><p>Name of the session cache callback module that implements the <c>ssl_session_cache_api</c> behavior. Defaults to - <c>ssl_session_cache.erl</c>.</p></item> + <c>ssl_session_cache</c>.</p></item> <tag><c><![CDATA[session_cb_init_args = proplist:proplist() <optional>]]></c></tag> <item><p>List of extra user-defined arguments to the <c>init</c> function in the session cache callback module. Defaults to <c>[]</c>.</p></item> + + <tag><c><![CDATA[session_cache_client_max = integer() <optional>]]></c></tag> + <tag><c><![CDATA[session_cache_server_max = integer() <optional>]]></c></tag> + <item><p>Limits the growth of the clients/servers session cache, + if the maximum number of sessions is reached, the current cache entries will + be invalidated regardless of their remaining lifetime. Defaults to 1000. + </p></item> <tag><c><![CDATA[ssl_pem_cache_clean = integer() <optional>]]></c></tag> <item> @@ -103,7 +110,10 @@ <section> <title>ERROR LOGGER AND EVENT HANDLERS</title> - <p>The SSL application uses the default <seealso marker="kernel:error_logger">OTP error logger</seealso> to log unexpected errors and TLS alerts. The logging of TLS alerts may be turned off with the <c>log_alert</c> option. </p> + <p>The SSL application uses the default <seealso + marker="kernel:error_logger">OTP error logger</seealso> to log + unexpected errors and TLS alerts. The logging of TLS alerts may be + turned off with the <c>log_alert</c> option. </p> </section> <section> diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl index 78662e0ea2..153d3fef48 100644 --- a/lib/ssl/src/dtls_connection.erl +++ b/lib/ssl/src/dtls_connection.erl @@ -145,7 +145,7 @@ init([Role, Host, Port, Socket, {SSLOpts0, _} = Options, User, CbInfo]) -> process_flag(trap_exit, true), State0 = initial_state(Role, Host, Port, Socket, Options, User, CbInfo), Handshake = ssl_handshake:init_handshake_history(), - TimeStamp = calendar:datetime_to_gregorian_seconds({date(), time()}), + TimeStamp = erlang:monotonic_time(), try ssl_config:init(SSLOpts0, Role) of {ok, Ref, CertDbHandle, FileRefHandle, CacheHandle, CRLDbInfo, OwnCert, Key, DHParams} -> Session = State0#state.session, diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index 12a56df69f..241871dc38 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -974,7 +974,7 @@ ssl_config(Opts, Role, State) -> {ok, Ref, CertDbHandle, FileRefHandle, CacheHandle, CRLDbInfo, OwnCert, Key, DHParams} = ssl_config:init(Opts, Role), Handshake = ssl_handshake:init_handshake_history(), - TimeStamp = calendar:datetime_to_gregorian_seconds({date(), time()}), + TimeStamp = erlang:monotonic_time(), Session = State#state.session, State#state{tls_handshake_history = Handshake, session = Session#session{own_certificate = OwnCert, diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl index cc15678f23..00e95f5c5b 100644 --- a/lib/ssl/src/ssl_manager.erl +++ b/lib/ssl/src/ssl_manager.erl @@ -46,15 +46,19 @@ -include_lib("kernel/include/file.hrl"). -record(state, { - session_cache_client, - session_cache_server, - session_cache_cb, - session_lifetime, - certificate_db, - session_validation_timer, + session_cache_client :: db_handle(), + session_cache_server :: db_handle(), + session_cache_cb :: atom(), + session_lifetime :: integer(), + certificate_db :: db_handle(), + session_validation_timer :: reference(), last_delay_timer = {undefined, undefined},%% Keep for testing purposes - last_pem_check, - clear_pem_cache + last_pem_check :: erlang:timestamp(), + clear_pem_cache :: integer(), + session_cache_client_max :: integer(), + session_cache_server_max :: integer(), + session_server_invalidator :: undefined | pid(), + session_client_invalidator :: undefined | pid() }). -define(GEN_UNIQUE_ID_MAX_TRIES, 10). @@ -62,7 +66,7 @@ -define(CLEAR_PEM_CACHE, 120000). -define(CLEAN_SESSION_DB, 60000). -define(CLEAN_CERT_DB, 500). --define(NOT_TO_BIG, 10). +-define(DEFAULT_MAX_SESSION_CACHE, 1000). %%==================================================================== %% API @@ -87,7 +91,8 @@ manager_name(dist) -> %%-------------------------------------------------------------------- start_link(Opts) -> DistMangerName = manager_name(normal), - gen_server:start_link({local, DistMangerName}, ?MODULE, [DistMangerName, Opts], []). + gen_server:start_link({local, DistMangerName}, + ?MODULE, [DistMangerName, Opts], []). %%-------------------------------------------------------------------- -spec start_link_dist(list()) -> {ok, pid()} | ignore | {error, term()}. @@ -97,7 +102,8 @@ start_link(Opts) -> %%-------------------------------------------------------------------- start_link_dist(Opts) -> DistMangerName = manager_name(dist), - gen_server:start_link({local, DistMangerName}, ?MODULE, [DistMangerName, Opts], []). + gen_server:start_link({local, DistMangerName}, + ?MODULE, [DistMangerName, Opts], []). %%-------------------------------------------------------------------- -spec connection_init(binary()| {der, list()}, client | server, @@ -167,7 +173,8 @@ new_session_id(Port) -> %% be called by ssl-connection processes. %%-------------------------------------------------------------------- clean_cert_db(Ref, File) -> - erlang:send_after(?CLEAN_CERT_DB, get(ssl_manager), {clean_cert_db, Ref, File}), + erlang:send_after(?CLEAN_CERT_DB, get(ssl_manager), + {clean_cert_db, Ref, File}), ok. %%-------------------------------------------------------------------- @@ -235,10 +242,12 @@ init([Name, Opts]) -> SessionLifeTime = proplists:get_value(session_lifetime, Opts, ?'24H_in_sec'), CertDb = ssl_pkix_db:create(), - ClientSessionCache = CacheCb:init([{role, client} | - proplists:get_value(session_cb_init_args, Opts, [])]), - ServerSessionCache = CacheCb:init([{role, server} | - proplists:get_value(session_cb_init_args, Opts, [])]), + ClientSessionCache = + CacheCb:init([{role, client} | + proplists:get_value(session_cb_init_args, Opts, [])]), + ServerSessionCache = + CacheCb:init([{role, server} | + proplists:get_value(session_cb_init_args, Opts, [])]), Timer = erlang:send_after(SessionLifeTime * 1000 + 5000, self(), validate_sessions), Interval = pem_check_interval(), @@ -250,7 +259,11 @@ init([Name, Opts]) -> session_lifetime = SessionLifeTime, session_validation_timer = Timer, last_pem_check = os:timestamp(), - clear_pem_cache = Interval + clear_pem_cache = Interval, + session_cache_client_max = + max_session_cache_size(session_cache_client_max), + session_cache_server_max = + max_session_cache_size(session_cache_server_max) }}. %%-------------------------------------------------------------------- @@ -267,7 +280,8 @@ init([Name, Opts]) -> handle_call({{connection_init, <<>>, Role, {CRLCb, UserCRLDb}}, _Pid}, _From, #state{certificate_db = [CertDb, FileRefDb, PemChace | _] = Db} = State) -> Ref = make_ref(), - Result = {ok, Ref, CertDb, FileRefDb, PemChace, session_cache(Role, State), {CRLCb, crl_db_info(Db, UserCRLDb)}}, + Result = {ok, Ref, CertDb, FileRefDb, PemChace, + session_cache(Role, State), {CRLCb, crl_db_info(Db, UserCRLDb)}}, {reply, Result, State#state{certificate_db = Db}}; handle_call({{connection_init, Trustedcerts, Role, {CRLCb, UserCRLDb}}, Pid}, _From, @@ -305,7 +319,8 @@ handle_call({{cache_pem,File}, _Pid}, _, _:Reason -> {reply, {error, Reason}, State} end; -handle_call({unconditionally_clear_pem_cache, _},_, #state{certificate_db = [_,_,PemChace | _]} = State) -> +handle_call({unconditionally_clear_pem_cache, _},_, + #state{certificate_db = [_,_,PemChace | _]} = State) -> ssl_pkix_db:clear(PemChace), {reply, ok, State}. @@ -317,27 +332,12 @@ handle_call({unconditionally_clear_pem_cache, _},_, #state{certificate_db = [_,_ %% %% Description: Handling cast messages %%-------------------------------------------------------------------- -handle_cast({register_session, Host, Port, Session}, - #state{session_cache_client = Cache, - session_cache_cb = CacheCb} = State) -> - TimeStamp = calendar:datetime_to_gregorian_seconds({date(), time()}), - NewSession = Session#session{time_stamp = TimeStamp}, - - case CacheCb:select_session(Cache, {Host, Port}) of - no_session -> - CacheCb:update(Cache, {{Host, Port}, - NewSession#session.session_id}, NewSession); - Sessions -> - register_unique_session(Sessions, NewSession, CacheCb, Cache, {Host, Port}) - end, +handle_cast({register_session, Host, Port, Session}, State0) -> + State = ssl_client_register_session(Host, Port, Session, State0), {noreply, State}; -handle_cast({register_session, Port, Session}, - #state{session_cache_server = Cache, - session_cache_cb = CacheCb} = State) -> - TimeStamp = calendar:datetime_to_gregorian_seconds({date(), time()}), - NewSession = Session#session{time_stamp = TimeStamp}, - CacheCb:update(Cache, {Port, NewSession#session.session_id}, NewSession), +handle_cast({register_session, Port, Session}, State0) -> + State = server_register_session(Port, Session, State0), {noreply, State}; handle_cast({invalidate_session, Host, Port, @@ -411,10 +411,10 @@ handle_info({clean_cert_db, Ref, File}, end, {noreply, State}; -handle_info({'EXIT', _, _}, State) -> - %% Session validator died!! Do we need to take any action? - %% maybe error log - {noreply, State}; +handle_info({'EXIT', Pid, _}, #state{session_client_invalidator = Pid} = State) -> + {noreply, State#state{session_client_invalidator = undefined}}; +handle_info({'EXIT', Pid, _}, #state{session_server_invalidator = Pid} = State) -> + {noreply, State#state{session_server_invalidator = undefined}}; handle_info(_Info, State) -> {noreply, State}. @@ -495,7 +495,15 @@ delay_time() -> ?CLEAN_SESSION_DB end. -invalidate_session(Cache, CacheCb, Key, Session, #state{last_delay_timer = LastTimer} = State) -> +max_session_cache_size(CacheType) -> + case application:get_env(ssl, CacheType) of + {ok, Size} when is_integer(Size) -> + Size; + _ -> + ?DEFAULT_MAX_SESSION_CACHE + end. + +invalidate_session(Cache, CacheCb, Key, Session, State) -> case CacheCb:lookup(Cache, Key) of undefined -> %% Session is already invalidated {noreply, State}; @@ -503,15 +511,23 @@ invalidate_session(Cache, CacheCb, Key, Session, #state{last_delay_timer = LastT CacheCb:delete(Cache, Key), {noreply, State}; _ -> - %% When a registered session is invalidated we need to wait a while before deleting - %% it as there might be pending connections that rightfully needs to look - %% up the session data but new connections should not get to use this session. - CacheCb:update(Cache, Key, Session#session{is_resumable = false}), - TRef = - erlang:send_after(delay_time(), self(), {delayed_clean_session, Key, Cache}), - {noreply, State#state{last_delay_timer = last_delay_timer(Key, TRef, LastTimer)}} + delayed_invalidate_session(CacheCb, Cache, Key, Session, State) end. +delayed_invalidate_session(CacheCb, Cache, Key, Session, + #state{last_delay_timer = LastTimer} = State) -> + %% When a registered session is invalidated we need to + %% wait a while before deleting it as there might be + %% pending connections that rightfully needs to look up + %% the session data but new connections should not get to + %% use this session. + CacheCb:update(Cache, Key, Session#session{is_resumable = false}), + TRef = + erlang:send_after(delay_time(), self(), + {delayed_clean_session, Key, Cache}), + {noreply, State#state{last_delay_timer = + last_delay_timer(Key, TRef, LastTimer)}}. + last_delay_timer({{_,_},_}, TRef, {LastServer, _}) -> {LastServer, TRef}; last_delay_timer({_,_}, TRef, {_, LastClient}) -> @@ -530,12 +546,12 @@ new_id(Port, Tries, Cache, CacheCb) -> Id = crypto:rand_bytes(?NUM_OF_SESSION_ID_BYTES), case CacheCb:lookup(Cache, {Port, Id}) of undefined -> - Now = calendar:datetime_to_gregorian_seconds({date(), time()}), + Now = erlang:monotonic_time(), %% New sessions can not be set to resumable %% until handshake is compleate and the %% other session values are set. CacheCb:update(Cache, {Port, Id}, #session{session_id = Id, - is_resumable = false, + is_resumable = new, time_stamp = Now}), Id; _ -> @@ -557,15 +573,62 @@ clean_cert_db(Ref, CertDb, RefDb, PemCache, File) -> ok end. +ssl_client_register_session(Host, Port, Session, #state{session_cache_client = Cache, + session_cache_cb = CacheCb, + session_cache_client_max = Max, + session_client_invalidator = Pid0} = State) -> + TimeStamp = erlang:monotonic_time(), + NewSession = Session#session{time_stamp = TimeStamp}, + + case CacheCb:select_session(Cache, {Host, Port}) of + no_session -> + Pid = do_register_session({{Host, Port}, + NewSession#session.session_id}, + NewSession, Max, Pid0, Cache, CacheCb), + State#state{session_client_invalidator = Pid}; + Sessions -> + register_unique_session(Sessions, NewSession, {Host, Port}, State) + end. + +server_register_session(Port, Session, #state{session_cache_server_max = Max, + session_cache_server = Cache, + session_cache_cb = CacheCb, + session_server_invalidator = Pid0} = State) -> + TimeStamp = erlang:monotonic_time(), + NewSession = Session#session{time_stamp = TimeStamp}, + Pid = do_register_session({Port, NewSession#session.session_id}, + NewSession, Max, Pid0, Cache, CacheCb), + State#state{session_server_invalidator = Pid}. + +do_register_session(Key, Session, Max, Pid, Cache, CacheCb) -> + try CacheCb:size(Cache) of + N when N > Max -> + invalidate_session_cache(Pid, CacheCb, Cache); + _ -> + CacheCb:update(Cache, Key, Session), + Pid + catch + error:undef -> + CacheCb:update(Cache, Key, Session), + Pid + end. + + %% Do not let dumb clients create a gigantic session table %% for itself creating big delays at connection time. -register_unique_session(Sessions, Session, CacheCb, Cache, PartialKey) -> +register_unique_session(Sessions, Session, PartialKey, + #state{session_cache_client_max = Max, + session_cache_client = Cache, + session_cache_cb = CacheCb, + session_client_invalidator = Pid0} = State) -> case exists_equivalent(Session , Sessions) of true -> - ok; + State; false -> - CacheCb:update(Cache, {PartialKey, - Session#session.session_id}, Session) + Pid = do_register_session({PartialKey, + Session#session.session_id}, + Session, Max, Pid0, Cache, CacheCb), + State#state{session_client_invalidator = Pid} end. exists_equivalent(_, []) -> @@ -620,7 +683,8 @@ pem_check_interval() -> end. is_before_checkpoint(Time, CheckPoint) -> - calendar:datetime_to_gregorian_seconds(calendar:now_to_datetime(CheckPoint)) - + calendar:datetime_to_gregorian_seconds( + calendar:now_to_datetime(CheckPoint)) - calendar:datetime_to_gregorian_seconds(Time) > 0. add_trusted_certs(Pid, Trustedcerts, Db) -> @@ -641,3 +705,9 @@ crl_db_info([_,_,_,Local], {internal, Info}) -> crl_db_info(_, UserCRLDb) -> UserCRLDb. +%% Only start a session invalidator if there is not +%% one already active +invalidate_session_cache(undefined, CacheCb, Cache) -> + start_session_validator(Cache, CacheCb, {invalidate_before, erlang:monotonic_time()}); +invalidate_session_cache(Pid, _CacheCb, _Cache) -> + Pid. diff --git a/lib/ssl/src/ssl_session.erl b/lib/ssl/src/ssl_session.erl index 1849a05314..2b24bff5ff 100644 --- a/lib/ssl/src/ssl_session.erl +++ b/lib/ssl/src/ssl_session.erl @@ -61,13 +61,16 @@ client_id(ClientInfo, Cache, CacheCb, OwnCert) -> SessionId end. --spec valid_session(#session{}, seconds()) -> boolean(). +-spec valid_session(#session{}, seconds() | {invalidate_before, integer()}) -> boolean(). %% %% Description: Check that the session has not expired %%-------------------------------------------------------------------- +valid_session(#session{time_stamp = TimeStamp}, {invalidate_before, Before}) -> + TimeStamp > Before; valid_session(#session{time_stamp = TimeStamp}, LifeTime) -> - Now = calendar:datetime_to_gregorian_seconds({date(), time()}), - Now - TimeStamp < LifeTime. + Now = erlang:monotonic_time(), + Lived = erlang:convert_time_unit(Now-TimeStamp, native, seconds), + Lived < LifeTime. server_id(Port, <<>>, _SslOpts, _Cert, _, _) -> {ssl_manager:new_session_id(Port), undefined}; diff --git a/lib/ssl/src/ssl_session_cache.erl b/lib/ssl/src/ssl_session_cache.erl index cfc48cd935..9585e613e6 100644 --- a/lib/ssl/src/ssl_session_cache.erl +++ b/lib/ssl/src/ssl_session_cache.erl @@ -27,7 +27,7 @@ -include("ssl_internal.hrl"). -export([init/1, terminate/1, lookup/2, update/3, delete/2, foldl/3, - select_session/2]). + select_session/2, size/1]). %%-------------------------------------------------------------------- %% Description: Return table reference. Called by ssl_manager process. @@ -86,6 +86,12 @@ select_session(Cache, PartialKey) -> [{{{PartialKey,'_'}, '$1'},[],['$1']}]). %%-------------------------------------------------------------------- +%% Description: Returns the cache size +%%-------------------------------------------------------------------- +size(Cache) -> + ets:info(Cache, size). + +%%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- cache_name(Name) -> diff --git a/lib/ssl/src/ssl_session_cache_api.erl b/lib/ssl/src/ssl_session_cache_api.erl index 536b52c44b..8f62c25be5 100644 --- a/lib/ssl/src/ssl_session_cache_api.erl +++ b/lib/ssl/src/ssl_session_cache_api.erl @@ -33,3 +33,4 @@ -callback delete(db_handle(), key()) -> any(). -callback foldl(fun(), term(), db_handle()) -> term(). -callback select_session(db_handle(), {host(), inet:port_number()} | inet:port_number()) -> [#session{}]. +-callback size(db_handle()) -> integer(). diff --git a/lib/ssl/src/ssl_v3.erl b/lib/ssl/src/ssl_v3.erl index 5e043624a7..f169059a75 100644 --- a/lib/ssl/src/ssl_v3.erl +++ b/lib/ssl/src/ssl_v3.erl @@ -144,6 +144,7 @@ suites() -> ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA, ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA, ?TLS_RSA_WITH_AES_128_CBC_SHA, + ?TLS_DHE_RSA_WITH_DES_CBC_SHA, ?TLS_RSA_WITH_DES_CBC_SHA ]. diff --git a/lib/ssl/test/ssl_session_cache_SUITE.erl b/lib/ssl/test/ssl_session_cache_SUITE.erl index 924898f6fa..85345c814f 100644 --- a/lib/ssl/test/ssl_session_cache_SUITE.erl +++ b/lib/ssl/test/ssl_session_cache_SUITE.erl @@ -31,6 +31,7 @@ -define(SLEEP, 500). -define(TIMEOUT, 60000). -define(LONG_TIMEOUT, 600000). +-define(MAX_TABLE_SIZE, 5). -behaviour(ssl_session_cache_api). @@ -46,7 +47,9 @@ all() -> [session_cleanup, session_cache_process_list, session_cache_process_mnesia, - client_unique_session]. + client_unique_session, + max_table_size + ]. groups() -> []. @@ -92,7 +95,17 @@ init_per_testcase(session_cleanup, Config) -> Config; init_per_testcase(client_unique_session, Config) -> - ct:timetrap({seconds, 20}), + ct:timetrap({seconds, 40}), + Config; + +init_per_testcase(max_table_size, Config) -> + ssl:stop(), + application:load(ssl), + application:set_env(ssl, session_cache_server_max, ?MAX_TABLE_SIZE), + application:set_env(ssl, session_cache_client_max, ?MAX_TABLE_SIZE), + application:set_env(ssl, session_delay_cleanup_time, ?DELAY), + ssl:start(), + ct:timetrap({seconds, 40}), Config. init_customized_session_cache(Type, Config) -> @@ -122,6 +135,10 @@ end_per_testcase(session_cleanup, Config) -> application:unset_env(ssl, session_delay_cleanup_time), application:unset_env(ssl, session_lifetime), end_per_testcase(default_action, Config); +end_per_testcase(max_table_size, Config) -> + application:unset_env(ssl, session_cach_server_max), + application:unset_env(ssl, session_cach_client_max), + end_per_testcase(default_action, Config); end_per_testcase(Case, Config) when Case == session_cache_process_list; Case == session_cache_process_mnesia -> ets:delete(ssl_test), @@ -148,7 +165,7 @@ client_unique_session(Config) when is_list(Config) -> {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), LastClient = clients_start(Server, - ClientNode, Hostname, Port, ClientOpts, 20), + ClientNode, Hostname, Port, ClientOpts, client_unique_session, 20), receive {LastClient, {ok, _}} -> ok @@ -157,7 +174,8 @@ client_unique_session(Config) when is_list(Config) -> [_, _,_, _, Prop] = StatusInfo, State = ssl_test_lib:state(Prop), ClientCache = element(2, State), - 1 = ets:info(ClientCache, size), + + 1 = ssl_session_cache:size(ClientCache), ssl_test_lib:close(Server, 500), ssl_test_lib:close(LastClient). @@ -223,35 +241,7 @@ session_cleanup(Config) when is_list(Config) -> ssl_test_lib:close(Server), ssl_test_lib:close(Client). -check_timer(Timer) -> - case erlang:read_timer(Timer) of - false -> - {status, _, _, _} = sys:get_status(whereis(ssl_manager)), - timer:sleep(?SLEEP), - {status, _, _, _} = sys:get_status(whereis(ssl_manager)), - ok; - Int -> - ct:sleep(Int), - check_timer(Timer) - end. -get_delay_timers() -> - {status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)), - [_, _,_, _, Prop] = StatusInfo, - State = ssl_test_lib:state(Prop), - case element(8, State) of - {undefined, undefined} -> - ct:sleep(?SLEEP), - get_delay_timers(); - {undefined, _} -> - ct:sleep(?SLEEP), - get_delay_timers(); - {_, undefined} -> - ct:sleep(?SLEEP), - get_delay_timers(); - DelayTimers -> - DelayTimers - end. %%-------------------------------------------------------------------- session_cache_process_list() -> [{doc,"Test reuse of sessions (short handshake)"}]. @@ -264,6 +254,42 @@ session_cache_process_mnesia(Config) when is_list(Config) -> session_cache_process(mnesia,Config). %%-------------------------------------------------------------------- + +max_table_size() -> + [{doc,"Test max limit on session table"}]. +max_table_size(Config) when is_list(Config) -> + process_flag(trap_exit, true), + ClientOpts = ?config(client_verification_opts, Config), + ServerOpts = ?config(server_verification_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {tcp_options, [{active, false}]}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + LastClient = clients_start(Server, + ClientNode, Hostname, Port, ClientOpts, max_table_size, 20), + receive + {LastClient, {ok, _}} -> + ok + end, + ct:sleep(1000), + {status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)), + [_, _,_, _, Prop] = StatusInfo, + State = ssl_test_lib:state(Prop), + ClientCache = element(2, State), + ServerCache = element(3, State), + N = ssl_session_cache:size(ServerCache), + M = ssl_session_cache:size(ClientCache), + ct:pal("~p",[{N, M}]), + ssl_test_lib:close(Server, 500), + ssl_test_lib:close(LastClient), + true = N =< ?MAX_TABLE_SIZE, + true = M =< ?MAX_TABLE_SIZE. + +%%-------------------------------------------------------------------- %%% Session cache API callbacks %%-------------------------------------------------------------------- @@ -403,21 +429,73 @@ session_cache_process(_Type,Config) when is_list(Config) -> ssl_basic_SUITE:reuse_session(Config). -clients_start(_Server, ClientNode, Hostname, Port, ClientOpts, 0) -> +clients_start(_Server, ClientNode, Hostname, Port, ClientOpts, Test, 0) -> %% Make sure session is registered ct:sleep(?SLEEP * 2), ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {mfa, {?MODULE, connection_info_result, []}}, - {from, self()}, {options, ClientOpts}]); -clients_start(Server, ClientNode, Hostname, Port, ClientOpts, N) -> + {from, self()}, {options, test_copts(Test, 0, ClientOpts)}]); +clients_start(Server, ClientNode, Hostname, Port, ClientOpts, Test, N) -> spawn_link(ssl_test_lib, start_client, [[{node, ClientNode}, {port, Port}, {host, Hostname}, {mfa, {ssl_test_lib, no_result, []}}, - {from, self()}, {options, ClientOpts}]]), + {from, self()}, {options, test_copts(Test, N, ClientOpts)}]]), Server ! listen, - clients_start(Server, ClientNode, Hostname, Port, ClientOpts, N-1). + wait_for_server(), + clients_start(Server, ClientNode, Hostname, Port, ClientOpts, Test, N-1). connection_info_result(Socket) -> ssl:connection_information(Socket, [protocol, cipher_suite]). + +check_timer(Timer) -> + case erlang:read_timer(Timer) of + false -> + {status, _, _, _} = sys:get_status(whereis(ssl_manager)), + timer:sleep(?SLEEP), + {status, _, _, _} = sys:get_status(whereis(ssl_manager)), + ok; + Int -> + ct:sleep(Int), + check_timer(Timer) + end. + +get_delay_timers() -> + {status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)), + [_, _,_, _, Prop] = StatusInfo, + State = ssl_test_lib:state(Prop), + case element(8, State) of + {undefined, undefined} -> + ct:sleep(?SLEEP), + get_delay_timers(); + {undefined, _} -> + ct:sleep(?SLEEP), + get_delay_timers(); + {_, undefined} -> + ct:sleep(?SLEEP), + get_delay_timers(); + DelayTimers -> + DelayTimers + end. + +wait_for_server() -> + ct:sleep(100). + + +test_copts(_, 0, ClientOpts) -> + ClientOpts; +test_copts(max_table_size, N, ClientOpts) -> + Version = tls_record:highest_protocol_version([]), + CipherSuites = %%lists:map(fun(X) -> ssl_cipher:suite_definition(X) end, ssl_cipher:filter_suites(ssl_cipher:suites(Version))), +[ Y|| Y = {Alg,_, _, _} <- lists:map(fun(X) -> ssl_cipher:suite_definition(X) end, ssl_cipher:filter_suites(ssl_cipher:suites(Version))), Alg =/= ecdhe_ecdsa, Alg =/= ecdh_ecdsa, Alg =/= ecdh_rsa, Alg =/= ecdhe_rsa, Alg =/= dhe_dss, Alg =/= dss], + case length(CipherSuites) of + M when M >= N -> + Cipher = lists:nth(N, CipherSuites), + ct:pal("~p",[Cipher]), + [{ciphers, [Cipher]} | ClientOpts]; + _ -> + ClientOpts + end; +test_copts(_, _, ClientOpts) -> + ClientOpts. diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl index c4cb5fdc80..5678e7eebe 100644 --- a/lib/stdlib/src/erl_lint.erl +++ b/lib/stdlib/src/erl_lint.erl @@ -293,6 +293,9 @@ format_error({variable_in_record_def,V}) -> %% --- binaries --- format_error({undefined_bittype,Type}) -> io_lib:format("bit type ~w undefined", [Type]); +format_error({bittype_mismatch,Val1,Val2,What}) -> + io_lib:format("conflict in ~s specification for bit field: '~p' and '~p'", + [What,Val1,Val2]); format_error(bittype_unit) -> "a bit unit size must not be specified unless a size is specified too"; format_error(illegal_bitsize) -> diff --git a/lib/stdlib/src/erl_pp.erl b/lib/stdlib/src/erl_pp.erl index f7a969977a..c5177aca90 100644 --- a/lib/stdlib/src/erl_pp.erl +++ b/lib/stdlib/src/erl_pp.erl @@ -253,6 +253,8 @@ lattribute(import, Name, _Opts, _State) when is_list(Name) -> attr("import", [{var,a0(),pname(Name)}]); lattribute(import, {From,Falist}, _Opts, _State) -> attr("import",[{var,a0(),pname(From)},falist(Falist)]); +lattribute(export_type, Talist, _Opts, _State) -> + call({var,a0(),"-export_type"}, [falist(Talist)], 0, options(none)); lattribute(optional_callbacks, Falist, Opts, _State) -> ArgL = try falist(Falist) catch _:_ -> abstract(Falist, Opts) @@ -321,7 +323,6 @@ ltype({type,_,'fun',[{type,_,any},_]}=FunType, _) -> ltype({type,_Line,'fun',[{type,_,product,_},_]}=FunType, _) -> [fun_type(['fun',$(], FunType),$)]; ltype({type,Line,T,Ts}, _) -> - %% Compatibility. Before 18.0. simple_type({atom,Line,T}, Ts); ltype({user_type,Line,T,Ts}, _) -> simple_type({atom,Line,T}, Ts); @@ -346,16 +347,8 @@ map_type(Fs) -> map_pair_types(Fs) -> tuple_type(Fs, fun map_pair_type/2). -map_pair_type({type,_Line,map_field_assoc,[Ktype,Vtype]}, Prec) -> - map_assoc_typed(ltype(Ktype), Vtype, Prec). - -map_assoc_typed(B, {type,_,union,Ts}, Prec) -> - {first,[B,$\s],{seq,[],[],[],map_assoc_union_type(Ts, Prec)}}; -map_assoc_typed(B, Type, Prec) -> - {list,[{cstep,[B," =>"],ltype(Type, Prec)}]}. - -map_assoc_union_type([T|Ts], Prec) -> - [[leaf("=> "),ltype(T)] | ltypes(Ts, fun union_elem/2, Prec)]. +map_pair_type({type,_Line,map_field_assoc,[KType,VType]}, Prec) -> + {list,[{cstep,[ltype(KType, Prec),leaf(" =>")],ltype(VType, Prec)}]}. record_type(Name, Fields) -> {first,[record_name(Name)],field_types(Fields)}. @@ -370,9 +363,6 @@ typed(B, Type) -> {_L,_P,R} = type_inop_prec('::'), {list,[{cstep,[B,' ::'],ltype(Type, R)}]}. -union_elem(T, Prec) -> - [leaf(" | "),ltype(T, Prec)]. - tuple_type(Ts, F) -> {seq,${,$},[$,],ltypes(Ts, F, 0)}. @@ -399,6 +389,9 @@ guard_type(Before, Gs) -> Gl = {list,[{step,'when',expr_list(Gs, [$,], fun constraint/2, Opts)}]}, {list,[{step,Before,Gl}]}. +constraint({type,_Line,constraint,[{atom,_,is_subtype},[{var,_,_}=V,Type]]}, + _Opts) -> + typed(lexpr(V, options(none)), Type); constraint({type,_Line,constraint,[Tag,As]}, _Opts) -> simple_type(Tag, As). diff --git a/lib/stdlib/src/rand.erl b/lib/stdlib/src/rand.erl index 8e8d0bc801..dc060e82d9 100644 --- a/lib/stdlib/src/rand.erl +++ b/lib/stdlib/src/rand.erl @@ -63,7 +63,7 @@ %% Return algorithm and seed so that RNG state can be recreated with seed/1 -spec export_seed() -> undefined | export_state(). export_seed() -> - case seed_get() of + case get(?SEED_DICT) of {#{type:=Alg}, Seed} -> {Alg, Seed}; _ -> undefined end. diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl index 0424e2b967..5347ccaf1f 100644 --- a/lib/stdlib/test/erl_lint_SUITE.erl +++ b/lib/stdlib/test/erl_lint_SUITE.erl @@ -3604,7 +3604,10 @@ bin_syntax_errors(Config) -> t(<<X/unit:8>>) -> X; t(<<X:7/float>>) -> X; t(<< <<_:8>> >>) -> ok; - t(<<(x ! y):8/integer>>) -> ok. + t(<<(x ! y):8/integer>>) -> ok; + t(X) -> + {<<X/binary-integer>>,<<X/signed-unsigned-integer>>, + <<X/little-big>>,<<X/unit:4-unit:8>>}. ">>, [], {error,[{1,erl_lint,illegal_bitsize}, @@ -3613,7 +3616,12 @@ bin_syntax_errors(Config) -> {4,erl_lint,{undefined_bittype,bad_type}}, {5,erl_lint,bittype_unit}, {7,erl_lint,illegal_pattern}, - {8,erl_lint,illegal_pattern}], + {8,erl_lint,illegal_pattern}, + {10,erl_lint,{bittype_mismatch,integer,binary,"type"}}, + {10,erl_lint,{bittype_mismatch,unsigned,signed,"sign"}}, + {11,erl_lint,{bittype_mismatch,8,4,"unit"}}, + {11,erl_lint,{bittype_mismatch,big,little,"endianness"}} + ], [{6,erl_lint,{bad_bitsize,"float"}}]}} ], [] = run(Config, Ts), @@ -3920,22 +3928,35 @@ run_test2(Conf, Test, Warnings0) -> %% is no reason to produce an output file since we are only %% interested in the errors and warnings. - %% Print warnings, call erl_lint:format_error/1. + %% Print warnings, call erl_lint:format_error/1. (But note that + %% the compiler will ignore failing calls to erl_lint:format_error/1.) compile:file(File, [binary,report|Opts]), case compile:file(File, [binary|Opts]) of - {ok, _M, Code, Ws} when is_binary(Code) -> warnings(File, Ws); - {error, [{File,Es}], []} -> {errors, Es, []}; - {error, [{File,Es}], [{File,Ws}]} -> {error, Es, Ws}; - {error, [{File,Es1},{File,Es2}], []} -> {errors2, Es1, Es2} + {ok, _M, Code, Ws} when is_binary(Code) -> + warnings(File, Ws); + {error, [{File,Es}], []} -> + {errors, call_format_error(Es), []}; + {error, [{File,Es}], [{File,Ws}]} -> + {error, call_format_error(Es), call_format_error(Ws)}; + {error, [{File,Es1},{File,Es2}], []} -> + {errors2, Es1, Es2} end. warnings(File, Ws) -> case lists:append([W || {F, W} <- Ws, F =:= File]) of - [] -> []; - L -> {warnings, L} + [] -> + []; + L -> + {warnings, call_format_error(L)} end. +call_format_error(L) -> + %% Smoke test of format_error/1 to make sure that no crashes + %% slip through. + _ = [Mod:format_error(Term) || {_,Mod,Term} <- L], + L. + fail() -> io:format("failed~n"), ?t:fail(). diff --git a/lib/stdlib/test/erl_pp_SUITE.erl b/lib/stdlib/test/erl_pp_SUITE.erl index 389fd059f6..92e2764c65 100644 --- a/lib/stdlib/test/erl_pp_SUITE.erl +++ b/lib/stdlib/test/erl_pp_SUITE.erl @@ -960,6 +960,9 @@ maps_syntax(Config) when is_list(Config) -> "-compile(export_all).\n" "-type t1() :: map().\n" "-type t2() :: #{ atom() => integer(), atom() => float() }.\n" + "-type u() :: #{a => (I :: integer()) | (A :: atom()),\n" + " (X :: atom()) | (Y :: atom()) =>\n" + " (I :: integer()) | (A :: atom())}.\n" "-spec f1(t1()) -> 'true'.\n" "f1(M) when is_map(M) -> true.\n" "-spec f2(t2()) -> integer().\n" diff --git a/lib/stdlib/test/rand_SUITE.erl b/lib/stdlib/test/rand_SUITE.erl index 111bf620de..03b5ce1a25 100644 --- a/lib/stdlib/test/rand_SUITE.erl +++ b/lib/stdlib/test/rand_SUITE.erl @@ -126,6 +126,9 @@ seed_1(Alg) -> false = (S1 =:= rand:seed_s(Alg)), %% Negative integers works _ = rand:seed_s(Alg, {-1,-1,-1}), + %% Check that export_seed/1 returns 'undefined' if there is no seed + erase(rand_seed), + undefined = rand:export_seed(), %% Other term do not work {'EXIT', _} = (catch rand:seed_s(foobar, os:timestamp())), diff --git a/lib/wx/api_gen/gl_gen_erl.erl b/lib/wx/api_gen/gl_gen_erl.erl index 20406a8d05..84e9600bc0 100644 --- a/lib/wx/api_gen/gl_gen_erl.erl +++ b/lib/wx/api_gen/gl_gen_erl.erl @@ -226,10 +226,14 @@ gen_types(Where) -> w("-type clamp() :: float(). %% 0.0..1.0~n", []), w("-type offset() :: non_neg_integer(). %% Offset in memory block~n", []) end, - w("-type matrix() :: {float(),float(),float(),float(),~n", []), + w("-type matrix12() :: {float(),float(),float(),float(),~n", []), + w(" float(),float(),float(),float(),~n", []), + w(" float(),float(),float(),float()}.~n", []), + w("-type matrix16() :: {float(),float(),float(),float(),~n", []), w(" float(),float(),float(),float(),~n", []), w(" float(),float(),float(),float(),~n", []), w(" float(),float(),float(),float()}.~n", []), + w("-type matrix() :: matrix12() | matrix16().~n", []), w("-type mem() :: binary() | tuple(). %% Memory block~n", []), ok. @@ -480,10 +484,12 @@ doc_arg_type2(T=#type{single=true}) -> doc_arg_type3(T); doc_arg_type2(T=#type{single=undefined}) -> doc_arg_type3(T); -doc_arg_type2(T=#type{single={tuple,undefined}}) -> - "{" ++ doc_arg_type3(T) ++ "}"; +doc_arg_type2(_T=#type{single={tuple,undefined}}) -> + "tuple()"; doc_arg_type2(#type{base=float, single={tuple,16}}) -> "matrix()"; +doc_arg_type2(#type{base=string, single=list}) -> + "iolist()"; doc_arg_type2(T=#type{single={tuple,Sz}}) -> "{" ++ args(fun doc_arg_type3/1, ",", lists:duplicate(Sz,T)) ++ "}"; doc_arg_type2(T=#type{single=list}) -> diff --git a/lib/wx/api_gen/wx_extra/wxEvtHandler.c_src b/lib/wx/api_gen/wx_extra/wxEvtHandler.c_src index 5e02066309..08fef1c2ff 100644 --- a/lib/wx/api_gen/wx_extra/wxEvtHandler.c_src +++ b/lib/wx/api_gen/wx_extra/wxEvtHandler.c_src @@ -42,11 +42,15 @@ case 101: { // wxEvtHandler::Disconnect int eventType = wxeEventTypeFromAtom(bp); bp += *eventTypeLen; if(eventType > 0) { + if(recurse_level > 1) { + delayed_delete->Append(Ecmd.Save()); + } else { bool Result = This->Disconnect((int) *winid,(int) *lastId,eventType, (wxObjectEventFunction)(wxEventFunction) &wxeEvtListener::forward, NULL, Listener); rt.addBool(Result); + } } else { rt.addAtom("badarg"); rt.addAtom("event_type"); diff --git a/lib/wx/api_gen/wx_gen_cpp.erl b/lib/wx/api_gen/wx_gen_cpp.erl index 5649336b5d..ed7b27f3bf 100644 --- a/lib/wx/api_gen/wx_gen_cpp.erl +++ b/lib/wx/api_gen/wx_gen_cpp.erl @@ -1141,6 +1141,7 @@ gen_macros() -> w("#include <wx/html/htmlcell.h>~n"), w("#include <wx/filename.h>~n"), w("#include <wx/sysopt.h>~n"), + w("#include <wx/overlay.h>~n"), w("~n~n", []), w("#ifndef wxICON_DEFAULT_BITMAP_TYPE~n",[]), @@ -1276,6 +1277,11 @@ encode_events(Evs) -> w(" } else {~n"), w(" send_res = rt.send();~n"), w(" if(cb->skip) event->Skip();~n"), + w(" if(app->recurse_level < 1) {~n"), + w(" app->recurse_level++;~n"), + w(" app->dispatch_cmds();~n"), + w(" app->recurse_level--;~n"), + w(" }~n"), w(" };~n"), w(" return send_res;~n"), w(" }~n"). diff --git a/lib/wx/api_gen/wxapi.conf b/lib/wx/api_gen/wxapi.conf index f076323bea..eabbec3297 100644 --- a/lib/wx/api_gen/wxapi.conf +++ b/lib/wx/api_gen/wxapi.conf @@ -367,7 +367,7 @@ {class,wxMirrorDC, wxDC, [], ['wxMirrorDC', '~wxMirrorDC']}. {class,wxScreenDC, wxDC, [], ['wxScreenDC', '~wxScreenDC']}. -{class,wxPostScriptDC,wxDC,[], +{class,wxPostScriptDC,wxDC,[{ifdef, wxUSE_POSTSCRIPT}], ['wxPostScriptDC','~wxPostScriptDC', {'SetResolution', [{deprecated, "!wxCHECK_VERSION(2,9,0)"}]}, {'GetResolution', [{deprecated, "!wxCHECK_VERSION(2,9,0)"}]}]}. @@ -1975,3 +1975,9 @@ {class, wxMouseCaptureLostEvent, wxEvent, [{event,[wxEVT_MOUSE_CAPTURE_LOST]}],[]}. + +{class, wxOverlay, root, [], + ['wxOverlay', '~wxOverlay', 'Reset']}. + +{class, wxDCOverlay, root, [], + ['wxDCOverlay', '~wxDCOverlay', 'Clear']}. diff --git a/lib/wx/c_src/gen/wxe_derived_dest.h b/lib/wx/c_src/gen/wxe_derived_dest.h index 03d1502c2a..1dcf029244 100644 --- a/lib/wx/c_src/gen/wxe_derived_dest.h +++ b/lib/wx/c_src/gen/wxe_derived_dest.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2008-2014. All Rights Reserved. + * Copyright Ericsson AB 2008-2015. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -86,11 +86,13 @@ class EwxScreenDC : public wxScreenDC { EwxScreenDC() : wxScreenDC() {}; }; +#if wxUSE_POSTSCRIPT class EwxPostScriptDC : public wxPostScriptDC { public: ~EwxPostScriptDC() {((WxeApp *)wxTheApp)->clearPtr(this);}; EwxPostScriptDC(const wxPrintData& printData) : wxPostScriptDC(printData) {}; EwxPostScriptDC() : wxPostScriptDC() {}; }; +#endif // wxUSE_POSTSCRIPT class EwxWindowDC : public wxWindowDC { public: ~EwxWindowDC() {((WxeApp *)wxTheApp)->clearPtr(this);}; @@ -787,3 +789,9 @@ class EwxPopupTransientWindow : public wxPopupTransientWindow { }; #endif // wxUSE_POPUPWIN +class EwxDCOverlay : public wxDCOverlay { + public: ~EwxDCOverlay() {((WxeApp *)wxTheApp)->clearPtr(this);}; + EwxDCOverlay(wxOverlay& overlay,wxWindowDC * dc,int x,int y,int width,int height) : wxDCOverlay(overlay,dc,x,y,width,height) {}; + EwxDCOverlay(wxOverlay& overlay,wxWindowDC * dc) : wxDCOverlay(overlay,dc) {}; +}; + diff --git a/lib/wx/c_src/gen/wxe_events.cpp b/lib/wx/c_src/gen/wxe_events.cpp index a532ee985d..4affe2ba53 100644 --- a/lib/wx/c_src/gen/wxe_events.cpp +++ b/lib/wx/c_src/gen/wxe_events.cpp @@ -897,6 +897,11 @@ case 235: {// wxMouseCaptureLostEvent } else { send_res = rt.send(); if(cb->skip) event->Skip(); + if(app->recurse_level < 1) { + app->recurse_level++; + app->dispatch_cmds(); + app->recurse_level--; + } }; return send_res; } diff --git a/lib/wx/c_src/gen/wxe_funcs.cpp b/lib/wx/c_src/gen/wxe_funcs.cpp index 3211664499..4025fb1c16 100644 --- a/lib/wx/c_src/gen/wxe_funcs.cpp +++ b/lib/wx/c_src/gen/wxe_funcs.cpp @@ -113,11 +113,15 @@ case 101: { // wxEvtHandler::Disconnect int eventType = wxeEventTypeFromAtom(bp); bp += *eventTypeLen; if(eventType > 0) { + if(recurse_level > 1) { + delayed_delete->Append(Ecmd.Save()); + } else { bool Result = This->Disconnect((int) *winid,(int) *lastId,eventType, (wxObjectEventFunction)(wxEventFunction) &wxeEvtListener::forward, NULL, Listener); rt.addBool(Result); + } } else { rt.addAtom("badarg"); rt.addAtom("event_type"); @@ -5856,6 +5860,7 @@ case wxScreenDC_new: { // wxScreenDC::wxScreenDC rt.addRef(getRef((void *)Result,memenv), "wxScreenDC"); break; } +#if wxUSE_POSTSCRIPT case wxPostScriptDC_new_0: { // wxPostScriptDC::wxPostScriptDC wxPostScriptDC * Result = new EwxPostScriptDC(); newPtr((void *) Result, 4, memenv); @@ -5883,6 +5888,7 @@ case wxPostScriptDC_GetResolution: { // wxPostScriptDC::GetResolution break; } #endif +#endif // wxUSE_POSTSCRIPT #if !wxCHECK_VERSION(2,9,0) case wxWindowDC_new_0: { // wxWindowDC::wxWindowDC wxWindowDC * Result = new EwxWindowDC(); @@ -31959,6 +31965,56 @@ case wxPopupTransientWindow_Dismiss: { // wxPopupTransientWindow::Dismiss break; } #endif // wxUSE_POPUPWIN +case wxOverlay_new: { // wxOverlay::wxOverlay + wxOverlay * Result = new wxOverlay(); + newPtr((void *) Result, 236, memenv); + rt.addRef(getRef((void *)Result,memenv), "wxOverlay"); + break; +} +case wxOverlay_destruct: { // wxOverlay::~wxOverlay + wxOverlay *This = (wxOverlay *) getPtr(bp,memenv); bp += 4; + if(This) { ((WxeApp *) wxTheApp)->clearPtr((void *) This); + delete This;} + break; +} +case wxOverlay_Reset: { // wxOverlay::Reset + wxOverlay *This = (wxOverlay *) getPtr(bp,memenv); bp += 4; + if(!This) throw wxe_badarg(0); + This->Reset(); + break; +} +case wxDCOverlay_new_6: { // wxDCOverlay::wxDCOverlay + wxOverlay *overlay = (wxOverlay *) getPtr(bp,memenv); bp += 4; + wxWindowDC *dc = (wxWindowDC *) getPtr(bp,memenv); bp += 4; + int * x = (int *) bp; bp += 4; + int * y = (int *) bp; bp += 4; + int * width = (int *) bp; bp += 4; + int * height = (int *) bp; bp += 4; + wxDCOverlay * Result = new EwxDCOverlay(*overlay,dc,*x,*y,*width,*height); + newPtr((void *) Result, 237, memenv); + rt.addRef(getRef((void *)Result,memenv), "wxDCOverlay"); + break; +} +case wxDCOverlay_new_2: { // wxDCOverlay::wxDCOverlay + wxOverlay *overlay = (wxOverlay *) getPtr(bp,memenv); bp += 4; + wxWindowDC *dc = (wxWindowDC *) getPtr(bp,memenv); bp += 4; + wxDCOverlay * Result = new EwxDCOverlay(*overlay,dc); + newPtr((void *) Result, 237, memenv); + rt.addRef(getRef((void *)Result,memenv), "wxDCOverlay"); + break; +} +case wxDCOverlay_destruct: { // wxDCOverlay::~wxDCOverlay + wxDCOverlay *This = (wxDCOverlay *) getPtr(bp,memenv); bp += 4; + if(This) { ((WxeApp *) wxTheApp)->clearPtr((void *) This); + delete This;} + break; +} +case wxDCOverlay_Clear: { // wxDCOverlay::Clear + wxDCOverlay *This = (wxDCOverlay *) getPtr(bp,memenv); bp += 4; + if(!This) throw wxe_badarg(0); + This->Clear(); + break; +} default: { wxeReturn error = wxeReturn(WXE_DRV_PORT, Ecmd.caller, false); error.addAtom("_wxe_error_"); error.addInt((int) Ecmd.op); @@ -32005,6 +32061,8 @@ bool WxeApp::delete_object(void *ptr, wxeRefData *refd) { case 215: /* delete (wxBitmapDataObject *) ptr;These objects must be deleted by owner object */ break; case 227: delete (wxLogNull *) ptr; break; case 231: delete (EwxLocale *) ptr; return false; + case 236: delete (wxOverlay *) ptr; break; + case 237: delete (EwxDCOverlay *) ptr; return false; default: delete (wxObject *) ptr; return false; } return true; diff --git a/lib/wx/c_src/gen/wxe_macros.h b/lib/wx/c_src/gen/wxe_macros.h index 59f9c7054e..5f51ac5f91 100644 --- a/lib/wx/c_src/gen/wxe_macros.h +++ b/lib/wx/c_src/gen/wxe_macros.h @@ -64,6 +64,7 @@ #include <wx/html/htmlcell.h> #include <wx/filename.h> #include <wx/sysopt.h> +#include <wx/overlay.h> #ifndef wxICON_DEFAULT_BITMAP_TYPE @@ -3411,5 +3412,12 @@ #define wxPopupTransientWindow_destruct 3583 #define wxPopupTransientWindow_Popup 3584 #define wxPopupTransientWindow_Dismiss 3585 +#define wxOverlay_new 3586 +#define wxOverlay_destruct 3587 +#define wxOverlay_Reset 3588 +#define wxDCOverlay_new_6 3589 +#define wxDCOverlay_new_2 3590 +#define wxDCOverlay_destruct 3591 +#define wxDCOverlay_Clear 3592 diff --git a/lib/wx/c_src/wxe_helpers.cpp b/lib/wx/c_src/wxe_helpers.cpp index cc57374d5b..1696b8bd50 100644 --- a/lib/wx/c_src/wxe_helpers.cpp +++ b/lib/wx/c_src/wxe_helpers.cpp @@ -61,6 +61,7 @@ wxeFifo::wxeFifo(unsigned int sz) m_max = sz; m_n = 0; m_first = 0; + cb_start = 0; m_old = NULL; for(unsigned int i = 0; i < sz; i++) { m_q[i].buffer = NULL; @@ -76,15 +77,30 @@ wxeFifo::~wxeFifo() { wxeCommand * wxeFifo::Get() { unsigned int pos; - if(m_n > 0) { + do { + if(m_n <= 0) + return NULL; + pos = m_first++; m_n--; m_first %= m_max; - return &m_q[pos]; - } - return NULL; + } while(m_q[pos].op == -1); + return &m_q[pos]; +} + +wxeCommand * wxeFifo::Peek(unsigned int *i) +{ + unsigned int pos; + do { + if(*i >= m_n || m_n <= 0) + return NULL; + pos = (m_first+*i) % m_max; + (*i)++; + } while(m_q[pos].op == -1); + return &m_q[pos]; } + void wxeFifo::Add(int fc, char * cbuf,int buflen, wxe_data *sd) { unsigned int pos; @@ -140,10 +156,12 @@ void wxeFifo::Append(wxeCommand *orig) pos = (m_first + m_n) % m_max; m_n++; + curr = &m_q[pos]; + curr->op = orig->op; + if(curr->op == -1) return; curr->caller = orig->caller; curr->port = orig->port; - curr->op = orig->op; curr->len = orig->len; curr->bin[0] = orig->bin[0]; curr->bin[1] = orig->bin[1]; @@ -171,17 +189,19 @@ void wxeFifo::Realloc() wxeCommand * old = m_q; wxeCommand * queue = (wxeCommand *)driver_alloc(new_sz*sizeof(wxeCommand)); + // fprintf(stderr, "\r\nrealloc qsz %d\r\n", new_sz);fflush(stderr); + m_max=new_sz; m_first = 0; m_n=0; m_q = queue; for(i=0; i < n; i++) { - unsigned int pos = i+first; - if(old[pos%max].op >= 0) { - Append(&old[pos%max]); - } + unsigned int pos = (i+first)%max; + if(old[pos].op >= 0) + Append(&old[pos]); } + for(i = m_n; i < new_sz; i++) { // Reset the rest m_q[i].buffer = NULL; m_q[i].op = -1; @@ -190,6 +210,26 @@ void wxeFifo::Realloc() m_old = old; } +// Strip end of queue if ops are already taken care of, avoids reallocs +void wxeFifo::Strip() +{ + while((m_n > 0) && (m_q[(m_first + m_n - 1)%m_max].op == -1)) { + m_n--; + } +} + +unsigned int wxeFifo::Cleanup(unsigned int def) +{ + if(m_old) { + driver_free(m_old); + m_old = NULL; + // Realloced we need to start from the beginning + return 0; + } else { + return def; + } +} + /* **************************************************************************** * TreeItemData * ****************************************************************************/ diff --git a/lib/wx/c_src/wxe_helpers.h b/lib/wx/c_src/wxe_helpers.h index 4f9d9ca9c3..ff949e332b 100644 --- a/lib/wx/c_src/wxe_helpers.h +++ b/lib/wx/c_src/wxe_helpers.h @@ -67,9 +67,13 @@ class wxeFifo { void Append(wxeCommand *Other); wxeCommand * Get(); + wxeCommand * Peek(unsigned int *item); void Realloc(); + void Strip(); + unsigned int Cleanup(unsigned int peek=0); + unsigned int cb_start; unsigned int m_max; unsigned int m_first; unsigned int m_n; diff --git a/lib/wx/c_src/wxe_impl.cpp b/lib/wx/c_src/wxe_impl.cpp index 5b34f08704..c5bad41573 100644 --- a/lib/wx/c_src/wxe_impl.cpp +++ b/lib/wx/c_src/wxe_impl.cpp @@ -57,10 +57,8 @@ extern ErlDrvTermData init_caller; extern int wxe_status; wxeFifo * wxe_queue = NULL; -wxeFifo * wxe_queue_cb_saved = NULL; unsigned int wxe_needs_signal = 0; // inside batch if larger than 0 -unsigned int wxe_cb_invoked = 0; /* ************************************************************ * Commands from erlang @@ -123,11 +121,10 @@ bool WxeApp::OnInit() { global_me = new wxeMemEnv(); - wxe_queue = new wxeFifo(1000); - wxe_queue_cb_saved = new wxeFifo(200); + wxe_queue = new wxeFifo(2000); cb_buff = NULL; recurse_level = 0; - delayed_delete = new wxeFifo(10); + delayed_delete = new wxeFifo(100); delayed_cleanup = new wxList; wxe_ps_init2(); @@ -173,7 +170,6 @@ void WxeApp::shutdown(wxeMetaCommand& Ecmd) { wxe_status = WXE_EXITING; ExitMainLoop(); delete wxe_queue; - delete wxe_queue_cb_saved; } void WxeApp::dummy_close(wxEvent& Ev) { @@ -229,12 +225,11 @@ void handle_event_callback(ErlDrvPort port, ErlDrvTermData process) if(driver_monitor_process(port, process, &monitor) == 0) { // Should we be able to handle commands when recursing? probably // fprintf(stderr, "\r\nCB EV Start %lu \r\n", process);fflush(stderr); - app->recurse_level++; - app->dispatch_cb(wxe_queue, wxe_queue_cb_saved, process); - app->recurse_level--; + app->recurse_level += 2; + app->dispatch_cb(wxe_queue, process); + app->recurse_level -= 2; // fprintf(stderr, "CB EV done %lu \r\n", process);fflush(stderr); driver_demonitor_process(port, &monitor); - wxe_cb_invoked = 1; } } @@ -242,16 +237,11 @@ void WxeApp::dispatch_cmds() { if(wxe_status != WXE_INITIATED) return; - do { - wxe_cb_invoked = 0; - recurse_level++; - // fprintf(stderr, "\r\ndispatch_saved 0 \r\n");fflush(stderr); - int level = dispatch(wxe_queue_cb_saved, 0, WXE_STORED); - // fprintf(stderr, "\r\ndispatch_normal %d\r\n", level);fflush(stderr); - dispatch(wxe_queue, level, WXE_NORMAL); - // fprintf(stderr, "\r\ndispatch_done \r\n");fflush(stderr); - recurse_level--; - } while(wxe_cb_invoked); + recurse_level++; + // fprintf(stderr, "\r\ndispatch_normal %d\r\n", level);fflush(stderr); + dispatch(wxe_queue); + // fprintf(stderr, "\r\ndispatch_done \r\n");fflush(stderr); + recurse_level--; // Cleanup old memenv's and deleted objects if(recurse_level == 0) { @@ -260,6 +250,7 @@ void WxeApp::dispatch_cmds() wxe_dispatch(*curr); curr->Delete(); } + delayed_delete->Cleanup(); if(delayed_cleanup->size() > 0) for( wxList::compatibility_iterator node = delayed_cleanup->GetFirst(); node; @@ -269,28 +260,19 @@ void WxeApp::dispatch_cmds() destroyMemEnv(*event); delete event; } - if(wxe_queue_cb_saved->m_old) { - driver_free(wxe_queue_cb_saved->m_old); - wxe_queue_cb_saved->m_old = NULL; - } - if(delayed_delete->m_old) { - driver_free(delayed_delete->m_old); - delayed_delete->m_old = NULL; - } } } -int WxeApp::dispatch(wxeFifo * batch, int blevel, int list_type) +int WxeApp::dispatch(wxeFifo * batch) { int ping = 0; + int blevel = 0; wxeCommand *event; - if(list_type == WXE_NORMAL) erl_drv_mutex_lock(wxe_batch_locker_m); + erl_drv_mutex_lock(wxe_batch_locker_m); while(true) { while((event = batch->Get()) != NULL) { - if(list_type == WXE_NORMAL) erl_drv_mutex_unlock(wxe_batch_locker_m); + erl_drv_mutex_unlock(wxe_batch_locker_m); switch(event->op) { - case -1: - break; case WXE_BATCH_END: {--blevel; } break; @@ -321,20 +303,10 @@ int WxeApp::dispatch(wxeFifo * batch, int blevel, int list_type) break; } event->Delete(); - if(list_type == WXE_NORMAL) { - if(wxe_cb_invoked) - return blevel; - else - erl_drv_mutex_lock(wxe_batch_locker_m); - } + erl_drv_mutex_lock(wxe_batch_locker_m); + batch->Cleanup(); } - if(list_type == WXE_STORED) - return blevel; - if(blevel <= 0) { // list_type == WXE_NORMAL - if(wxe_queue->m_old) { - driver_free(wxe_queue->m_old); - wxe_queue->m_old = NULL; - } + if(blevel <= 0) { erl_drv_mutex_unlock(wxe_batch_locker_m); return blevel; } @@ -348,12 +320,13 @@ int WxeApp::dispatch(wxeFifo * batch, int blevel, int list_type) } } -void WxeApp::dispatch_cb(wxeFifo * batch, wxeFifo * temp, ErlDrvTermData process) { +void WxeApp::dispatch_cb(wxeFifo * batch, ErlDrvTermData process) { wxeCommand *event; + unsigned int peek; erl_drv_mutex_lock(wxe_batch_locker_m); + peek = batch->Cleanup(batch->cb_start); while(true) { - while((event = batch->Get()) != NULL) { - erl_drv_mutex_unlock(wxe_batch_locker_m); + while((event = batch->Peek(&peek)) != NULL) { wxeMemEnv *memenv = getMemEnv(event->port); // fprintf(stderr, " Ev %d %lu\r\n", event->op, event->caller); if(event->caller == process || // Callbacks from CB process only @@ -361,8 +334,8 @@ void WxeApp::dispatch_cb(wxeFifo * batch, wxeFifo * temp, ErlDrvTermData process event->op == WXE_CB_DIED || // Event callback process died // Allow connect_cb during CB i.e. msg from wxe_server. (memenv && event->caller == memenv->owner)) { + erl_drv_mutex_unlock(wxe_batch_locker_m); switch(event->op) { - case -1: case WXE_BATCH_END: case WXE_BATCH_BEGIN: case WXE_DEBUG_PING: @@ -373,52 +346,42 @@ void WxeApp::dispatch_cb(wxeFifo * batch, wxeFifo * temp, ErlDrvTermData process memcpy(cb_buff, event->buffer, event->len); } // continue case WXE_CB_DIED: + batch->cb_start = 0; event->Delete(); + erl_drv_mutex_lock(wxe_batch_locker_m); + batch->Strip(); + erl_drv_mutex_unlock(wxe_batch_locker_m); return; case WXE_CB_START: // CB start from now accept message from CB process only process = event->caller; break; default: - size_t start=temp->m_n; + batch->cb_start = peek; // In case of recursive callbacks if(event->op < OPENGL_START) { - // fprintf(stderr, " cb %d \r\n", event->op); wxe_dispatch(*event); } else { gl_dispatch(event->op,event->buffer,event->caller,event->bin); } - if(temp->m_n > start) { - erl_drv_mutex_lock(wxe_batch_locker_m); - // We have recursed dispatch_cb and messages for this - // callback may be saved on temp list move them - // to orig list - for(unsigned int i=start; i < temp->m_n; i++) { - wxeCommand *ev = &temp->m_q[(temp->m_first+i) % temp->m_max]; - if(ev->caller == process) { - batch->Append(ev); - } - } - erl_drv_mutex_unlock(wxe_batch_locker_m); - } break; } event->Delete(); - } else { - // fprintf(stderr, " save %d %lu\r\n", event->op, event->caller); - temp->Append(event); + erl_drv_mutex_lock(wxe_batch_locker_m); + peek = batch->Cleanup(peek); } - erl_drv_mutex_lock(wxe_batch_locker_m); } // sleep until something happens // fprintf(stderr, "%s:%d sleep %d %d\r\n", __FILE__, __LINE__, - // batch->m_n, temp->m_n);fflush(stderr); + // peek, batch->m_n);fflush(stderr); wxe_needs_signal = 1; - while(batch->m_n == 0) { + while(peek >= batch->m_n) { erl_drv_cond_wait(wxe_batch_locker_c, wxe_batch_locker_m); + peek = batch->Cleanup(peek); } wxe_needs_signal = 0; } } + /* Memory handling */ void WxeApp::newMemEnv(wxeMetaCommand& Ecmd) { diff --git a/lib/wx/c_src/wxe_impl.h b/lib/wx/c_src/wxe_impl.h index d6d3095a0f..fd25296c73 100644 --- a/lib/wx/c_src/wxe_impl.h +++ b/lib/wx/c_src/wxe_impl.h @@ -67,8 +67,8 @@ public: void shutdown(wxeMetaCommand& event); - int dispatch(wxeFifo *, int, int); - void dispatch_cb(wxeFifo * batch, wxeFifo * temp, ErlDrvTermData process); + int dispatch(wxeFifo *); + void dispatch_cb(wxeFifo * batch, ErlDrvTermData process); void wxe_dispatch(wxeCommand& event); diff --git a/lib/wx/examples/demo/ex_canvas.erl b/lib/wx/examples/demo/ex_canvas.erl index 1cbb96de1f..cdc783055c 100644 --- a/lib/wx/examples/demo/ex_canvas.erl +++ b/lib/wx/examples/demo/ex_canvas.erl @@ -35,7 +35,9 @@ parent, config, canvas, - bitmap + bitmap, + overlay, + pos }). start(Config) -> @@ -60,6 +62,10 @@ do_init(Config) -> wxPanel:connect(Canvas, paint, [callback]), wxPanel:connect(Canvas, size), + wxPanel:connect(Canvas, left_down), + wxPanel:connect(Canvas, left_up), + wxPanel:connect(Canvas, motion), + wxPanel:connect(Button, command_button_clicked), %% Add to sizers @@ -78,7 +84,9 @@ do_init(Config) -> Bitmap = wxBitmap:new(erlang:max(W,30),erlang:max(30,H)), {Panel, #state{parent=Panel, config=Config, - canvas = Canvas, bitmap = Bitmap}}. + canvas = Canvas, bitmap = Bitmap, + overlay = wxOverlay:new() + }}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Sync event from callback events, paint event must be handled in callbacks @@ -127,11 +135,39 @@ handle_event(#wx{event = #wxCommand{type = command_button_clicked}}, wxBitmap:destroy(Bmp), {noreply, State}; handle_event(#wx{event = #wxSize{size={W,H}}}, - State = #state{bitmap=Prev}) -> + State = #state{bitmap=Prev, canvas=Canvas}) -> Bitmap = wxBitmap:new(W,H), - draw(State#state.canvas, Bitmap, fun(DC) -> wxDC:clear(DC) end), + draw(Canvas, Bitmap, fun(DC) -> wxDC:clear(DC) end), wxBitmap:destroy(Prev), {noreply, State#state{bitmap = Bitmap}}; + +handle_event(#wx{event = #wxMouse{type=left_down, x=X, y=Y}}, State) -> + {noreply, State#state{pos={X,Y}}}; +handle_event(#wx{event = #wxMouse{type=motion, x=X1, y=Y1}}, + #state{pos=Start, overlay=Overlay, canvas=Canvas} = State) -> + case Start of + undefined -> ignore; + {X0,Y0} -> + DC = wxClientDC:new(Canvas), + DCO = wxDCOverlay:new(Overlay, DC), + wxDCOverlay:clear(DCO), + wxDC:setPen(DC, ?wxLIGHT_GREY_PEN), + wxDC:setBrush(DC, ?wxTRANSPARENT_BRUSH), + wxDC:drawRectangle(DC, {X0,Y0, X1-X0, Y1-Y0}), + wxDCOverlay:destroy(DCO), + wxClientDC:destroy(DC) + end, + {noreply, State}; +handle_event(#wx{event = #wxMouse{type=left_up}}, + #state{overlay=Overlay, canvas=Canvas} = State) -> + DC = wxClientDC:new(Canvas), + DCO = wxDCOverlay:new(Overlay, DC), + wxDCOverlay:clear(DCO), + wxDCOverlay:destroy(DCO), + wxClientDC:destroy(DC), + wxOverlay:reset(Overlay), + {noreply, State#state{pos=undefined}}; + handle_event(Ev = #wx{}, State = #state{}) -> demo:format(State#state.config, "Got Event ~p\n", [Ev]), {noreply, State}. @@ -155,7 +191,8 @@ handle_cast(Msg, State) -> code_change(_, _, State) -> {stop, ignore, State}. -terminate(_Reason, _) -> +terminate(_Reason, #state{overlay=Overlay}) -> + wxOverlay:destroy(Overlay), ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/lib/wx/src/gen/gl.erl b/lib/wx/src/gen/gl.erl index 58cdb59aa2..bedd4e9cca 100644 --- a/lib/wx/src/gen/gl.erl +++ b/lib/wx/src/gen/gl.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2013. All Rights Reserved. +%% Copyright Ericsson AB 2008-2015. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -53,10 +53,14 @@ -type enum() :: non_neg_integer(). %% See wx/include/gl.hrl -type clamp() :: float(). %% 0.0..1.0 -type offset() :: non_neg_integer(). %% Offset in memory block --type matrix() :: {float(),float(),float(),float(), +-type matrix12() :: {float(),float(),float(),float(), + float(),float(),float(),float(), + float(),float(),float(),float()}. +-type matrix16() :: {float(),float(),float(),float(), float(),float(),float(),float(), float(),float(),float(),float(), float(),float(),float(),float()}. +-type matrix() :: matrix12() | matrix16(). -type mem() :: binary() | tuple(). %% Memory block -export([clearIndex/1,clearColor/4,clear/1,indexMask/1,colorMask/4,alphaFunc/2, @@ -4289,14 +4293,14 @@ lighti(Light,Pname,Param) -> %% @doc %% See {@link lightf/3} --spec lightfv(Light, Pname, Params) -> ok when Light :: enum(),Pname :: enum(),Params :: {float()}. +-spec lightfv(Light, Pname, Params) -> ok when Light :: enum(),Pname :: enum(),Params :: tuple(). lightfv(Light,Pname,Params) -> cast(5207, <<Light:?GLenum,Pname:?GLenum,(size(Params)):?GLuint, (<< <<C:?GLfloat>> ||C <- tuple_to_list(Params)>>)/binary,0:(((1+size(Params)) rem 2)*32)>>). %% @doc %% See {@link lightf/3} --spec lightiv(Light, Pname, Params) -> ok when Light :: enum(),Pname :: enum(),Params :: {integer()}. +-spec lightiv(Light, Pname, Params) -> ok when Light :: enum(),Pname :: enum(),Params :: tuple(). lightiv(Light,Pname,Params) -> cast(5208, <<Light:?GLenum,Pname:?GLenum,(size(Params)):?GLuint, (<< <<C:?GLint>> ||C <- tuple_to_list(Params)>>)/binary,0:(((1+size(Params)) rem 2)*32)>>). @@ -4460,14 +4464,14 @@ lightModeli(Pname,Param) -> %% @doc %% See {@link lightModelf/2} --spec lightModelfv(Pname, Params) -> ok when Pname :: enum(),Params :: {float()}. +-spec lightModelfv(Pname, Params) -> ok when Pname :: enum(),Params :: tuple(). lightModelfv(Pname,Params) -> cast(5213, <<Pname:?GLenum,(size(Params)):?GLuint, (<< <<C:?GLfloat>> ||C <- tuple_to_list(Params)>>)/binary,0:(((0+size(Params)) rem 2)*32)>>). %% @doc %% See {@link lightModelf/2} --spec lightModeliv(Pname, Params) -> ok when Pname :: enum(),Params :: {integer()}. +-spec lightModeliv(Pname, Params) -> ok when Pname :: enum(),Params :: tuple(). lightModeliv(Pname,Params) -> cast(5214, <<Pname:?GLenum,(size(Params)):?GLuint, (<< <<C:?GLint>> ||C <- tuple_to_list(Params)>>)/binary,0:(((0+size(Params)) rem 2)*32)>>). @@ -4547,14 +4551,14 @@ materiali(Face,Pname,Param) -> %% @doc %% See {@link materialf/3} --spec materialfv(Face, Pname, Params) -> ok when Face :: enum(),Pname :: enum(),Params :: {float()}. +-spec materialfv(Face, Pname, Params) -> ok when Face :: enum(),Pname :: enum(),Params :: tuple(). materialfv(Face,Pname,Params) -> cast(5217, <<Face:?GLenum,Pname:?GLenum,(size(Params)):?GLuint, (<< <<C:?GLfloat>> ||C <- tuple_to_list(Params)>>)/binary,0:(((1+size(Params)) rem 2)*32)>>). %% @doc %% See {@link materialf/3} --spec materialiv(Face, Pname, Params) -> ok when Face :: enum(),Pname :: enum(),Params :: {integer()}. +-spec materialiv(Face, Pname, Params) -> ok when Face :: enum(),Pname :: enum(),Params :: tuple(). materialiv(Face,Pname,Params) -> cast(5218, <<Face:?GLenum,Pname:?GLenum,(size(Params)):?GLuint, (<< <<C:?GLint>> ||C <- tuple_to_list(Params)>>)/binary,0:(((1+size(Params)) rem 2)*32)>>). @@ -5890,21 +5894,21 @@ texGeni(Coord,Pname,Param) -> %% @doc %% See {@link texGend/3} --spec texGendv(Coord, Pname, Params) -> ok when Coord :: enum(),Pname :: enum(),Params :: {float()}. +-spec texGendv(Coord, Pname, Params) -> ok when Coord :: enum(),Pname :: enum(),Params :: tuple(). texGendv(Coord,Pname,Params) -> cast(5246, <<Coord:?GLenum,Pname:?GLenum,(size(Params)):?GLuint,0:32, (<< <<C:?GLdouble>> ||C <- tuple_to_list(Params)>>)/binary>>). %% @doc %% See {@link texGend/3} --spec texGenfv(Coord, Pname, Params) -> ok when Coord :: enum(),Pname :: enum(),Params :: {float()}. +-spec texGenfv(Coord, Pname, Params) -> ok when Coord :: enum(),Pname :: enum(),Params :: tuple(). texGenfv(Coord,Pname,Params) -> cast(5247, <<Coord:?GLenum,Pname:?GLenum,(size(Params)):?GLuint, (<< <<C:?GLfloat>> ||C <- tuple_to_list(Params)>>)/binary,0:(((1+size(Params)) rem 2)*32)>>). %% @doc %% See {@link texGend/3} --spec texGeniv(Coord, Pname, Params) -> ok when Coord :: enum(),Pname :: enum(),Params :: {integer()}. +-spec texGeniv(Coord, Pname, Params) -> ok when Coord :: enum(),Pname :: enum(),Params :: tuple(). texGeniv(Coord,Pname,Params) -> cast(5248, <<Coord:?GLenum,Pname:?GLenum,(size(Params)):?GLuint, (<< <<C:?GLint>> ||C <- tuple_to_list(Params)>>)/binary,0:(((1+size(Params)) rem 2)*32)>>). @@ -6123,14 +6127,14 @@ texEnvi(Target,Pname,Param) -> %% replacement. The default value is `?GL_FALSE'. %% %% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexEnv.xml">external</a> documentation. --spec texEnvfv(Target, Pname, Params) -> ok when Target :: enum(),Pname :: enum(),Params :: {float()}. +-spec texEnvfv(Target, Pname, Params) -> ok when Target :: enum(),Pname :: enum(),Params :: tuple(). texEnvfv(Target,Pname,Params) -> cast(5254, <<Target:?GLenum,Pname:?GLenum,(size(Params)):?GLuint, (<< <<C:?GLfloat>> ||C <- tuple_to_list(Params)>>)/binary,0:(((1+size(Params)) rem 2)*32)>>). %% @doc %% See {@link texEnvfv/3} --spec texEnviv(Target, Pname, Params) -> ok when Target :: enum(),Pname :: enum(),Params :: {integer()}. +-spec texEnviv(Target, Pname, Params) -> ok when Target :: enum(),Pname :: enum(),Params :: tuple(). texEnviv(Target,Pname,Params) -> cast(5255, <<Target:?GLenum,Pname:?GLenum,(size(Params)):?GLuint, (<< <<C:?GLint>> ||C <- tuple_to_list(Params)>>)/binary,0:(((1+size(Params)) rem 2)*32)>>). @@ -6455,14 +6459,14 @@ texParameteri(Target,Pname,Param) -> %% @doc %% See {@link texParameterf/3} --spec texParameterfv(Target, Pname, Params) -> ok when Target :: enum(),Pname :: enum(),Params :: {float()}. +-spec texParameterfv(Target, Pname, Params) -> ok when Target :: enum(),Pname :: enum(),Params :: tuple(). texParameterfv(Target,Pname,Params) -> cast(5260, <<Target:?GLenum,Pname:?GLenum,(size(Params)):?GLuint, (<< <<C:?GLfloat>> ||C <- tuple_to_list(Params)>>)/binary,0:(((1+size(Params)) rem 2)*32)>>). %% @doc %% See {@link texParameterf/3} --spec texParameteriv(Target, Pname, Params) -> ok when Target :: enum(),Pname :: enum(),Params :: {integer()}. +-spec texParameteriv(Target, Pname, Params) -> ok when Target :: enum(),Pname :: enum(),Params :: tuple(). texParameteriv(Target,Pname,Params) -> cast(5261, <<Target:?GLenum,Pname:?GLenum,(size(Params)):?GLuint, (<< <<C:?GLint>> ||C <- tuple_to_list(Params)>>)/binary,0:(((1+size(Params)) rem 2)*32)>>). @@ -7609,14 +7613,14 @@ fogi(Pname,Param) -> %% @doc %% See {@link fogf/2} --spec fogfv(Pname, Params) -> ok when Pname :: enum(),Params :: {float()}. +-spec fogfv(Pname, Params) -> ok when Pname :: enum(),Params :: tuple(). fogfv(Pname,Params) -> cast(5306, <<Pname:?GLenum,(size(Params)):?GLuint, (<< <<C:?GLfloat>> ||C <- tuple_to_list(Params)>>)/binary,0:(((0+size(Params)) rem 2)*32)>>). %% @doc %% See {@link fogf/2} --spec fogiv(Pname, Params) -> ok when Pname :: enum(),Params :: {integer()}. +-spec fogiv(Pname, Params) -> ok when Pname :: enum(),Params :: tuple(). fogiv(Pname,Params) -> cast(5307, <<Pname:?GLenum,(size(Params)):?GLuint, (<< <<C:?GLint>> ||C <- tuple_to_list(Params)>>)/binary,0:(((0+size(Params)) rem 2)*32)>>). @@ -8522,24 +8526,24 @@ convolutionFilter2D(Target,Internalformat,Width,Height,Format,Type,Image) -> %% image were replicated. %% %% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glConvolutionParameter.xml">external</a> documentation. --spec convolutionParameterf(Target, Pname, Params) -> ok when Target :: enum(),Pname :: enum(),Params :: {float()}. +-spec convolutionParameterf(Target, Pname, Params) -> ok when Target :: enum(),Pname :: enum(),Params :: tuple(). convolutionParameterf(Target,Pname,Params) -> cast(5339, <<Target:?GLenum,Pname:?GLenum,(size(Params)):?GLuint, (<< <<C:?GLfloat>> ||C <- tuple_to_list(Params)>>)/binary,0:(((1+size(Params)) rem 2)*32)>>). %% @equiv convolutionParameterf(Target,Pname,Params) --spec convolutionParameterfv(Target :: enum(),Pname :: enum(),Params) -> ok when Params :: {Params :: {float()}}. +-spec convolutionParameterfv(Target :: enum(),Pname :: enum(),Params) -> ok when Params :: {Params :: tuple()}. convolutionParameterfv(Target,Pname,{Params}) -> convolutionParameterf(Target,Pname,Params). %% @doc %% See {@link convolutionParameterf/3} --spec convolutionParameteri(Target, Pname, Params) -> ok when Target :: enum(),Pname :: enum(),Params :: {integer()}. +-spec convolutionParameteri(Target, Pname, Params) -> ok when Target :: enum(),Pname :: enum(),Params :: tuple(). convolutionParameteri(Target,Pname,Params) -> cast(5340, <<Target:?GLenum,Pname:?GLenum,(size(Params)):?GLuint, (<< <<C:?GLint>> ||C <- tuple_to_list(Params)>>)/binary,0:(((1+size(Params)) rem 2)*32)>>). %% @equiv convolutionParameteri(Target,Pname,Params) --spec convolutionParameteriv(Target :: enum(),Pname :: enum(),Params) -> ok when Params :: {Params :: {integer()}}. +-spec convolutionParameteriv(Target :: enum(),Pname :: enum(),Params) -> ok when Params :: {Params :: tuple()}. convolutionParameteriv(Target,Pname,{Params}) -> convolutionParameteri(Target,Pname,Params). %% @doc Copy pixels into a one-dimensional convolution filter @@ -9671,7 +9675,7 @@ pointParameterf(Pname,Param) -> %% @doc %% See {@link pointParameterf/2} --spec pointParameterfv(Pname, Params) -> ok when Pname :: enum(),Params :: {float()}. +-spec pointParameterfv(Pname, Params) -> ok when Pname :: enum(),Params :: tuple(). pointParameterfv(Pname,Params) -> cast(5397, <<Pname:?GLenum,(size(Params)):?GLuint, (<< <<C:?GLfloat>> ||C <- tuple_to_list(Params)>>)/binary,0:(((0+size(Params)) rem 2)*32)>>). @@ -9684,7 +9688,7 @@ pointParameteri(Pname,Param) -> %% @doc %% See {@link pointParameterf/2} --spec pointParameteriv(Pname, Params) -> ok when Pname :: enum(),Params :: {integer()}. +-spec pointParameteriv(Pname, Params) -> ok when Pname :: enum(),Params :: tuple(). pointParameteriv(Pname,Params) -> cast(5399, <<Pname:?GLenum,(size(Params)):?GLuint, (<< <<C:?GLint>> ||C <- tuple_to_list(Params)>>)/binary,0:(((0+size(Params)) rem 2)*32)>>). @@ -11529,7 +11533,7 @@ linkProgram(Program) -> %% scanned or parsed at this time; they are simply copied into the specified shader object. %% %% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glShaderSource.xml">external</a> documentation. --spec shaderSource(Shader, String) -> ok when Shader :: integer(),String :: [string()]. +-spec shaderSource(Shader, String) -> ok when Shader :: integer(),String :: iolist(). shaderSource(Shader,String) -> StringTemp = list_to_binary([[Str|[0]] || Str <- String ]), cast(5473, <<Shader:?GLuint,(length(String)):?GLuint,(size(StringTemp)):?GLuint,(StringTemp)/binary,0:((8-((size(StringTemp)+0) rem 8)) rem 8)>>). @@ -12278,7 +12282,7 @@ bindBufferBase(Target,Index,Buffer) -> %% and the buffer mode is `?GL_INTERLEAVED_ATTRIBS'. %% %% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTransformFeedbackVaryings.xml">external</a> documentation. --spec transformFeedbackVaryings(Program, Varyings, BufferMode) -> ok when Program :: integer(),Varyings :: [string()],BufferMode :: enum(). +-spec transformFeedbackVaryings(Program, Varyings, BufferMode) -> ok when Program :: integer(),Varyings :: iolist(),BufferMode :: enum(). transformFeedbackVaryings(Program,Varyings,BufferMode) -> VaryingsTemp = list_to_binary([[Str|[0]] || Str <- Varyings ]), cast(5536, <<Program:?GLuint,(length(Varyings)):?GLuint,(size(VaryingsTemp)):?GLuint,(VaryingsTemp)/binary,0:((8-((size(VaryingsTemp)+0) rem 8)) rem 8),BufferMode:?GLenum>>). @@ -12596,7 +12600,7 @@ uniform4uiv(Location,Value) -> %% @doc %% See {@link texParameterf/3} --spec texParameterIiv(Target, Pname, Params) -> ok when Target :: enum(),Pname :: enum(),Params :: {integer()}. +-spec texParameterIiv(Target, Pname, Params) -> ok when Target :: enum(),Pname :: enum(),Params :: tuple(). texParameterIiv(Target,Pname,Params) -> cast(5568, <<Target:?GLenum,Pname:?GLenum,(size(Params)):?GLuint, (<< <<C:?GLint>> ||C <- tuple_to_list(Params)>>)/binary,0:(((1+size(Params)) rem 2)*32)>>). @@ -12604,7 +12608,7 @@ texParameterIiv(Target,Pname,Params) -> %% @doc glTexParameterI %% %% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexParameterI.xml">external</a> documentation. --spec texParameterIuiv(Target, Pname, Params) -> ok when Target :: enum(),Pname :: enum(),Params :: {integer()}. +-spec texParameterIuiv(Target, Pname, Params) -> ok when Target :: enum(),Pname :: enum(),Params :: tuple(). texParameterIuiv(Target,Pname,Params) -> cast(5569, <<Target:?GLenum,Pname:?GLenum,(size(Params)):?GLuint, (<< <<C:?GLuint>> ||C <- tuple_to_list(Params)>>)/binary,0:(((1+size(Params)) rem 2)*32)>>). @@ -12651,21 +12655,21 @@ getTexParameterIuiv(Target,Pname) -> %% and the buffer being cleared is defined. However, this is not an error. %% %% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glClearBuffer.xml">external</a> documentation. --spec clearBufferiv(Buffer, Drawbuffer, Value) -> ok when Buffer :: enum(),Drawbuffer :: integer(),Value :: {integer()}. +-spec clearBufferiv(Buffer, Drawbuffer, Value) -> ok when Buffer :: enum(),Drawbuffer :: integer(),Value :: tuple(). clearBufferiv(Buffer,Drawbuffer,Value) -> cast(5572, <<Buffer:?GLenum,Drawbuffer:?GLint,(size(Value)):?GLuint, (<< <<C:?GLint>> ||C <- tuple_to_list(Value)>>)/binary,0:(((1+size(Value)) rem 2)*32)>>). %% @doc %% See {@link clearBufferiv/3} --spec clearBufferuiv(Buffer, Drawbuffer, Value) -> ok when Buffer :: enum(),Drawbuffer :: integer(),Value :: {integer()}. +-spec clearBufferuiv(Buffer, Drawbuffer, Value) -> ok when Buffer :: enum(),Drawbuffer :: integer(),Value :: tuple(). clearBufferuiv(Buffer,Drawbuffer,Value) -> cast(5573, <<Buffer:?GLenum,Drawbuffer:?GLint,(size(Value)):?GLuint, (<< <<C:?GLuint>> ||C <- tuple_to_list(Value)>>)/binary,0:(((1+size(Value)) rem 2)*32)>>). %% @doc %% See {@link clearBufferiv/3} --spec clearBufferfv(Buffer, Drawbuffer, Value) -> ok when Buffer :: enum(),Drawbuffer :: integer(),Value :: {float()}. +-spec clearBufferfv(Buffer, Drawbuffer, Value) -> ok when Buffer :: enum(),Drawbuffer :: integer(),Value :: tuple(). clearBufferfv(Buffer,Drawbuffer,Value) -> cast(5574, <<Buffer:?GLenum,Drawbuffer:?GLint,(size(Value)):?GLuint, (<< <<C:?GLfloat>> ||C <- tuple_to_list(Value)>>)/binary,0:(((1+size(Value)) rem 2)*32)>>). @@ -13219,7 +13223,7 @@ createShaderObjectARB(ShaderType) -> %% @doc glShaderSourceARB %% %% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glShaderSourceARB.xml">external</a> documentation. --spec shaderSourceARB(ShaderObj, String) -> ok when ShaderObj :: integer(),String :: [string()]. +-spec shaderSourceARB(ShaderObj, String) -> ok when ShaderObj :: integer(),String :: iolist(). shaderSourceARB(ShaderObj,String) -> StringTemp = list_to_binary([[Str|[0]] || Str <- String ]), cast(5630, <<ShaderObj:?GLhandleARB,(length(String)):?GLuint,(size(StringTemp)):?GLuint,(StringTemp)/binary,0:((8-((size(StringTemp)+4) rem 8)) rem 8)>>). @@ -13927,7 +13931,7 @@ isVertexArray(Array) -> %% If an error occurs, nothing is written to `UniformIndices' . %% %% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glGetUniformIndices.xml">external</a> documentation. --spec getUniformIndices(Program, UniformNames) -> [integer()] when Program :: integer(),UniformNames :: [string()]. +-spec getUniformIndices(Program, UniformNames) -> [integer()] when Program :: integer(),UniformNames :: iolist(). getUniformIndices(Program,UniformNames) -> UniformNamesTemp = list_to_binary([[Str|[0]] || Str <- UniformNames ]), call(5675, <<Program:?GLuint,(length(UniformNames)):?GLuint,(size(UniformNamesTemp)):?GLuint,(UniformNamesTemp)/binary,0:((8-((size(UniformNamesTemp)+0) rem 8)) rem 8)>>). @@ -14458,7 +14462,7 @@ deleteNamedStringARB(Name) -> %% @doc glCompileShaderIncludeARB %% %% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glCompileShaderIncludeARB.xml">external</a> documentation. --spec compileShaderIncludeARB(Shader, Path) -> ok when Shader :: integer(),Path :: [string()]. +-spec compileShaderIncludeARB(Shader, Path) -> ok when Shader :: integer(),Path :: iolist(). compileShaderIncludeARB(Shader,Path) -> PathTemp = list_to_binary([[Str|[0]] || Str <- Path ]), cast(5703, <<Shader:?GLuint,(length(Path)):?GLuint,(size(PathTemp)):?GLuint,(PathTemp)/binary,0:((8-((size(PathTemp)+0) rem 8)) rem 8)>>). @@ -15617,7 +15621,7 @@ activeShaderProgram(Pipeline,Program) -> %% @doc glCreateShaderProgramv %% %% See <a href="http://www.opengl.org/sdk/docs/man/xhtml/glCreateShaderProgramv.xml">external</a> documentation. --spec createShaderProgramv(Type, Strings) -> integer() when Type :: enum(),Strings :: [string()]. +-spec createShaderProgramv(Type, Strings) -> integer() when Type :: enum(),Strings :: iolist(). createShaderProgramv(Type,Strings) -> StringsTemp = list_to_binary([[Str|[0]] || Str <- Strings ]), call(5778, <<Type:?GLenum,(length(Strings)):?GLuint,(size(StringsTemp)):?GLuint,(StringsTemp)/binary,0:((8-((size(StringsTemp)+0) rem 8)) rem 8)>>). diff --git a/lib/wx/src/gen/glu.erl b/lib/wx/src/gen/glu.erl index 6a6e20b3e4..5faba48930 100644 --- a/lib/wx/src/gen/glu.erl +++ b/lib/wx/src/gen/glu.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2013. All Rights Reserved. +%% Copyright Ericsson AB 2008-2015. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -51,10 +51,14 @@ -define(GLint64,64/native-signed). -type vertex() :: {float(), float(), float()}. -type enum() :: non_neg_integer(). %% See wx/include/gl.hrl or glu.hrl --type matrix() :: {float(),float(),float(),float(), +-type matrix12() :: {float(),float(),float(),float(), + float(),float(),float(),float(), + float(),float(),float(),float()}. +-type matrix16() :: {float(),float(),float(),float(), float(),float(),float(),float(), float(),float(),float(),float(), float(),float(),float(),float()}. +-type matrix() :: matrix12() | matrix16(). -type mem() :: binary() | tuple(). %% Memory block -export([tesselate/2,build1DMipmapLevels/9,build1DMipmaps/6,build2DMipmapLevels/10, diff --git a/lib/wx/src/gen/wxDCOverlay.erl b/lib/wx/src/gen/wxDCOverlay.erl new file mode 100644 index 0000000000..f98e310ba6 --- /dev/null +++ b/lib/wx/src/gen/wxDCOverlay.erl @@ -0,0 +1,70 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2008-2015. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% This file is generated DO NOT EDIT + +%% @doc See external documentation: <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxdcoverlay.html">wxDCOverlay</a>. +%% @type wxDCOverlay(). An object reference, The representation is internal +%% and can be changed without notice. It can't be used for comparsion +%% stored on disc or distributed for use on other nodes. + +-module(wxDCOverlay). +-include("wxe.hrl"). +-export([clear/1,destroy/1,new/2,new/6]). + +%% inherited exports +-export([parent_class/1]). + +-export_type([wxDCOverlay/0]). +%% @hidden +parent_class(_Class) -> erlang:error({badtype, ?MODULE}). + +-type wxDCOverlay() :: wx:wx_object(). +%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxdcoverlay.html#wxdcoverlaywxdcoverlay">external documentation</a>. +-spec new(Overlay, Dc) -> wxDCOverlay() when + Overlay::wxOverlay:wxOverlay(), Dc::wxWindowDC:wxWindowDC(). +new(#wx_ref{type=OverlayT,ref=OverlayRef},#wx_ref{type=DcT,ref=DcRef}) -> + ?CLASS(OverlayT,wxOverlay), + ?CLASS(DcT,wxWindowDC), + wxe_util:construct(?wxDCOverlay_new_2, + <<OverlayRef:32/?UI,DcRef:32/?UI>>). + +%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxdcoverlay.html#wxdcoverlaywxdcoverlay">external documentation</a>. +-spec new(Overlay, Dc, X, Y, Width, Height) -> wxDCOverlay() when + Overlay::wxOverlay:wxOverlay(), Dc::wxWindowDC:wxWindowDC(), X::integer(), Y::integer(), Width::integer(), Height::integer(). +new(#wx_ref{type=OverlayT,ref=OverlayRef},#wx_ref{type=DcT,ref=DcRef},X,Y,Width,Height) + when is_integer(X),is_integer(Y),is_integer(Width),is_integer(Height) -> + ?CLASS(OverlayT,wxOverlay), + ?CLASS(DcT,wxWindowDC), + wxe_util:construct(?wxDCOverlay_new_6, + <<OverlayRef:32/?UI,DcRef:32/?UI,X:32/?UI,Y:32/?UI,Width:32/?UI,Height:32/?UI>>). + +%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxdcoverlay.html#wxdcoverlayclear">external documentation</a>. +-spec clear(This) -> ok when + This::wxDCOverlay(). +clear(#wx_ref{type=ThisT,ref=ThisRef}) -> + ?CLASS(ThisT,wxDCOverlay), + wxe_util:cast(?wxDCOverlay_Clear, + <<ThisRef:32/?UI>>). + +%% @doc Destroys this object, do not use object again +-spec destroy(This::wxDCOverlay()) -> ok. +destroy(Obj=#wx_ref{type=Type}) -> + ?CLASS(Type,wxDCOverlay), + wxe_util:destroy(?wxDCOverlay_destruct,Obj), + ok. diff --git a/lib/wx/src/gen/wxOverlay.erl b/lib/wx/src/gen/wxOverlay.erl new file mode 100644 index 0000000000..7da3ece657 --- /dev/null +++ b/lib/wx/src/gen/wxOverlay.erl @@ -0,0 +1,57 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2008-2015. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% This file is generated DO NOT EDIT + +%% @doc See external documentation: <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxoverlay.html">wxOverlay</a>. +%% @type wxOverlay(). An object reference, The representation is internal +%% and can be changed without notice. It can't be used for comparsion +%% stored on disc or distributed for use on other nodes. + +-module(wxOverlay). +-include("wxe.hrl"). +-export([destroy/1,new/0,reset/1]). + +%% inherited exports +-export([parent_class/1]). + +-export_type([wxOverlay/0]). +%% @hidden +parent_class(_Class) -> erlang:error({badtype, ?MODULE}). + +-type wxOverlay() :: wx:wx_object(). +%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxoverlay.html#wxoverlaywxoverlay">external documentation</a>. +-spec new() -> wxOverlay(). +new() -> + wxe_util:construct(?wxOverlay_new, + <<>>). + +%% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxoverlay.html#wxoverlayreset">external documentation</a>. +-spec reset(This) -> ok when + This::wxOverlay(). +reset(#wx_ref{type=ThisT,ref=ThisRef}) -> + ?CLASS(ThisT,wxOverlay), + wxe_util:cast(?wxOverlay_Reset, + <<ThisRef:32/?UI>>). + +%% @doc Destroys this object, do not use object again +-spec destroy(This::wxOverlay()) -> ok. +destroy(Obj=#wx_ref{type=Type}) -> + ?CLASS(Type,wxOverlay), + wxe_util:destroy(?wxOverlay_destruct,Obj), + ok. diff --git a/lib/wx/src/gen/wxe_debug.hrl b/lib/wx/src/gen/wxe_debug.hrl index 2cb73c0fed..6c7602acd3 100644 --- a/lib/wx/src/gen/wxe_debug.hrl +++ b/lib/wx/src/gen/wxe_debug.hrl @@ -3363,6 +3363,13 @@ wxdebug_table() -> {3583, {wxPopupTransientWindow, destruct, 0}}, {3584, {wxPopupTransientWindow, popup, 1}}, {3585, {wxPopupTransientWindow, dismiss, 0}}, + {3586, {wxOverlay, new, 0}}, + {3587, {wxOverlay, destruct, 0}}, + {3588, {wxOverlay, reset, 0}}, + {3589, {wxDCOverlay, new_6, 6}}, + {3590, {wxDCOverlay, new_2, 2}}, + {3591, {wxDCOverlay, destruct, 0}}, + {3592, {wxDCOverlay, clear, 0}}, {-1, {mod, func, -1}} ]. diff --git a/lib/wx/src/gen/wxe_funcs.hrl b/lib/wx/src/gen/wxe_funcs.hrl index d8c2ba9171..3a34cd494d 100644 --- a/lib/wx/src/gen/wxe_funcs.hrl +++ b/lib/wx/src/gen/wxe_funcs.hrl @@ -3360,3 +3360,10 @@ -define(wxPopupTransientWindow_destruct, 3583). -define(wxPopupTransientWindow_Popup, 3584). -define(wxPopupTransientWindow_Dismiss, 3585). +-define(wxOverlay_new, 3586). +-define(wxOverlay_destruct, 3587). +-define(wxOverlay_Reset, 3588). +-define(wxDCOverlay_new_6, 3589). +-define(wxDCOverlay_new_2, 3590). +-define(wxDCOverlay_destruct, 3591). +-define(wxDCOverlay_Clear, 3592). diff --git a/lib/wx/src/wxe_server.erl b/lib/wx/src/wxe_server.erl index cc253b1143..ae9440f890 100644 --- a/lib/wx/src/wxe_server.erl +++ b/lib/wx/src/wxe_server.erl @@ -352,25 +352,8 @@ handle_disconnect(Object, Evh = #evh{cb=Fun}, From, State0 = #state{users=Users0, cb=Callbacks}) -> #user{events=Evs0} = gb_trees:get(From, Users0), FunId = gb_trees:lookup(Fun, Callbacks), - case find_handler(Evs0, Object, Evh#evh{cb=FunId}) of - [] -> - {reply, false, State0}; - Handlers -> - case disconnect(Object,Handlers) of - #evh{} -> {reply, true, State0}; - Result -> {reply, Result, State0} - end - end. - -disconnect(Object,[Ev|Evs]) -> - try wxEvtHandler:disconnect_impl(Object,Ev) of - true -> Ev; - false -> disconnect(Object, Evs); - Error -> Error - catch _:_ -> - false - end; -disconnect(_, []) -> false. + Handlers = find_handler(Evs0, Object, Evh#evh{cb=FunId}), + {reply, {try_in_order, Handlers}, State0}. find_handler([{Object,Evh}|Evs], Object, Match) -> case match_handler(Match, Evh) of diff --git a/lib/wx/src/wxe_util.erl b/lib/wx/src/wxe_util.erl index 1983e6783a..398ceddd4f 100644 --- a/lib/wx/src/wxe_util.erl +++ b/lib/wx/src/wxe_util.erl @@ -127,8 +127,21 @@ connect_cb(Object,EvData0 = #evh{cb=Callback}) -> disconnect_cb(Object,EvData) -> Server = (wx:get_env())#wx_env.sv, - gen_server:call(Server, {disconnect_cb,Object,EvData}, infinity). - + {try_in_order, Handlers} = + gen_server:call(Server, {disconnect_cb,Object,EvData}, infinity), + disconnect(Object, Handlers). + +disconnect(Object,[Ev|Evs]) -> + try wxEvtHandler:disconnect_impl(Object,Ev) of + true -> true; + false -> disconnect(Object, Evs); + Error -> Error + catch _:_ -> + false + end; +disconnect(_, []) -> false. + + debug_cast(1, Op, _Args, _Port) -> check_previous(), case ets:lookup(wx_debug_info,Op) of |