aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bootstrap/lib/compiler/ebin/beam_validator.beambin34488 -> 34556 bytes
-rw-r--r--bootstrap/lib/kernel/ebin/application.beambin4020 -> 4572 bytes
-rw-r--r--bootstrap/lib/stdlib/ebin/edlin.beambin9260 -> 9956 bytes
-rw-r--r--erts/doc/src/erl_driver.xml24
-rw-r--r--erts/doc/src/tty.xml26
-rw-r--r--erts/emulator/beam/erl_alloc_util.c2
-rw-r--r--erts/emulator/beam/erl_async.c14
-rw-r--r--erts/emulator/beam/erl_driver.h4
-rw-r--r--erts/emulator/drivers/common/efile_drv.c37
-rw-r--r--erts/emulator/drivers/win32/win_efile.c1
-rw-r--r--erts/emulator/sys/win32/erl_win_dyn_driver.h4
-rw-r--r--erts/emulator/test/efile_SUITE.erl88
-rw-r--r--erts/test/erlc_SUITE.erl83
-rw-r--r--erts/test/erlc_SUITE_data/src/erl_test_missing_header.erl22
-rw-r--r--lib/compiler/src/beam_validator.erl1
-rw-r--r--lib/compiler/test/beam_validator_SUITE.erl9
-rw-r--r--lib/compiler/test/beam_validator_SUITE_data/bad_bin_match.S15
-rw-r--r--lib/inets/doc/src/notes.xml15
-rw-r--r--lib/inets/src/http_lib/http_transport.erl21
-rw-r--r--lib/inets/src/http_server/httpd_conf.erl18
-rw-r--r--lib/inets/src/http_server/httpd_request_handler.erl2
-rw-r--r--lib/inets/src/http_server/httpd_response.erl6
-rw-r--r--lib/inets/src/http_server/mod_cgi.erl2
-rw-r--r--lib/inets/src/http_server/mod_esi.erl2
-rw-r--r--lib/inets/test/httpd_basic_SUITE.erl25
-rw-r--r--lib/kernel/doc/src/application.xml27
-rw-r--r--lib/kernel/src/application.erl43
-rw-r--r--lib/kernel/test/application_SUITE.erl107
-rw-r--r--lib/kernel/test/interactive_shell_SUITE.erl51
-rw-r--r--lib/mnesia/src/mnesia_checkpoint.erl279
-rw-r--r--lib/mnesia/test/mnesia_consistency_test.erl28
-rw-r--r--lib/parsetools/src/yecc.erl8
-rw-r--r--lib/parsetools/test/yecc_SUITE.erl37
-rw-r--r--lib/sasl/doc/src/rb.xml34
-rw-r--r--lib/sasl/src/rb.erl56
-rw-r--r--lib/sasl/test/rb_SUITE.erl32
-rw-r--r--lib/ssh/doc/src/ssh.xml1
-rw-r--r--lib/ssh/src/ssh.hrl3
-rw-r--r--lib/ssh/src/ssh_connection_handler.erl4
-rw-r--r--lib/ssh/src/ssh_transport.erl37
-rw-r--r--lib/ssh/test/ssh_basic_SUITE.erl23
-rw-r--r--lib/ssl/doc/src/notes.xml17
-rw-r--r--lib/ssl/doc/src/ssl.xml6
-rw-r--r--lib/ssl/src/ssl.erl1
-rw-r--r--lib/ssl/src/ssl_internal.hrl3
-rw-r--r--lib/ssl/src/ssl_manager.erl24
-rw-r--r--lib/ssl/src/tls.erl8
-rw-r--r--lib/ssl/src/tls_connection.erl29
-rw-r--r--lib/stdlib/doc/src/io_lib.xml3
-rw-r--r--lib/stdlib/src/dets.erl4
-rw-r--r--lib/stdlib/src/digraph.erl2
-rw-r--r--lib/stdlib/src/edlin.erl30
-rw-r--r--lib/stdlib/src/erl_lint.erl3
-rw-r--r--lib/stdlib/src/io_lib.erl9
-rw-r--r--lib/stdlib/src/io_lib_fread.erl9
-rw-r--r--lib/stdlib/test/dets_SUITE.erl26
-rw-r--r--lib/stdlib/test/erl_lint_SUITE.erl18
-rw-r--r--lib/tools/emacs/erlang.el44
58 files changed, 1104 insertions, 323 deletions
diff --git a/bootstrap/lib/compiler/ebin/beam_validator.beam b/bootstrap/lib/compiler/ebin/beam_validator.beam
index 69539e400c..6b8673cb82 100644
--- a/bootstrap/lib/compiler/ebin/beam_validator.beam
+++ b/bootstrap/lib/compiler/ebin/beam_validator.beam
Binary files differ
diff --git a/bootstrap/lib/kernel/ebin/application.beam b/bootstrap/lib/kernel/ebin/application.beam
index c0a615f5a3..e39352117f 100644
--- a/bootstrap/lib/kernel/ebin/application.beam
+++ b/bootstrap/lib/kernel/ebin/application.beam
Binary files differ
diff --git a/bootstrap/lib/stdlib/ebin/edlin.beam b/bootstrap/lib/stdlib/ebin/edlin.beam
index bb586c5f6a..7b8e03a722 100644
--- a/bootstrap/lib/stdlib/ebin/edlin.beam
+++ b/bootstrap/lib/stdlib/ebin/edlin.beam
Binary files differ
diff --git a/erts/doc/src/erl_driver.xml b/erts/doc/src/erl_driver.xml
index f52d973709..540390e1b1 100644
--- a/erts/doc/src/erl_driver.xml
+++ b/erts/doc/src/erl_driver.xml
@@ -1981,7 +1981,7 @@ ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len
thread, the following call can be used:</p>
<p></p>
<code type="none"><![CDATA[
- unsigned int myKey = (unsigned int) myPort;
+ unsigned int myKey = driver_async_port_key(myPort);
r = driver_async(myPort, &myKey, myData, myFunc);
]]></code>
@@ -2022,6 +2022,24 @@ ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len
</desc>
</func>
<func>
+ <name><ret>unsigned int</ret><nametext>driver_async_port_key (ErlDrvPort port)</nametext></name>
+ <fsummary>Calculate an async key from an ErlDrvPort</fsummary>
+ <desc>
+ <marker id="driver_async_port_key"></marker>
+ <p>This function calculates a key for later use in <seealso
+ marker="#driver_async">driver_async()</seealso>. The keys are
+ evenly distributed so that a fair mapping between port id's
+ and async thread id's is achieved.</p>
+ <note>
+ <p>Before OTP-R16, the actual port id could be used as a key
+ with proper casting, but after the rewrite of the port
+ subsystem, this is no longer the case. With this function, you
+ can achieve the same distribution based on port id's as before
+ OTP-R16.</p>
+ </note>
+ </desc>
+ </func>
+ <func>
<name><ret>int</ret><nametext>driver_async_cancel(long id)</nametext></name>
<fsummary>Cancel an asynchronous call</fsummary>
<desc>
@@ -2033,10 +2051,10 @@ ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len
The user had to implement synchronization of cancellation anyway.
It also unnecessarily complicated the implementation. Therefore,
as of OTP-R15B <c>driver_async_cancel()</c> is deprecated, and
- scheduled for removal in OTP-R16. It will currently always fail,
+ scheduled for removal in OTP-R17. It will currently always fail,
and return 0.</p>
<warning><p><c>driver_async_cancel()</c> is deprecated and will
- be removed in the OTP-R16 release.</p>
+ be removed in the OTP-R17 release.</p>
</warning>
</desc>
diff --git a/erts/doc/src/tty.xml b/erts/doc/src/tty.xml
index 7d662a2849..b16523e085 100644
--- a/erts/doc/src/tty.xml
+++ b/erts/doc/src/tty.xml
@@ -47,7 +47,7 @@
<section>
<title>Normal Mode</title>
<p>In normal mode keystrokes from the user are collected and interpreted by <c><![CDATA[tty]]></c>. Most of the <em>emacs</em> line editing commands are supported. The following is a complete list of the supported line editing commands.<br></br></p>
- <p><em>Note:</em> The notation <c><![CDATA[C-a]]></c> means pressing the control key and the letter <c><![CDATA[a]]></c> simultaneously. <c><![CDATA[M-f]]></c> means pressing the <c><![CDATA[ESC]]></c> key followed by the letter <c><![CDATA[f]]></c>.
+ <p><em>Note:</em> The notation <c><![CDATA[C-a]]></c> means pressing the control key and the letter <c><![CDATA[a]]></c> simultaneously. <c><![CDATA[M-f]]></c> means pressing the <c><![CDATA[ESC]]></c> key followed by the letter <c><![CDATA[f]]></c>. <c><![CDATA[Home]]></c> and <c><![CDATA[End]]></c> represent the keys with the same name on the keyboard, whereas <c><![CDATA[Left]]></c> and <c><![CDATA[Right]]></c> represent the corresponding arrow keys.
</p>
<table>
<row>
@@ -55,6 +55,10 @@
<cell align="left" valign="middle"><em>Function</em></cell>
</row>
<row>
+ <cell align="left" valign="middle">Home</cell>
+ <cell align="left" valign="middle">Beginning of line</cell>
+ </row>
+ <row>
<cell align="left" valign="middle">C-a</cell>
<cell align="left" valign="middle">Beginning of line</cell>
</row>
@@ -63,6 +67,10 @@
<cell align="left" valign="middle">Backward character</cell>
</row>
<row>
+ <cell align="left" valign="middle">C-Left</cell>
+ <cell align="left" valign="middle">Backward word</cell>
+ </row>
+ <row>
<cell align="left" valign="middle">M-b</cell>
<cell align="left" valign="middle">Backward word</cell>
</row>
@@ -75,6 +83,10 @@
<cell align="left" valign="middle">Delete word</cell>
</row>
<row>
+ <cell align="left" valign="middle">End</cell>
+ <cell align="left" valign="middle">End of line</cell>
+ </row>
+ <row>
<cell align="left" valign="middle">C-e</cell>
<cell align="left" valign="middle">End of line</cell>
</row>
@@ -83,6 +95,10 @@
<cell align="left" valign="middle">Forward character</cell>
</row>
<row>
+ <cell align="left" valign="middle">C-Right</cell>
+ <cell align="left" valign="middle">Forward word</cell>
+ </row>
+ <row>
<cell align="left" valign="middle">M-f</cell>
<cell align="left" valign="middle">Forward word</cell>
</row>
@@ -95,6 +111,10 @@
<cell align="left" valign="middle">Kill line</cell>
</row>
<row>
+ <cell align="left" valign="middle">C-u</cell>
+ <cell align="left" valign="middle">Backward kill line</cell>
+ </row>
+ <row>
<cell align="left" valign="middle">C-l</cell>
<cell align="left" valign="middle">Redraw line</cell>
</row>
@@ -111,6 +131,10 @@
<cell align="left" valign="middle">Transpose characters</cell>
</row>
<row>
+ <cell align="left" valign="middle">C-w</cell>
+ <cell align="left" valign="middle">Backward kill word</cell>
+ </row>
+ <row>
<cell align="left" valign="middle">C-y</cell>
<cell align="left" valign="middle">Insert previously killed text</cell>
</row>
diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c
index bf8a37c71b..e6d9f83aed 100644
--- a/erts/emulator/beam/erl_alloc_util.c
+++ b/erts/emulator/beam/erl_alloc_util.c
@@ -4369,7 +4369,7 @@ info_options(Allctr_t *allctr,
#endif
"option lmbcs: %beu\n"
"option smbcs: %beu\n"
- "option mbcgs: %beu\n",
+ "option mbcgs: %beu\n"
"option acul: %d\n",
topt,
allctr->ramv ? "true" : "false",
diff --git a/erts/emulator/beam/erl_async.c b/erts/emulator/beam/erl_async.c
index 054d1a48f6..e6d72f569b 100644
--- a/erts/emulator/beam/erl_async.c
+++ b/erts/emulator/beam/erl_async.c
@@ -583,6 +583,20 @@ int erts_async_ready_clean(void *varq, void *val)
#endif
/*
+** Generate a fair async key prom an ErlDrvPort
+** The port data gives a fair distribution grom port pointer
+** to unsigned integer - to be used in key for driver_async below.
+*/
+unsigned int driver_async_port_key(ErlDrvPort port)
+{
+ ErlDrvTermData td = driver_mk_port(port);
+ if (td == (ErlDrvTermData) NIL) {
+ return 0;
+ }
+ return (unsigned int) (UWord) internal_port_data(td);
+}
+
+/*
** Schedule async_invoke on a worker thread
** NOTE will be syncrounous when threads are unsupported
** return values:
diff --git a/erts/emulator/beam/erl_driver.h b/erts/emulator/beam/erl_driver.h
index e280563de1..1ab6e17f56 100644
--- a/erts/emulator/beam/erl_driver.h
+++ b/erts/emulator/beam/erl_driver.h
@@ -133,7 +133,7 @@ typedef struct {
#define ERL_DRV_EXTENDED_MARKER (0xfeeeeeed)
#define ERL_DRV_EXTENDED_MAJOR_VERSION 2
-#define ERL_DRV_EXTENDED_MINOR_VERSION 1
+#define ERL_DRV_EXTENDED_MINOR_VERSION 2
/*
* The emulator will refuse to load a driver with different major
@@ -638,6 +638,8 @@ EXTERN int erl_drv_send_term(ErlDrvTermData port,
int len);
/* Async IO functions */
+EXTERN unsigned int driver_async_port_key(ErlDrvPort port);
+
EXTERN long driver_async(ErlDrvPort ix,
unsigned int* key,
void (*async_invoke)(void*),
diff --git a/erts/emulator/drivers/common/efile_drv.c b/erts/emulator/drivers/common/efile_drv.c
index edcb56de1d..c997fe1bf9 100644
--- a/erts/emulator/drivers/common/efile_drv.c
+++ b/erts/emulator/drivers/common/efile_drv.c
@@ -553,8 +553,10 @@ efile_ev_get_char(ErlIOVec *ev, char *p, int *pp, int *qp) {
*(p) = *EV_CHAR_P(ev, *(pp), *(qp));
if (*(pp)+1 < (ev)->iov[*(qp)].iov_len)
*(pp) = *(pp)+1;
- else
+ else {
(*(qp))++;
+ *pp = 0;
+ }
return !0;
}
return 0;
@@ -575,8 +577,10 @@ efile_ev_get_uint32(ErlIOVec *ev, Uint32 *p, int *pp, int *qp) {
| (EV_UINT32(ev, *(pp)+3, *(qp)));
if (*(pp)+4 < (ev)->iov[*(qp)].iov_len)
*(pp) = *(pp)+4;
- else
+ else {
(*(qp))++;
+ *pp = 0;
+ }
return !0;
}
return 0;
@@ -601,8 +605,10 @@ efile_ev_get_uint64(ErlIOVec *ev, Uint64 *p, int *pp, int *qp) {
| (EV_UINT64(ev, *(pp)+7, *(qp)));
if (*(pp)+8 < (ev)->iov[*(qp)].iov_len)
*(pp) = *(pp)+8;
- else
+ else {
(*(qp))++;
+ *pp = 0;
+ }
return !0;
}
return 0;
@@ -766,6 +772,7 @@ file_init(void)
return 0;
}
+
/*********************************************************************
* Driver entry point -> start
*/
@@ -782,7 +789,7 @@ file_start(ErlDrvPort port, char* command)
}
desc->fd = FILE_FD_INVALID;
desc->port = port;
- desc->key = (unsigned int) (UWord) port;
+ desc->key = driver_async_port_key(port);
desc->flags = 0;
desc->invoke = NULL;
desc->d = NULL;
@@ -3127,25 +3134,25 @@ file_flush(ErlDrvData e) {
/*********************************************************************
* Driver entry point -> control
+ * Only debug functionality...
*/
static ErlDrvSSizeT
file_control(ErlDrvData e, unsigned int command,
char* buf, ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen) {
- /*
- * warning: variable ‘desc’ set but not used
- * [-Wunused-but-set-variable]
- * ... no kidding ...
- *
- *
file_descriptor *desc = (file_descriptor *)e;
switch (command) {
+ case 'K' :
+ if (rlen < 4) {
+ *rbuf = EF_ALLOC(4);
+ }
+ (*rbuf)[0] = ((desc->key) >> 24) & 0xFF;
+ (*rbuf)[1] = ((desc->key) >> 16) & 0xFF;
+ (*rbuf)[2] = ((desc->key) >> 8) & 0xFF;
+ (*rbuf)[3] = (desc->key) & 0xFF;
+ return 4;
default:
return 0;
- }
- ASSERT(0);
- desc = NULL;
- */
- return 0;
+ }
}
/*********************************************************************
diff --git a/erts/emulator/drivers/win32/win_efile.c b/erts/emulator/drivers/win32/win_efile.c
index be3d86a1d2..b36a103f8e 100644
--- a/erts/emulator/drivers/win32/win_efile.c
+++ b/erts/emulator/drivers/win32/win_efile.c
@@ -772,6 +772,7 @@ efile_may_openfile(Efile_error* errInfo, char *name) {
DWORD attr;
if ((attr = GetFileAttributesW(wname)) == INVALID_FILE_ATTRIBUTES) {
+ errno = ENOENT;
return check_error(-1, errInfo);
}
diff --git a/erts/emulator/sys/win32/erl_win_dyn_driver.h b/erts/emulator/sys/win32/erl_win_dyn_driver.h
index a7c53c904d..b9a9838a36 100644
--- a/erts/emulator/sys/win32/erl_win_dyn_driver.h
+++ b/erts/emulator/sys/win32/erl_win_dyn_driver.h
@@ -80,6 +80,7 @@ WDD_TYPEDEF(int, erl_drv_output_term, (ErlDrvTermData, ErlDrvTermData*, int));
WDD_TYPEDEF(int, driver_output_term, (ErlDrvPort, ErlDrvTermData*, int));
WDD_TYPEDEF(int, erl_drv_send_term, (ErlDrvTermData, ErlDrvTermData, ErlDrvTermData*, int));
WDD_TYPEDEF(int, driver_send_term, (ErlDrvPort, ErlDrvTermData, ErlDrvTermData*, int));
+WDD_TYPEDEF(unsigned int, driver_async_port_key, (ErlDrvPort));
WDD_TYPEDEF(long, driver_async, (ErlDrvPort,unsigned int*,void (*)(void*),void*,void (*)(void*)));
WDD_TYPEDEF(int, driver_async_cancel, (unsigned int));
WDD_TYPEDEF(int, driver_lock_driver, (ErlDrvPort));
@@ -197,6 +198,7 @@ typedef struct {
WDD_FTYPE(driver_output_term) *driver_output_term;
WDD_FTYPE(erl_drv_send_term) *erl_drv_send_term;
WDD_FTYPE(driver_send_term) *driver_send_term;
+ WDD_FTYPE(driver_async_port_key) *driver_async_port_key;
WDD_FTYPE(driver_async) *driver_async;
WDD_FTYPE(driver_async_cancel) *driver_async_cancel;
WDD_FTYPE(driver_lock_driver) *driver_lock_driver;
@@ -308,6 +310,7 @@ extern TWinDynDriverCallbacks WinDynDriverCallbacks;
#define driver_output_term (WinDynDriverCallbacks.driver_output_term)
#define erl_drv_send_term (WinDynDriverCallbacks.erl_drv_send_term)
#define driver_send_term (WinDynDriverCallbacks.driver_send_term)
+#define driver_async_port_key (WinDynDriverCallbacks.driver_async_port_key)
#define driver_async (WinDynDriverCallbacks.driver_async)
#define driver_async_cancel (WinDynDriverCallbacks.driver_async_cancel)
#define driver_lock_driver (WinDynDriverCallbacks.driver_lock_driver)
@@ -443,6 +446,7 @@ do { \
((W).driver_output_term) = driver_output_term; \
((W).erl_drv_send_term) = erl_drv_send_term; \
((W).driver_send_term) = driver_send_term; \
+((W).driver_async_port_key) = driver_async_port_key; \
((W).driver_async) = driver_async; \
((W).driver_async_cancel) = driver_async_cancel; \
((W).driver_lock_driver) = driver_lock_driver; \
diff --git a/erts/emulator/test/efile_SUITE.erl b/erts/emulator/test/efile_SUITE.erl
index 65367eab98..f79bb761d1 100644
--- a/erts/emulator/test/efile_SUITE.erl
+++ b/erts/emulator/test/efile_SUITE.erl
@@ -19,16 +19,16 @@
-module(efile_SUITE).
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2]).
--export([iter_max_files/1]).
+-export([iter_max_files/1, async_dist/1]).
--export([do_iter_max_files/2]).
+-export([do_iter_max_files/2, do_async_dist/1]).
-include_lib("test_server/include/test_server.hrl").
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [iter_max_files].
+ [iter_max_files, async_dist].
groups() ->
[].
@@ -45,6 +45,84 @@ init_per_group(_GroupName, Config) ->
end_per_group(_GroupName, Config) ->
Config.
+do_async_dist(Dir) ->
+ X = 100,
+ AT = erlang:system_info(thread_pool_size),
+ Keys = file_keys(Dir,AT*X,[],[]),
+ Tab = ets:new(x,[ordered_set]),
+ [ ets:insert(Tab,{N,0}) || N <- lists:seq(0,AT-1) ],
+ [ ets:update_counter(Tab,(N rem AT),1) || N <- Keys ],
+ Res = [ V || {_,V} <- ets:tab2list(Tab) ],
+ ets:delete(Tab),
+ {Res, sdev(Res)/X}.
+
+sdev(List) ->
+ Len = length(List),
+ Mean = lists:sum(List)/Len,
+ math:sqrt(lists:sum([ (X - Mean) * (X - Mean) || X <- List ]) / Len).
+
+file_keys(_,0,FdList,FnList) ->
+ [ file:close(FD) || FD <- FdList ],
+ [ file:delete(FN) || FN <- FnList ],
+ [];
+file_keys(Dir,Num,FdList,FnList) ->
+ Name = "dummy"++integer_to_list(Num),
+ FN = filename:join([Dir,Name]),
+ case file:open(FN,[write,raw]) of
+ {ok,FD} ->
+ {file_descriptor,prim_file,{Port,_}} = FD,
+ <<X:32/integer-big>> =
+ iolist_to_binary(erlang:port_control(Port,$K,[])),
+ [X | file_keys(Dir,Num-1,[FD|FdList],[FN|FnList])];
+ {error,_} ->
+ % Try freeing up FD's if there are any
+ case FdList of
+ [] ->
+ exit({cannot_open_file,FN});
+ _ ->
+ [ file:close(FD) || FD <- FdList ],
+ [ file:delete(F) || F <- FnList ],
+ file_keys(Dir,Num,[],[])
+ end
+ end.
+
+async_dist(doc) ->
+ "Check that the distribution of files over async threads is fair";
+async_dist(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir,Config),
+ TestFile = filename:join(DataDir, "existing_file"),
+ Dir = filename:dirname(code:which(?MODULE)),
+ AsyncSizes = [7,10,100,255,256,64,63,65],
+ Max = 0.5,
+
+ lists:foreach(fun(Size) ->
+ {ok,Node} =
+ test_server:start_node
+ (test_iter_max_files,slave,
+ [{args,
+ "+A "++integer_to_list(Size)++
+ " -pa " ++ Dir}]),
+ {Distr,SD} = rpc:call(Node,?MODULE,do_async_dist,
+ [DataDir]),
+ test_server:stop_node(Node),
+ if
+ SD > Max ->
+ io:format("Bad async queue distribution for "
+ "~p async threads:~n"
+ " Standard deviation is ~p~n"
+ " Key distribution:~n ~lp~n",
+ [Size,SD,Distr]),
+ exit({bad_async_dist,Size,SD,Distr});
+ true ->
+ io:format("OK async queue distribution for "
+ "~p async threads:~n"
+ " Standard deviation is ~p~n"
+ " Key distribution:~n ~lp~n",
+ [Size,SD,Distr]),
+ ok
+ end
+ end, AsyncSizes),
+ ok.
%%
%% Open as many files as possible. Do this several times and check
@@ -98,7 +176,7 @@ open_files(Name) ->
?line case file:open(Name, [read,raw]) of
{ok, Fd} ->
[Fd| open_files(Name)];
- {error, Reason} ->
-% io:format("Error reason: ~p", [Reason]),
+ {error, _Reason} ->
+% io:format("Error reason: ~p", [_Reason]),
[]
end.
diff --git a/erts/test/erlc_SUITE.erl b/erts/test/erlc_SUITE.erl
index ab774dbc4f..ed7a43c7e7 100644
--- a/erts/test/erlc_SUITE.erl
+++ b/erts/test/erlc_SUITE.erl
@@ -23,7 +23,8 @@
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2, compile_erl/1,
compile_yecc/1, compile_script/1,
- compile_mib/1, good_citizen/1, deep_cwd/1, arg_overflow/1]).
+ compile_mib/1, good_citizen/1, deep_cwd/1, arg_overflow/1,
+ make_dep_options/1]).
-include_lib("test_server/include/test_server.hrl").
@@ -31,7 +32,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[compile_erl, compile_yecc, compile_script, compile_mib,
- good_citizen, deep_cwd, arg_overflow].
+ good_citizen, deep_cwd, arg_overflow, make_dep_options].
groups() ->
[].
@@ -255,13 +256,89 @@ erlc() ->
Erlc ->
"\"" ++ Erlc ++ "\""
end.
-
+
+make_dep_options(Config) ->
+ {SrcDir,OutDir,Cmd} = get_cmd(Config),
+ FileName = filename:join(SrcDir, "erl_test_ok.erl"),
+
+
+ DepRE = ["/erl_test_ok[.]beam: \\\\$",
+ "/system_test/erlc_SUITE_data/src/erl_test_ok[.]erl \\\\$",
+ "/system_test/erlc_SUITE_data/include/erl_test[.]hrl$",
+ "_OK_"],
+
+ DepRETarget =
+ ["^target: \\\\$",
+ "/system_test/erlc_SUITE_data/src/erl_test_ok[.]erl \\\\$",
+ "/system_test/erlc_SUITE_data/include/erl_test[.]hrl$",
+ "_OK_"],
+
+ DepREMP =
+ ["/erl_test_ok[.]beam: \\\\$",
+ "/system_test/erlc_SUITE_data/src/erl_test_ok[.]erl \\\\$",
+ "/system_test/erlc_SUITE_data/include/erl_test[.]hrl$",
+ [],
+ "/system_test/erlc_SUITE_data/include/erl_test.hrl:$",
+ "_OK_"],
+
+ DepREMissing =
+ ["/erl_test_missing_header[.]beam: \\\\$",
+ "/system_test/erlc_SUITE_data/src/erl_test_missing_header[.]erl \\\\$",
+ "/system_test/erlc_SUITE_data/include/erl_test[.]hrl \\\\$",
+ "missing.hrl$",
+ "_OK_"],
+
+ %% Test plain -M
+ run(Config, Cmd, FileName, "-M", DepRE),
+
+ %% Test -MF File
+ DepFile = filename:join(OutDir, "my.deps"),
+ run(Config, Cmd, FileName, "-MF "++DepFile, ["_OK_"]),
+ {ok,MFBin} = file:read_file(DepFile),
+ verify_result(binary_to_list(MFBin)++["_OK_"], DepRE),
+
+ %% Test -MD
+ run(Config, Cmd, FileName, "-MD", ["_OK_"]),
+ MDFile = filename:join(OutDir, "erl_test_ok.Pbeam"),
+ {ok,MFBin} = file:read_file(MDFile),
+
+ %% Test -M -MT Target
+ run(Config, Cmd, FileName, "-M -MT target", DepRETarget),
+
+ %% Test -MF File -MT Target
+ TargetDepFile = filename:join(OutDir, "target.deps"),
+ run(Config, Cmd, FileName, "-MF "++TargetDepFile++" -MT target",
+ ["_OK_"]),
+ {ok,TargetBin} = file:read_file(TargetDepFile),
+ verify_result(binary_to_list(TargetBin)++["_OK_"], DepRETarget),
+
+ %% Test -MD -MT Target
+ run(Config, Cmd, FileName, "-MD -MT target", ["_OK_"]),
+ TargetMDFile = filename:join(OutDir, "erl_test_ok.Pbeam"),
+ {ok,TargetBin} = file:read_file(TargetMDFile),
+
+ %% Test -M -MQ Target. (Note: Passing a $ on the command line
+ %% portably for Unix and Windows is tricky, so we will just test
+ %% that MQ works at all.)
+ run(Config, Cmd, FileName, "-M -MQ target", DepRETarget),
+
+ %% Test -M -MP
+ run(Config, Cmd, FileName, "-M -MP", DepREMP),
+
+ %% Test -M -MG
+ MissingHeader = filename:join(SrcDir, "erl_test_missing_header.erl"),
+ run(Config, Cmd, MissingHeader, "-M -MG", DepREMissing),
+ ok.
+
%% Runs a command.
run(Config, Cmd0, Name, Options, Expect) ->
Cmd = Cmd0 ++ " " ++ Options ++ " " ++ Name,
io:format("~s", [Cmd]),
Result = run_command(Config, Cmd),
+ verify_result(Result, Expect).
+
+verify_result(Result, Expect) ->
Messages = split(Result, [], []),
io:format("Result: ~p", [Messages]),
io:format("Expected: ~p", [Expect]),
diff --git a/erts/test/erlc_SUITE_data/src/erl_test_missing_header.erl b/erts/test/erlc_SUITE_data/src/erl_test_missing_header.erl
new file mode 100644
index 0000000000..4d6c42c803
--- /dev/null
+++ b/erts/test/erlc_SUITE_data/src/erl_test_missing_header.erl
@@ -0,0 +1,22 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2013. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(erl_test_missing_header).
+-include("erl_test.hrl").
+-include("missing.hrl").
diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl
index eb72290306..70279ab658 100644
--- a/lib/compiler/src/beam_validator.erl
+++ b/lib/compiler/src/beam_validator.erl
@@ -628,6 +628,7 @@ valfun_4({gc_bif,Op,{f,Fail},Live,Src,Dst}, #vst{current=St0}=Vst0) ->
Type = bif_type(Op, Src, Vst),
set_type_reg(Type, Dst, Vst);
valfun_4(return, #vst{current=#st{numy=none}}=Vst) ->
+ assert_term({x,0}, Vst),
kill_state(Vst);
valfun_4(return, #vst{current=#st{numy=NumY}}) ->
error({stack_frame,NumY});
diff --git a/lib/compiler/test/beam_validator_SUITE.erl b/lib/compiler/test/beam_validator_SUITE.erl
index bc1a9e2b3b..626f89ba7a 100644
--- a/lib/compiler/test/beam_validator_SUITE.erl
+++ b/lib/compiler/test/beam_validator_SUITE.erl
@@ -28,7 +28,7 @@
overwrite_catchtag/1,overwrite_trytag/1,accessing_tags/1,bad_catch_try/1,
cons_guard/1,
freg_range/1,freg_uninit/1,freg_state/1,
- bin_match/1,bin_aligned/1,bad_dsetel/1,
+ bin_match/1,bad_bin_match/1,bin_aligned/1,bad_dsetel/1,
state_after_fault_in_catch/1,no_exception_in_catch/1,
undef_label/1,illegal_instruction/1,failing_gc_guard_bif/1]).
@@ -56,7 +56,7 @@ groups() ->
unsafe_catch,dead_code,mult_labels,
overwrite_catchtag,overwrite_trytag,accessing_tags,
bad_catch_try,cons_guard,freg_range,freg_uninit,
- freg_state,bin_match,bin_aligned,bad_dsetel,
+ freg_state,bin_match,bad_bin_match,bin_aligned,bad_dsetel,
state_after_fault_in_catch,no_exception_in_catch,
undef_label,illegal_instruction,failing_gc_guard_bif]}].
@@ -317,6 +317,11 @@ bin_match(Config) when is_list(Config) ->
{{t,x,1},{{bs_restore,1},16,{no_save_point,1}}}] = Errors,
ok.
+bad_bin_match(Config) when is_list(Config) ->
+ [{{t,t,1},{return,5,{match_context,{x,0}}}}] =
+ do_val(bad_bin_match, Config),
+ ok.
+
bin_aligned(Config) when is_list(Config) ->
Errors = do_val(bin_aligned, Config),
?line
diff --git a/lib/compiler/test/beam_validator_SUITE_data/bad_bin_match.S b/lib/compiler/test/beam_validator_SUITE_data/bad_bin_match.S
new file mode 100644
index 0000000000..a60ca1e89a
--- /dev/null
+++ b/lib/compiler/test/beam_validator_SUITE_data/bad_bin_match.S
@@ -0,0 +1,15 @@
+{module, bad_bin_match}. %% version = 0
+
+{exports, [{t,1}]}.
+
+{attributes, []}.
+
+{labels, 3}.
+
+
+{function, t, 1, 2}.
+ {label,1}.
+ {func_info,{atom,t},{atom,t},1}.
+ {label,2}.
+ {test,bs_start_match2,{f,1},1,[{x,0},0],{x,0}}.
+ return.
diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml
index d2e7ade5d6..f6bb2cca49 100644
--- a/lib/inets/doc/src/notes.xml
+++ b/lib/inets/doc/src/notes.xml
@@ -45,8 +45,6 @@
</item>
</list>
</section>
-
-
<section><title>Improvements and New Features</title>
<list>
<item>
@@ -158,7 +156,20 @@
</section>
</section>
+<section><title>Inets 5.9.2.2</title>
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Make log_alert configurable as option in ssl, SSLLogLevel
+ added as option to inets conf file</p>
+ <p>
+ Own Id: OTP-11259</p>
+ </item>
+ </list>
+ </section>
+</section>
<section><title>Inets 5.9.2.1</title>
<section><title>Improvements and New Features</title>
<list>
diff --git a/lib/inets/src/http_lib/http_transport.erl b/lib/inets/src/http_lib/http_transport.erl
index df58fa1b81..7e679531cf 100644
--- a/lib/inets/src/http_lib/http_transport.erl
+++ b/lib/inets/src/http_lib/http_transport.erl
@@ -159,7 +159,7 @@ listen(ip_comm = _SocketType, Addr, Port, Fd, IpFamily) ->
listen_ip_comm(Addr, Port, Fd, IpFamily);
listen({essl, SSLConfig}, Addr, Port, Fd, IpFamily) ->
- listen_ssl(Addr, Port, Fd, SSLConfig, IpFamily).
+ listen_ssl(Addr, Port, Fd, SSLConfig, IpFamily, []).
listen(ip_comm = _SocketType, Addr, Port, IpFamily) ->
listen_ip_comm(Addr, Port, undefined, IpFamily);
@@ -178,7 +178,13 @@ listen({essl, SSLConfig}, Addr, Port, IpFamily) ->
[{addr, Addr},
{port, Port},
{ssl_config, SSLConfig}]),
- listen_ssl(Addr, Port, undefined, SSLConfig, IpFamily).
+ {SSLConfig2, ExtraOpts} = case proplists:get_value(log_alert, SSLConfig, undefined) of
+ undefined ->
+ {SSLConfig, []};
+ LogAlert ->
+ {proplists:delete(log_alert, SSLConfig), [{log_alert, LogAlert}]}
+ end,
+ listen_ssl(Addr, Port, undefined, SSLConfig2, IpFamily, ExtraOpts).
listen_ip_comm(Addr, Port, Fd, IpFamily) ->
case (catch do_listen_ip_comm(Addr, Port, Fd, IpFamily)) of
@@ -221,24 +227,23 @@ do_listen_ip_comm(Addr, Port, Fd, IpFamily) ->
gen_tcp:listen(NewPort, Opts2)
end.
-
-listen_ssl(Addr, Port, Fd, Opts0, IpFamily) ->
+listen_ssl(Addr, Port, Fd, Opts0, IpFamily, ExtraOpts) ->
{NewPort, SockOpt} = get_socket_info(Addr, Port, Fd),
Opts = SockOpt ++ Opts0,
case IpFamily of
inet6fb4 ->
- Opts2 = [inet6 | Opts],
+ Opts2 = [inet6 | Opts] ++ ExtraOpts,
?hlrt("try ipv6 listen", [{opts, Opts2}]),
case (catch ssl:listen(Port, Opts2)) of
{error, Reason} when ((Reason =:= nxdomain) orelse
(Reason =:= eafnosupport)) ->
- Opts3 = [inet | Opts],
+ Opts3 = [inet | Opts] ++ ExtraOpts,
?hlrt("ipv6 listen failed - try ipv4 instead",
[{reason, Reason}, {opts, Opts3}]),
ssl:listen(NewPort, Opts3);
{'EXIT', Reason} ->
- Opts3 = [inet | Opts],
+ Opts3 = [inet | Opts] ++ ExtraOpts,
?hlrt("ipv6 listen exit - try ipv4 instead",
[{reason, Reason}, {opts, Opts3}]),
ssl:listen(NewPort, Opts3);
@@ -251,7 +256,7 @@ listen_ssl(Addr, Port, Fd, Opts0, IpFamily) ->
_ ->
Opts2 = [IpFamily | Opts],
?hlrt("listen", [{opts, Opts2}]),
- ssl:listen(NewPort, Opts2)
+ ssl:listen(NewPort, Opts2 ++ ExtraOpts)
end.
diff --git a/lib/inets/src/http_server/httpd_conf.erl b/lib/inets/src/http_server/httpd_conf.erl
index d45f3c0048..b3ca13e2fe 100644
--- a/lib/inets/src/http_server/httpd_conf.erl
+++ b/lib/inets/src/http_server/httpd_conf.erl
@@ -390,6 +390,13 @@ load("SSLCertificateFile " ++ SSLCertificateFile, []) ->
{error, ?NICE(clean(SSLCertificateFile)++
" is an invalid SSLCertificateFile")}
end;
+load("SSLLogLevel " ++ SSLLogAlert, []) ->
+ case SSLLogAlert of
+ "none" ->
+ {ok, [], {ssl_log_alert, false}};
+ _ ->
+ {ok, [], {ssl_log_alert, true}}
+ end;
load("SSLCertificateKeyFile " ++ SSLCertificateKeyFile, []) ->
case is_file(clean(SSLCertificateKeyFile)) of
{ok, File} ->
@@ -948,7 +955,8 @@ ssl_config(ConfigDB) ->
ssl_ciphers(ConfigDB) ++
ssl_password(ConfigDB) ++
ssl_verify_depth(ConfigDB) ++
- ssl_ca_certificate_file(ConfigDB).
+ ssl_ca_certificate_file(ConfigDB) ++
+ ssl_log_level(ConfigDB).
@@ -1214,6 +1222,14 @@ ssl_certificate_key_file(ConfigDB) ->
[{keyfile,SSLCertificateKeyFile}]
end.
+ssl_log_level(ConfigDB) ->
+ case httpd_util:lookup(ConfigDB,ssl_log_alert) of
+ undefined ->
+ [];
+ SSLLogLevel ->
+ [{log_alert,SSLLogLevel}]
+ end.
+
ssl_verify_client(ConfigDB) ->
case httpd_util:lookup(ConfigDB,ssl_verify_client) of
undefined ->
diff --git a/lib/inets/src/http_server/httpd_request_handler.erl b/lib/inets/src/http_server/httpd_request_handler.erl
index 0f47d785ef..cb20159794 100644
--- a/lib/inets/src/http_server/httpd_request_handler.erl
+++ b/lib/inets/src/http_server/httpd_request_handler.erl
@@ -106,7 +106,7 @@ init([Manager, ConfigDB, AcceptTimeout]) ->
case http_transport:negotiate(SocketType, Socket, TimeOut) of
{error, Error} ->
?hdrd("negotiation failed", [{error, Error}]),
- exit(Error); %% Can be 'normal'.
+ exit(shutdown); %% Can be 'normal'.
ok ->
?hdrt("negotiation successfull", []),
NewTimeout = TimeOut - timer:now_diff(now(),Then) div 1000,
diff --git a/lib/inets/src/http_server/httpd_response.erl b/lib/inets/src/http_server/httpd_response.erl
index 6b6532266b..a45b04f275 100644
--- a/lib/inets/src/http_server/httpd_response.erl
+++ b/lib/inets/src/http_server/httpd_response.erl
@@ -20,7 +20,7 @@
-module(httpd_response).
-export([generate_and_send_response/1, send_status/3, send_header/3,
send_body/3, send_chunk/3, send_final_chunk/2, split_header/2,
- is_disable_chunked_send/1, cache_headers/1]).
+ is_disable_chunked_send/1, cache_headers/2]).
-export([map_status_code/2]).
-include("httpd.hrl").
@@ -266,8 +266,8 @@ get_connection(false,"HTTP/1.1") ->
get_connection(_,_) ->
"".
-cache_headers(#mod{config_db = Db}) ->
- case httpd_util:lookup(Db, script_nocache, false) of
+cache_headers(#mod{config_db = Db}, NoCacheType) ->
+ case httpd_util:lookup(Db, NoCacheType, false) of
true ->
Date = httpd_util:rfc1123_date(),
[{"cache-control", "no-cache"},
diff --git a/lib/inets/src/http_server/mod_cgi.erl b/lib/inets/src/http_server/mod_cgi.erl
index c854166c29..f1b73810e6 100644
--- a/lib/inets/src/http_server/mod_cgi.erl
+++ b/lib/inets/src/http_server/mod_cgi.erl
@@ -295,7 +295,7 @@ receive_headers(Port, Module, Function, Args, Timeout) ->
end.
send_headers(ModData, {StatusCode, _}, HTTPHeaders) ->
- ExtraHeaders = httpd_response:cache_headers(ModData),
+ ExtraHeaders = httpd_response:cache_headers(ModData, script_nocache),
httpd_response:send_header(ModData, StatusCode,
ExtraHeaders ++ HTTPHeaders).
diff --git a/lib/inets/src/http_server/mod_esi.erl b/lib/inets/src/http_server/mod_esi.erl
index e36c33b282..b11df34f9e 100644
--- a/lib/inets/src/http_server/mod_esi.erl
+++ b/lib/inets/src/http_server/mod_esi.erl
@@ -440,7 +440,7 @@ receive_headers(Timeout) ->
end.
send_headers(ModData, StatusCode, HTTPHeaders) ->
- ExtraHeaders = httpd_response:cache_headers(ModData),
+ ExtraHeaders = httpd_response:cache_headers(ModData, erl_script_nocache),
httpd_response:send_header(ModData, StatusCode,
ExtraHeaders ++ HTTPHeaders).
diff --git a/lib/inets/test/httpd_basic_SUITE.erl b/lib/inets/test/httpd_basic_SUITE.erl
index 523cf9d38c..fef0a1f0f4 100644
--- a/lib/inets/test/httpd_basic_SUITE.erl
+++ b/lib/inets/test/httpd_basic_SUITE.erl
@@ -33,7 +33,8 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[
uri_too_long_414,
- header_too_long_413,
+ header_too_long_413,
+ erl_script_nocache_opt,
escaped_url_in_error_body,
slowdose
].
@@ -178,6 +179,28 @@ header_too_long_413(Config) when is_list(Config) ->
{version, "HTTP/1.1"}]),
inets:stop(httpd, Pid).
+%%-------------------------------------------------------------------------
+%%-------------------------------------------------------------------------
+
+erl_script_nocache_opt(doc) ->
+ ["Test that too long headers's get 413 HTTP code"];
+erl_script_nocache_opt(suite) ->
+ [];
+erl_script_nocache_opt(Config) when is_list(Config) ->
+ HttpdConf = ?config(httpd_conf, Config),
+ {ok, Pid} = inets:start(httpd, [{port, 0}, {erl_script_nocache, true} | HttpdConf]),
+ Info = httpd:info(Pid),
+ Port = proplists:get_value(port, Info),
+ _Address = proplists:get_value(bind_address, Info),
+ URL1 = ?URL_START ++ integer_to_list(Port),
+ case httpc:request(get, {URL1 ++ "/dummy.html", []},
+ [{url_encode, false},
+ {version, "HTTP/1.0"}],
+ [{full_result, false}]) of
+ {ok, {200, _}} ->
+ ok
+ end,
+ inets:stop(httpd, Pid).
%%-------------------------------------------------------------------------
%%-------------------------------------------------------------------------
diff --git a/lib/kernel/doc/src/application.xml b/lib/kernel/doc/src/application.xml
index 362c373c6c..3909b11e59 100644
--- a/lib/kernel/doc/src/application.xml
+++ b/lib/kernel/doc/src/application.xml
@@ -253,15 +253,30 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]</code>
</warning>
</desc>
</func>
- <func>
+ <func>
<name name="ensure_started" arity="1"/>
<name name="ensure_started" arity="2"/>
<fsummary>Load and start an application</fsummary>
- <desc>
- <p>Equivalent to <seealso marker="#start/2"><c>application:start/1,2</c></seealso> except
- it returns <c>ok</c> for already started applications.</p>
- </desc>
- </func>
+ <desc>
+ <p>Equivalent to <seealso marker="#start/2"><c>application:start/1,2</c></seealso> except
+ it returns <c>ok</c> for already started applications.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="ensure_all_started" arity="1"/>
+ <name name="ensure_all_started" arity="2"/>
+ <fsummary>Load and start an application and its dependencies, recursively</fsummary>
+ <desc>
+ <p>Equivalent to calling <seealso marker="#start/2"><c>application:start/1,2</c></seealso>
+ repeatedly on all dependencies that have not yet been started for an application.
+ The function returns <c>{ok, AppNames}</c> for a successful start or for an already started
+ application (which are however omitted from the <c>AppNames</c> list), and reports
+ <c>{error, {AppName,Reason}}</c> for errors, where <c>Reason</c> is any possible reason
+ returned by <seealso marker="#start/2"><c>application:start/1,2</c></seealso> when starting a
+ specific dependency. In case of an error, the applications that were started by the
+ function are stopped to bring the set of running applications back to its initial state.</p>
+ </desc>
+ </func>
<func>
<name name="start" arity="1"/>
<name name="start" arity="2"/>
diff --git a/lib/kernel/src/application.erl b/lib/kernel/src/application.erl
index 5dd6b73857..4e8ba1b78a 100644
--- a/lib/kernel/src/application.erl
+++ b/lib/kernel/src/application.erl
@@ -18,7 +18,8 @@
%%
-module(application).
--export([start/1, start/2, start_boot/1, start_boot/2, stop/1,
+-export([ensure_all_started/1, ensure_all_started/2, start/1, start/2,
+ start_boot/1, start_boot/2, stop/1,
load/1, load/2, unload/1, takeover/2,
which_applications/0, which_applications/1,
loaded_applications/0, permit/2]).
@@ -113,6 +114,46 @@ load1(Application, DistNodes) ->
unload(Application) ->
application_controller:unload_application(Application).
+
+-spec ensure_all_started(Application) -> {'ok', Started} | {'error', Reason} when
+ Application :: atom(),
+ Started :: [atom()],
+ Reason :: term().
+ensure_all_started(Application) ->
+ ensure_all_started(Application, temporary).
+
+-spec ensure_all_started(Application, Type) -> {'ok', Started} | {'error', Reason} when
+ Application :: atom(),
+ Type :: restart_type(),
+ Started :: [atom()],
+ Reason :: term().
+ensure_all_started(Application, Type) ->
+ case ensure_all_started(Application, Type, []) of
+ {ok, Started} ->
+ {ok, lists:reverse(Started)};
+ {error, Reason, Started} ->
+ [stop(App) || App <- Started],
+ {error, Reason}
+ end.
+
+ensure_all_started(Application, Type, Started) ->
+ case start(Application, Type) of
+ ok ->
+ {ok, [Application | Started]};
+ {error, {already_started, Application}} ->
+ {ok, Started};
+ {error, {not_started, Dependency}} ->
+ case ensure_all_started(Dependency, Type, Started) of
+ {ok, NewStarted} ->
+ ensure_all_started(Application, Type, NewStarted);
+ Error ->
+ Error
+ end;
+ {error, Reason} ->
+ {error, {Application, Reason}, Started}
+ end.
+
+
-spec start(Application) -> 'ok' | {'error', Reason} when
Application :: atom(),
Reason :: term().
diff --git a/lib/kernel/test/application_SUITE.erl b/lib/kernel/test/application_SUITE.erl
index 1ff291be54..9ec8a15861 100644
--- a/lib/kernel/test/application_SUITE.erl
+++ b/lib/kernel/test/application_SUITE.erl
@@ -35,7 +35,7 @@
-export([config_change/1,
distr_changed_tc1/1, distr_changed_tc2/1,
- ensure_started/1,
+ ensure_started/1, ensure_all_started/1,
shutdown_func/1, do_shutdown/1, shutdown_timeout/1]).
-define(TESTCASE, testcase_name).
@@ -52,7 +52,7 @@ all() ->
[failover, failover_comp, permissions, load,
load_use_cache, ensure_started, {group, reported_bugs}, start_phases,
script_start, nodedown_start, permit_false_start_local,
- permit_false_start_dist, get_key, get_env,
+ permit_false_start_dist, get_key, get_env, ensure_all_started,
{group, distr_changed}, config_change, shutdown_func, shutdown_timeout].
groups() ->
@@ -978,6 +978,85 @@ ensure_started(Conf) ->
ok = application:unload(app1),
ok.
+ensure_all_started(suite) -> [];
+ensure_all_started(doc) -> ["Test application:ensure_all_started/1-2."];
+ensure_all_started(Conf) ->
+
+ {ok, Fd1} = file:open("app1.app", [write]),
+ w_app1(Fd1),
+ file:close(Fd1),
+ {ok, Fd9} = file:open("app9.app", [write]),
+ w_app9(Fd9),
+ file:close(Fd9),
+ {ok, Fd10} = file:open("app10.app", [write]),
+ w_app10_dep9(Fd10),
+ file:close(Fd10),
+ {ok, FdErr} = file:open("app_chain_error.app", [write]),
+ w_app(FdErr, app_chain_error()),
+ file:close(FdErr),
+ {ok, FdErr2} = file:open("app_chain_error2.app", [write]),
+ w_app(FdErr2, app_chain_error2()),
+ file:close(FdErr2),
+
+ %% Single app start/stop
+ false = lists:keyfind(app1, 1, application:which_applications()),
+ {ok, [app1]} = application:ensure_all_started(app1), % app1 started
+ {app1, _, _} = lists:keyfind(app1, 1, application:which_applications()),
+ {ok, []} = application:ensure_all_started(app1), % no start needed
+ ok = application:stop(app1),
+ false = lists:keyfind(app1, 1, application:which_applications()),
+ ok = application:unload(app1),
+
+ %% App or dependency not found.
+ Name = hopefully_not_an_existing_app_file,
+ {error,{Name, {"no such file or directory", _ }}} =
+ application:ensure_all_started(Name),
+
+ %% Start dependencies.
+ {error, {not_started, app9}} = application:start(app10),
+ {ok, [app9,app10]} = application:ensure_all_started(app10, temporary),
+ {app9, _, _} = lists:keyfind(app9, 1, application:which_applications()),
+ {app10, _, _} = lists:keyfind(app10, 1, application:which_applications()),
+ %% Only report apps/dependencies that actually needed to start
+ ok = application:stop(app10),
+ ok = application:unload(app10),
+ {ok, [app10]} = application:ensure_all_started(app10, temporary),
+ ok = application:stop(app9),
+ ok = application:unload(app9),
+ ok = application:stop(app10),
+ ok = application:unload(app10),
+
+ %% Deeper failure chain. We have the following dependencies:
+ %% app_chain_error -> app_chain_error2
+ %% app_chain_error2 -> app10
+ %% app_chain_error2 -> hopefully_not_an_existing_app
+ %% app10 -> app 9
+ %% First we have none running and we expect to have neither app9
+ %% nor app10 running after failing to start
+ %% hopefully_not_an_existing_app
+ {error, {hopefully_not_an_existing_app, {"no such file or directory", _}}}=
+ application:ensure_all_started(app_chain_error),
+ false = lists:keyfind(app9, 1, application:which_applications()),
+ false = lists:keyfind(app10, 1, application:which_applications()),
+ false = lists:keyfind(app_chain_error2,1,application:which_applications()),
+ false = lists:keyfind(app_chain_error, 1, application:which_applications()),
+ %% Here we will have app9 already running, and app10 should be
+ %% able to boot fine.
+ %% In this dependency failing, we expect app9 to still be running, but
+ %% not app10 after failing to start hopefully_not_an_existing_app
+ {ok, [app9]} = application:ensure_all_started(app9, temporary),
+ {error, {hopefully_not_an_existing_app, {"no such file or directory", _}}}=
+ application:ensure_all_started(app_chain_error),
+ {app9, _, _} = lists:keyfind(app9, 1, application:which_applications()),
+ false = lists:keyfind(app10, 1, application:which_applications()),
+ false = lists:keyfind(app_chain_error2,1,application:which_applications()),
+ false = lists:keyfind(app_chain_error, 1, application:which_applications()),
+ ok = application:stop(app9),
+ ok = application:unload(app9),
+ ok = application:unload(app10),
+ ok = application:unload(app_chain_error2),
+ ok = application:unload(app_chain_error),
+ ok.
%%%-----------------------------------------------------------------
%%% Testing of reported bugs and other tickets.
@@ -2125,6 +2204,24 @@ app_start_error() ->
{applications, [kernel]},
{mod, {app_start_error, []}}]}.
+app_chain_error() ->
+ {application, app_chain_error,
+ [{description, "ERTS CXC 138 ce"},
+ {vsn, "2.0"},
+ {modules, []},
+ {registered, []},
+ {applications, [kernel, app_chain_error2]},
+ {mod, {ch_sup, {app_chain_error, 20,20}}}]}.
+
+app_chain_error2() ->
+ {application, app_chain_error2,
+ [{description, "ERTS CXC 138 ce2"},
+ {vsn, "2.0"},
+ {modules, []},
+ {registered, []},
+ {applications, [kernel, app10, hopefully_not_an_existing_app]},
+ {mod, {ch_sup, {app_chain_error2, 21,21}}}]}.
+
app_group_leader() ->
{application, group_leader,
[{description, "GROUP_LEADER CXC 138 11"},
@@ -2374,6 +2471,12 @@ w_app7(Fd) ->
w_app8(Fd) ->
io:format(Fd, "~p.\n", [app8()]).
+w_app9(Fd) ->
+ io:format(Fd, "~p.\n", [app9()]).
+
+w_app10_dep9(Fd) ->
+ io:format(Fd, "~p.\n", [app10_dep9()]).
+
w_app_start_error(Fd) ->
io:format(Fd, "~p.\n", [app_start_error()]).
diff --git a/lib/kernel/test/interactive_shell_SUITE.erl b/lib/kernel/test/interactive_shell_SUITE.erl
index d7d9434b1f..a375adceea 100644
--- a/lib/kernel/test/interactive_shell_SUITE.erl
+++ b/lib/kernel/test/interactive_shell_SUITE.erl
@@ -22,7 +22,7 @@
init_per_group/2,end_per_group/2,
get_columns_and_rows/1, exit_initial/1, job_control_local/1,
job_control_remote/1,
- job_control_remote_noshell/1]).
+ job_control_remote_noshell/1,ctrl_keys/1]).
-export([init_per_testcase/2, end_per_testcase/2]).
%% For spawn
@@ -41,7 +41,8 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[get_columns_and_rows, exit_initial, job_control_local,
- job_control_remote, job_control_remote_noshell].
+ job_control_remote, job_control_remote_noshell,
+ ctrl_keys].
groups() ->
[].
@@ -289,7 +290,51 @@ job_control_remote_noshell(Config) when is_list(Config) ->
?line stop_noshell_node(NSNode),
?line Res
end.
-
+
+ctrl_keys(suite) -> [];
+ctrl_keys(doc) -> ["Tests various control keys"];
+ctrl_keys(_Conf) when is_list(_Conf) ->
+ Cu=[$\^u],
+ Cw=[$\^w],
+ Home=[27,$O,$H],
+ End=[27,$O,$F],
+ rtnode([{putline,""},
+ {putline,"2."},
+ {getline,"2"},
+ {putline,"\"hello "++Cw++"world\"."}, % test <CTRL>+W
+ {getline,"\"world\""},
+ {putline,"\"hello "++Cu++"\"world\"."}, % test <CTRL>+U
+ {getline,"\"world\""},
+ {putline,"world\"."++Home++"\"hello "}, % test <HOME>
+ {getline,"\"hello world\""},
+ {putline,"world"++Home++"\"hello "++End++"\"."}, % test <END>
+ {getline,"\"hello world\""}]
+ ++wordLeft()++wordRight(),[]).
+
+
+wordLeft() ->
+ L1=[27,27,$[,$D],
+ L2=[27]++"[5D",
+ L3=[27]++"[1;5D",
+ wordLeft(L1)++wordLeft(L2)++wordLeft(L3).
+
+wordLeft(Chars) ->
+ End=[27,$O,$F],
+ [{putline,"\"world\""++Chars++"hello "++End++"."},
+ {getline,"\"hello world\""}].
+
+wordRight() ->
+ R1=[27,27,$[,$C],
+ R2=[27]++"[5C",
+ R3=[27]++"[1;5C",
+ wordRight(R1)++wordRight(R2)++wordRight(R3).
+
+wordRight(Chars) ->
+ Home=[27,$O,$H],
+ [{putline,"world"++Home++"\"hello "++Chars++"\"."},
+ {getline,"\"hello world\""}].
+
+
rtnode(C,N) ->
rtnode(C,N,[]).
rtnode(Commands,Nodename,ErlPrefix) ->
diff --git a/lib/mnesia/src/mnesia_checkpoint.erl b/lib/mnesia/src/mnesia_checkpoint.erl
index eb8fe38908..173e3be2f5 100644
--- a/lib/mnesia/src/mnesia_checkpoint.erl
+++ b/lib/mnesia/src/mnesia_checkpoint.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2013
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -31,8 +31,7 @@
tm_retain/5,
tm_enter_pending/1,
tm_enter_pending/3,
- tm_exit_pending/1,
- convert_cp_record/1
+ tm_exit_pending/1
]).
%% Public interface
@@ -88,25 +87,6 @@
pid
}).
-%% Old record definition
--record(checkpoint, {name,
- allow_remote,
- ram_overrides_dump,
- nodes,
- node,
- now,
- min,
- max,
- pending_tab,
- wait_for_old,
- is_activated,
- ignore_new,
- retainers,
- iterators,
- supervisor,
- pid
- }).
-
-record(retainer, {cp_name, tab_name, store, writers = [], really_retain = true}).
-record(iter, {tab_name, oid_tab, main_tab, retainer_tab, source, val, pid}).
@@ -129,15 +109,6 @@ tm_prepare(Cp) when is_record(Cp, checkpoint_args) ->
start_retainer(Cp);
true ->
{error, {already_exists, Name, node()}}
- end;
-tm_prepare(Cp) when is_record(Cp, checkpoint) ->
- %% Node with old protocol sent an old checkpoint record
- %% and we have to convert it
- case convert_cp_record(Cp) of
- {ok, NewCp} ->
- tm_prepare(NewCp);
- {error, Reason} ->
- {error, Reason}
end.
tm_mnesia_down(Node) ->
@@ -156,6 +127,7 @@ tm_enter_pending(Pending) ->
tm_enter_pending([], Pending) ->
Pending;
tm_enter_pending([Tab | Tabs], Pending) ->
+ %% io:format("Add ~p ~p ~p~n",[Tab, Pending, hd(tl(element(2, process_info(self(), current_stacktrace))))]),
catch ?ets_insert(Tab, Pending),
tm_enter_pending(Tabs, Pending).
@@ -371,9 +343,7 @@ activate(Args) ->
end.
args2cp(Args) when is_list(Args)->
- case catch lists:foldl(fun check_arg/2, #checkpoint_args{}, Args) of
- {'EXIT', Reason} ->
- {error, Reason};
+ try lists:foldl(fun check_arg/2, #checkpoint_args{}, Args) of
Cp ->
case check_tables(Cp) of
{error, Reason} ->
@@ -381,6 +351,10 @@ args2cp(Args) when is_list(Args)->
{ok, Overriders, AllTabs} ->
arrange_retainers(Cp, Overriders, AllTabs)
end
+ catch exit:Reason ->
+ {error, Reason};
+ error:Reason ->
+ {error, Reason}
end;
args2cp(Args) ->
{error, {badarg, Args}}.
@@ -390,10 +364,10 @@ check_arg({name, Name}, Cp) ->
true ->
exit({already_exists, Name});
false ->
- case catch tab2retainer({foo, Name}) of
- List when is_list(List) ->
- Cp#checkpoint_args{name = Name};
- _ ->
+ try
+ [_|_] = tab2retainer({foo, Name}),
+ Cp#checkpoint_args{name = Name}
+ catch _:_ ->
exit({badarg, Name})
end
end;
@@ -641,11 +615,7 @@ init(Cp) ->
process_flag(priority, high), %% Needed dets files might starve the system
Name = Cp#checkpoint_args.name,
Props = [set, public, {keypos, 2}],
- case catch ?ets_new_table(mnesia_pending_checkpoint, Props) of
- {'EXIT', Reason} -> %% system limit
- Msg = "Cannot create an ets table for pending transactions",
- Error = {error, {system_limit, Name, Msg, Reason}},
- proc_lib:init_ack(Cp#checkpoint_args.supervisor, Error);
+ try ?ets_new_table(mnesia_pending_checkpoint, Props) of
PendingTab ->
Rs = [prepare_tab(Cp, R) || R <- Cp#checkpoint_args.retainers],
Cp2 = Cp#checkpoint_args{retainers = Rs,
@@ -658,6 +628,10 @@ init(Cp) ->
dbg_out("Checkpoint ~p (~p) started~n", [Name, self()]),
proc_lib:init_ack(Cp2#checkpoint_args.supervisor, {ok, self()}),
retainer_loop(Cp2)
+ catch error:Reason -> %% system limit
+ Msg = "Cannot create an ets table for pending transactions",
+ Error = {error, {system_limit, Name, Msg, Reason}},
+ proc_lib:init_ack(Cp#checkpoint_args.supervisor, Error)
end.
prepare_tab(Cp, R) ->
@@ -798,104 +772,142 @@ retainer_delete({dets, Store}) ->
Fname = tab2retainer(Store),
file:delete(Fname).
-retainer_loop(Cp) ->
- Name = Cp#checkpoint_args.name,
+retainer_loop(Cp = #checkpoint_args{is_activated=false, name=Name}) ->
receive
- {_From, {retain, Tid, Tab, Key, OldRecs}}
- when Cp#checkpoint_args.wait_for_old == [] ->
- R = val({Tab, {retainer, Name}}),
- PendingTab = Cp#checkpoint_args.pending_tab,
- case R#retainer.really_retain of
- true when PendingTab =:= undefined ->
- Store = R#retainer.store,
- case retainer_get(Store, Key) of
- [] -> retainer_put(Store, {Tab, Key, OldRecs});
- _ -> already_retained
- end;
- true ->
- case ets:member(PendingTab, Tid) of
- true -> ignore;
- false ->
- Store = R#retainer.store,
- case retainer_get(Store, Key) of
- [] -> retainer_put(Store, {Tab, Key, OldRecs});
- _ -> already_retained
- end
- end;
- false ->
- ignore
- end,
- retainer_loop(Cp);
-
%% Adm
+ {From, {activate, Pending}} ->
+ StillPending = mnesia_recover:still_pending(Pending),
+ enter_still_pending(StillPending, Cp#checkpoint_args.pending_tab),
+ Local = [Tid || #tid{pid=Pid} = Tid <- StillPending, node(Pid) =/= node()],
+ Cp2 = maybe_activate(Cp#checkpoint_args{wait_for_old = Local}),
+
+ reply(From, Name, activated),
+ retainer_loop(Cp2);
+
+ {_From, {exit_pending, Tid}} when is_list(Cp#checkpoint_args.wait_for_old) ->
+ StillPending = lists:delete(Tid, Cp#checkpoint_args.wait_for_old),
+ Cp2 = Cp#checkpoint_args{wait_for_old = StillPending},
+ Cp3 = maybe_activate(Cp2),
+ retainer_loop(Cp3);
+
{From, deactivate} ->
do_stop(Cp),
reply(From, Name, deactivated),
unlink(From),
exit(shutdown);
+ {From, get_checkpoint} ->
+ reply(From, Name, Cp),
+ retainer_loop(Cp);
+ {_From, {add_retainer, R, Node}} ->
+ Cp2 = do_add_retainer(Cp, R, Node),
+ retainer_loop(Cp2);
+
+ {From, collect_pending} ->
+ PendingTab = Cp#checkpoint_args.pending_tab,
+ del(pending_checkpoints, PendingTab),
+ Pending = ?ets_match_object(PendingTab, '_'),
+ reply(From, Name, {ok, Pending}),
+ retainer_loop(Cp);
+
+ {_From, {mnesia_down, Node}} ->
+ Cp2 = do_del_retainers(Cp, Node),
+ retainer_loop(Cp2);
+
{'EXIT', Parent, _} when Parent == Cp#checkpoint_args.supervisor ->
%% do_stop(Cp),
%% assume that entire Mnesia is terminating
exit(shutdown);
- {_From, {mnesia_down, Node}} ->
- Cp2 = do_del_retainers(Cp, Node),
- retainer_loop(Cp2);
+ {'EXIT', From, _Reason} ->
+ Iters = [Iter || Iter <- Cp#checkpoint_args.iterators,
+ check_iter(From, Iter)],
+ retainer_loop(Cp#checkpoint_args{iterators = Iters});
+
+ {system, From, Msg} ->
+ dbg_out("~p got {system, ~p, ~p}~n", [?MODULE, From, Msg]),
+ sys:handle_system_msg(Msg, From, Cp#checkpoint_args.supervisor,
+ ?MODULE, [], Cp)
+ end;
+
+retainer_loop(Cp = #checkpoint_args{name=Name}) ->
+ receive
+ {_From, {retain, Tid, Tab, Key, OldRecs}} ->
+ R = val({Tab, {retainer, Name}}),
+ PendingTab = Cp#checkpoint_args.pending_tab,
+ case R#retainer.really_retain of
+ true ->
+ Store = R#retainer.store,
+ try true = ets:member(PendingTab, Tid),
+ %% io:format("CP: ~p ~p ~p ~p~n",[true, Tab, Key, Tid]),
+ case retainer_get(Store, Key) of
+ [] -> ignore;
+ _ -> ets:delete(element(2,Store), Key)
+ end
+ catch _:_ ->
+ %% io:format("CP: ~p ~p ~p ~p~n",[false, Tab, Key, Tid]),
+ case retainer_get(Store, Key) of
+ [] -> retainer_put(Store, {Tab, Key, OldRecs});
+ _ -> already_retained
+ end
+ end;
+ false ->
+ ignore
+ end,
+ retainer_loop(Cp);
+
+ %% Adm
{From, get_checkpoint} ->
reply(From, Name, Cp),
retainer_loop(Cp);
- {From, {add_copy, Tab, Node}} when Cp#checkpoint_args.wait_for_old == [] ->
+ {From, {add_copy, Tab, Node}} ->
{Res, Cp2} = do_add_copy(Cp, Tab, Node),
reply(From, Name, Res),
retainer_loop(Cp2);
- {From, {del_copy, Tab, Node}} when Cp#checkpoint_args.wait_for_old == [] ->
+ {From, {del_copy, Tab, Node}} ->
Cp2 = do_del_copy(Cp, Tab, Node),
reply(From, Name, ok),
retainer_loop(Cp2);
- {From, {change_copy, Tab, From, To}} when Cp#checkpoint_args.wait_for_old == [] ->
+ {From, {change_copy, Tab, From, To}} ->
Cp2 = do_change_copy(Cp, Tab, From, To),
reply(From, Name, ok),
retainer_loop(Cp2);
{_From, {add_retainer, R, Node}} ->
Cp2 = do_add_retainer(Cp, R, Node),
retainer_loop(Cp2);
- {_From, {del_retainer, R, Node}} when Cp#checkpoint_args.wait_for_old == [] ->
+ {_From, {del_retainer, R, Node}} ->
Cp2 = do_del_retainer(Cp, R, Node),
retainer_loop(Cp2);
%% Iteration
- {From, {iter_begin, Iter}} when Cp#checkpoint_args.wait_for_old == [] ->
+ {From, {iter_begin, Iter}} ->
Cp2 = iter_begin(Cp, From, Iter),
retainer_loop(Cp2);
- {From, {iter_end, Iter}} when Cp#checkpoint_args.wait_for_old == [] ->
+ {From, {iter_end, Iter}} ->
retainer_fixtable(Iter#iter.oid_tab, false),
Iters = Cp#checkpoint_args.iterators -- [Iter],
reply(From, Name, ok),
- retainer_loop(Cp#checkpoint_args{iterators = Iters});
-
- {_From, {exit_pending, Tid}}
- when is_list(Cp#checkpoint_args.wait_for_old) ->
- StillPending = lists:delete(Tid, Cp#checkpoint_args.wait_for_old),
- Cp2 = Cp#checkpoint_args{wait_for_old = StillPending},
- Cp3 = maybe_activate(Cp2),
- retainer_loop(Cp3);
+ retainer_loop(Cp#checkpoint_args{iterators = Iters});
- {From, collect_pending} ->
- PendingTab = Cp#checkpoint_args.pending_tab,
- del(pending_checkpoints, PendingTab),
- Pending = ?ets_match_object(PendingTab, '_'),
- reply(From, Name, {ok, Pending}),
+ {_From, {exit_pending, _Tid}} ->
retainer_loop(Cp);
- {From, {activate, Pending}} ->
- StillPending = mnesia_recover:still_pending(Pending),
- enter_still_pending(StillPending, Cp#checkpoint_args.pending_tab),
- Cp2 = maybe_activate(Cp#checkpoint_args{wait_for_old = StillPending}),
- reply(From, Name, activated),
+ {From, deactivate} ->
+ do_stop(Cp),
+ reply(From, Name, deactivated),
+ unlink(From),
+ exit(shutdown);
+
+ {_From, {mnesia_down, Node}} ->
+ Cp2 = do_del_retainers(Cp, Node),
retainer_loop(Cp2);
+ {'EXIT', Parent, _} when Parent == Cp#checkpoint_args.supervisor ->
+ %% do_stop(Cp),
+ %% assume that entire Mnesia is terminating
+ exit(shutdown);
+
{'EXIT', From, _Reason} ->
Iters = [Iter || Iter <- Cp#checkpoint_args.iterators,
check_iter(From, Iter)],
@@ -903,13 +915,17 @@ retainer_loop(Cp) ->
{system, From, Msg} ->
dbg_out("~p got {system, ~p, ~p}~n", [?MODULE, From, Msg]),
- sys:handle_system_msg(Msg, From, no_parent, ?MODULE, [], Cp)
+ sys:handle_system_msg(Msg, From, Cp#checkpoint_args.supervisor,
+ ?MODULE, [], Cp);
+ Msg ->
+ dbg_out("~p got ~p~n", [?MODULE, Msg])
end.
maybe_activate(Cp)
- when Cp#checkpoint_args.wait_for_old == [],
- Cp#checkpoint_args.is_activated == false ->
- Cp#checkpoint_args{pending_tab = undefined, is_activated = true};
+ when Cp#checkpoint_args.wait_for_old == [],
+ Cp#checkpoint_args.is_activated == false ->
+ Cp#checkpoint_args{%% pending_tab = undefined,
+ is_activated = true};
maybe_activate(Cp) ->
Cp.
@@ -1226,65 +1242,6 @@ system_terminate(_Reason, _Parent,_Debug, Cp) ->
system_code_change(Cp, _Module, _OldVsn, _Extra) ->
{ok, Cp}.
-convert_cp_record(Cp) when is_record(Cp, checkpoint) ->
- ROD =
- case Cp#checkpoint.ram_overrides_dump of
- true -> Cp#checkpoint.min ++ Cp#checkpoint.max;
- false -> []
- end,
-
- {ok, #checkpoint_args{name = Cp#checkpoint.name,
- allow_remote = Cp#checkpoint.name,
- ram_overrides_dump = ROD,
- nodes = Cp#checkpoint.nodes,
- node = Cp#checkpoint.node,
- now = Cp#checkpoint.now,
- cookie = ?unique_cookie,
- min = Cp#checkpoint.min,
- max = Cp#checkpoint.max,
- pending_tab = Cp#checkpoint.pending_tab,
- wait_for_old = Cp#checkpoint.wait_for_old,
- is_activated = Cp#checkpoint.is_activated,
- ignore_new = Cp#checkpoint.ignore_new,
- retainers = Cp#checkpoint.retainers,
- iterators = Cp#checkpoint.iterators,
- supervisor = Cp#checkpoint.supervisor,
- pid = Cp#checkpoint.pid
- }};
-convert_cp_record(Cp) when is_record(Cp, checkpoint_args) ->
- AllTabs = Cp#checkpoint_args.min ++ Cp#checkpoint_args.max,
- ROD = case Cp#checkpoint_args.ram_overrides_dump of
- [] ->
- false;
- AllTabs ->
- true;
- _ ->
- error
- end,
- if
- ROD == error ->
- {error, {"Old node cannot handle new checkpoint protocol",
- ram_overrides_dump}};
- true ->
- {ok, #checkpoint{name = Cp#checkpoint_args.name,
- allow_remote = Cp#checkpoint_args.name,
- ram_overrides_dump = ROD,
- nodes = Cp#checkpoint_args.nodes,
- node = Cp#checkpoint_args.node,
- now = Cp#checkpoint_args.now,
- min = Cp#checkpoint_args.min,
- max = Cp#checkpoint_args.max,
- pending_tab = Cp#checkpoint_args.pending_tab,
- wait_for_old = Cp#checkpoint_args.wait_for_old,
- is_activated = Cp#checkpoint_args.is_activated,
- ignore_new = Cp#checkpoint_args.ignore_new,
- retainers = Cp#checkpoint_args.retainers,
- iterators = Cp#checkpoint_args.iterators,
- supervisor = Cp#checkpoint_args.supervisor,
- pid = Cp#checkpoint_args.pid
- }}
- end.
-
%%%%%%%%%%%%%%%%%%%%%%%%%%
val(Var) ->
diff --git a/lib/mnesia/test/mnesia_consistency_test.erl b/lib/mnesia/test/mnesia_consistency_test.erl
index 922b89ec2b..a76a0116d5 100644
--- a/lib/mnesia/test/mnesia_consistency_test.erl
+++ b/lib/mnesia/test/mnesia_consistency_test.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2012. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -39,7 +39,6 @@ all() ->
{group, consistency_after_move_replica},
{group, consistency_after_transform_table},
consistency_after_change_table_copy_type,
- {group, consistency_after_fallback},
{group, consistency_after_restore},
consistency_after_rename_of_node,
{group, checkpoint_retainer_consistency},
@@ -99,10 +98,14 @@ groups() ->
[{group, updates_during_checkpoint_activation},
{group, updates_during_checkpoint_iteration},
{group, load_table_with_activated_checkpoint},
- {group,
- add_table_copy_to_table_checkpoint}]},
+ {group, add_table_copy_to_table_checkpoint},
+ {group, consistency_after_fallback}
+ ]},
{updates_during_checkpoint_activation, [],
- [updates_during_checkpoint_activation_2_ram,
+ [updates_during_checkpoint_activation_1_ram,
+ updates_during_checkpoint_activation_1_disc,
+ updates_during_checkpoint_activation_1_disc_only,
+ updates_during_checkpoint_activation_2_ram,
updates_during_checkpoint_activation_2_disc,
updates_during_checkpoint_activation_2_disc_only,
updates_during_checkpoint_activation_3_ram,
@@ -730,6 +733,18 @@ consistency_after_rename_of_node(doc) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+updates_during_checkpoint_activation_1_ram(suite) -> [];
+updates_during_checkpoint_activation_1_ram(Config) when is_list(Config) ->
+ updates_during_checkpoint_activation(ram_copies, 1, Config).
+
+updates_during_checkpoint_activation_1_disc(suite) -> [];
+updates_during_checkpoint_activation_1_disc(Config) when is_list(Config) ->
+ updates_during_checkpoint_activation(disc_copies, 1, Config).
+
+updates_during_checkpoint_activation_1_disc_only(suite) -> [];
+updates_during_checkpoint_activation_1_disc_only(Config) when is_list(Config) ->
+ updates_during_checkpoint_activation(disc_only_copies, 1, Config).
+
updates_during_checkpoint_activation_2_ram(suite) -> [];
updates_during_checkpoint_activation_2_ram(Config) when is_list(Config) ->
updates_during_checkpoint_activation(ram_copies, 2, Config).
@@ -771,7 +786,8 @@ updates_during_checkpoint_activation(ReplicaType,NodeConfig,Config) ->
timer:sleep(timer:seconds(Delay)),
{ok, CPName, _NodeList} =
- mnesia:activate_checkpoint([{max, mnesia:system_info(tables)}]),
+ mnesia:activate_checkpoint([{max, mnesia:system_info(tables)},
+ {ram_overrides_dump, true}]),
timer:sleep(timer:seconds(Delay)),
%% Stop tpcb
diff --git a/lib/parsetools/src/yecc.erl b/lib/parsetools/src/yecc.erl
index 53292b037a..f9207d926e 100644
--- a/lib/parsetools/src/yecc.erl
+++ b/lib/parsetools/src/yecc.erl
@@ -2106,8 +2106,8 @@ output_state_actions(St0, State, State, {Actions, Jump}, SI) ->
output_state_actions(St, State, JState, _XActions, _SI) ->
fwrite(St, <<"%% yeccpars2_~w: see yeccpars2_~w\n\n">>, [State, JState]).
-output_state_actions1(St, State, [], _IsFirst, normal, _SI) ->
- output_state_actions_fini(State, St);
+output_state_actions1(St, State, [], IsFirst, normal, _SI) ->
+ output_state_actions_fini(State, IsFirst, St);
output_state_actions1(St0, State, [], IsFirst, {to, ToS}, _SI) ->
St = delim(St0, IsFirst),
fwrite(St,
@@ -2151,9 +2151,9 @@ output_call_to_includefile(NewState, St) ->
fwrite(St, <<" yeccpars1(S, ~w, Ss, Stack, T, Ts, Tzr)">>,
[NewState]).
-output_state_actions_fini(State, St0) ->
+output_state_actions_fini(State, IsFirst, St0) ->
%% Backward compatible.
- St10 = delim(St0, false),
+ St10 = delim(St0, IsFirst),
St = fwrite(St10, <<"yeccpars2_~w(_, _, _, _, T, _, _) ->\n">>, [State]),
fwrite(St, <<" yeccerror(T).\n\n">>, []).
diff --git a/lib/parsetools/test/yecc_SUITE.erl b/lib/parsetools/test/yecc_SUITE.erl
index 3d66a2a525..9c865a1ec6 100644
--- a/lib/parsetools/test/yecc_SUITE.erl
+++ b/lib/parsetools/test/yecc_SUITE.erl
@@ -49,7 +49,7 @@
otp_5369/1, otp_6362/1, otp_7945/1, otp_8483/1, otp_8486/1,
- otp_7292/1, otp_7969/1, otp_8919/1, otp_10302/1]).
+ otp_7292/1, otp_7969/1, otp_8919/1, otp_10302/1, otp_11269/1]).
% Default timetrap timeout (set in init_per_testcase).
-define(default_timeout, ?t:minutes(1)).
@@ -76,7 +76,8 @@ groups() ->
[empty, prec, yeccpre, lalr, old_yecc, other_examples]},
{bugs, [],
[otp_5369, otp_6362, otp_7945, otp_8483, otp_8486]},
- {improvements, [], [otp_7292, otp_7969, otp_8919, otp_10302]}].
+ {improvements, [], [otp_7292, otp_7969, otp_8919, otp_10302,
+ otp_11269]}].
init_per_suite(Config) ->
Config.
@@ -1963,6 +1964,38 @@ otp_10302(Config) when is_list(Config) ->
run(Config, Ts),
ok.
+otp_11269(doc) ->
+ "OTP-11269. A bug.";
+otp_11269(suite) -> [];
+otp_11269(Config) when is_list(Config) ->
+ Dir = ?privdir,
+ Filename = filename:join(Dir, "OTP-11269.yrl"),
+ Ret = [return, {report, false}],
+ Pai = <<"Nonterminals
+ list list0 list1 newline_list.
+
+ Terminals
+ '\n' semi.
+
+ Rootsymbol list.
+
+ Endsymbol '$end'.
+
+ list -> newline_list list0 : '$2'.
+
+ list0 -> list1 '\n' newline_list : '$1'.
+
+ list1 -> list1 semi newline_list list1 :
+ {command_connect, '$1', '$4', semi}.
+
+ newline_list -> newline_list '\n' : nil.">>,
+ ok = file:write_file(Filename, Pai),
+ {ok,ErlFile,[{_YrlFile,[{none,yecc,{conflicts,1,0}}]}]} =
+ yecc:file(Filename, Ret),
+ Opts = [return, warn_unused_vars,{outdir,Dir}],
+ {ok,'OTP-11269',_Warnings} = compile:file(ErlFile, Opts),
+ ok.
+
yeccpre_size() ->
yeccpre_size(default_yeccpre()).
diff --git a/lib/sasl/doc/src/rb.xml b/lib/sasl/doc/src/rb.xml
index 3da825878e..b94914d8f9 100644
--- a/lib/sasl/doc/src/rb.xml
+++ b/lib/sasl/doc/src/rb.xml
@@ -138,6 +138,24 @@
</desc>
</func>
<func>
+ <name>log_list()</name>
+ <name>log_list(Type)</name>
+ <fsummary>Log reports list</fsummary>
+ <type>
+ <v>Type = type()</v>
+ <v>type() = error | error_report | info_msg | info_report |
+ warning_msg | warning_report | crash_report |
+ supervisor_report | progress</v>
+ </type>
+ <desc>
+ <p>Same as <c>list/0</c> or <c>list/1</c> functions
+ but result is printed to logfile, if set, otherwise to standard_io.
+ </p>
+ <p>If no <c>Type</c> is given, all reports are listed.
+ </p>
+ </desc>
+ </func>
+ <func>
<name>rescan()</name>
<name>rescan(Options)</name>
<fsummary>Rescan the report directory</fsummary>
@@ -172,7 +190,7 @@
<type>
<v>Options = [opt()]</v>
<v>opt() = {start_log, FileName} | {max, MaxNoOfReports} | {report_dir, DirString} | {type, ReportType} | {abort_on_error, Bool}</v>
- <v>FileName = string() | standard_io</v>
+ <v>FileName = string() | atom() | pid()</v>
<v>MaxNoOfReports = int() | all</v>
<v>DirString = string()</v>
<v>ReportType = type() | [type()] | all</v>
@@ -185,11 +203,13 @@
reports can be browsed. When the <c>rb_server</c> is
started, the files in the specified directory are
scanned. The other functions assume that the server has
- started.
+ started.
</p>
- <p><c>{start_log, FileName}</c> starts logging to file. All
- reports will be printed to the named file. The default is
- <c>standard_io</c>.
+ <p><c>{start_log, FileName}</c> starts logging to file,
+ registered name or io_device. All reports will be printed
+ to the named file. The default is <c>standard_io</c>.
+ The option {start_log, standard_error} is not allowed and
+ will be replaced by default standard_io.
</p>
<p><c>{max, MaxNoOfReports}</c>. Controls how many reports the
<c>rb_server</c> should read on start-up. This option is
@@ -226,11 +246,11 @@
<name>start_log(FileName)</name>
<fsummary>Redirect all output to <c>FileName</c></fsummary>
<type>
- <v>FileName = string()</v>
+ <v>FileName = string() | atom() | pid()</v>
</type>
<desc>
<p>Redirects all report output from the RB tool to the
- specified file.
+ specified file, registered name or io_device.
</p>
</desc>
</func>
diff --git a/lib/sasl/src/rb.erl b/lib/sasl/src/rb.erl
index 8004ef2c5a..767932e659 100644
--- a/lib/sasl/src/rb.erl
+++ b/lib/sasl/src/rb.erl
@@ -22,7 +22,7 @@
%% External exports
-export([start/0, start/1, stop/0, rescan/0, rescan/1]).
--export([list/0, list/1, show/0, show/1, grep/1, filter/1, filter/2, start_log/1, stop_log/0]).
+-export([list/0, list/1, log_list/0, log_list/1, show/0, show/1, grep/1, filter/1, filter/2, start_log/1, stop_log/0]).
-export([h/0, help/0]).
%% Internal exports
@@ -62,6 +62,9 @@ rescan(Options) ->
list() -> list(all).
list(Type) -> call({list, Type}).
+log_list() -> log_list(all).
+log_list(Type) -> call({log_list, Type}).
+
show() ->
call(show).
@@ -93,6 +96,8 @@ help() ->
io:format("rb:help() - print this help~n"),
io:format("rb:list() - list all reports~n"),
io:format("rb:list(Type) - list all reports of type Type~n"),
+ io:format("rb:log_list() - log list of all reports~n"),
+ io:format("rb:log_list(Type) - log list of all reports of type Type~n"),
io:format(" currently supported types are:~n"),
print_types(),
io:format("rb:grep(RegExp) - print reports containing RegExp.~n"),
@@ -113,7 +118,7 @@ help() ->
io:format("rb:show(Number) - print report no Number~n"),
io:format("rb:show(Type) - print all reports of type Type~n"),
io:format("rb:show() - print all reports~n"),
- io:format("rb:start_log(File) - redirect all reports to file~n"),
+ io:format("rb:start_log(File) - redirect all reports to file or io_device~n"),
io:format("rb:stop_log() - close the log file and redirect to~n"),
io:format(" standard_io~n"),
io:format("rb:stop - stop the rb_server~n").
@@ -207,7 +212,10 @@ handle_call({rescan, Options}, _From, State) ->
handle_call(_, _From, #state{data = undefined}) ->
{reply, {error, no_data}, #state{}};
handle_call({list, Type}, _From, State) ->
- print_list(State#state.data, Type),
+ print_list(standard_io, State#state.data, Type),
+ {reply, ok, State};
+handle_call({log_list, Type}, _From, State) ->
+ print_list(State#state.device, State#state.data, Type),
{reply, ok, State};
handle_call({start_log, FileName}, _From, State) ->
NewDevice = open_log_file(FileName),
@@ -262,7 +270,16 @@ code_change(_OldVsn, State, _Extra) ->
%% Returns: A Device for later use in call to io:format
%%-----------------------------------------------------------------
open_log_file(standard_io) -> standard_io;
-open_log_file(FileName) ->
+open_log_file(Fd) when is_atom(Fd),Fd=/=standard_error ->
+ case whereis(Fd) of
+ undefined -> io:format("rb: Registered name not found '~s'.~n",
+ [Fd]),
+ io:format("rb: Using standard_io~n"),
+ open_log_file(standard_io);
+ Pid -> open_log_file(Pid)
+ end;
+open_log_file(Fd) when is_pid(Fd)-> Fd;
+open_log_file(FileName) when is_list(FileName) ->
case file:open(FileName, [write,append]) of
{ok, Fd} -> Fd;
Error ->
@@ -270,7 +287,10 @@ open_log_file(FileName) ->
[FileName, Error]),
io:format("rb: Using standard_io~n"),
standard_io
- end.
+ end;
+open_log_file(standard_error) ->
+ io:format("rb: Using standard_io~n"),
+ standard_io.
close_device(Fd) when is_pid(Fd) ->
catch file:close(Fd);
@@ -550,18 +570,18 @@ local_time_to_universal_time({Date,Time}) ->
end.
-print_list(Data, Type) ->
+print_list(Fd, Data, Type) ->
Header = {"No", "Type", "Process", "Date Time"},
Width = find_width([Header | Data], 0)+1,
DateWidth = find_date_width([Header | Data], 0) +1,
Format = lists:concat(["~4s~20s ~", Width, "s~20s~n"]),
- io:format(Format, tuple_to_list(Header)),
- io:format(Format, ["==", "====", "=======", "==== ===="]),
- print_list(Data, Type, Width, DateWidth).
-print_list([], _, _, _) -> true;
-print_list([H|T], Type, Width, DateWidth) ->
- print_one_report(H, Type, Width, DateWidth),
- print_list(T, Type, Width, DateWidth).
+ io:format(Fd, Format, tuple_to_list(Header)),
+ io:format(Fd, Format, ["==", "====", "=======", "==== ===="]),
+ print_list(Fd, Data, Type, Width, DateWidth).
+print_list(_, [], _, _, _) -> true;
+print_list(Fd, [H|T], Type, Width, DateWidth) ->
+ print_one_report(Fd, H, Type, Width, DateWidth),
+ print_list(Fd, T, Type, Width, DateWidth).
find_width([], Width) -> Width;
find_width([H|T], Width) ->
@@ -578,22 +598,22 @@ find_date_width([H|T], Width) ->
true -> find_date_width(T, Width)
end.
-print_one_report({No, RealType, ShortDescr, Date, _Fname, _FilePos},
+print_one_report(Fd, {No, RealType, ShortDescr, Date, _Fname, _FilePos},
WantedType,
Width, DateWidth) ->
if
WantedType == all ->
- print_short_descr(No, RealType, ShortDescr, Date, Width,
+ print_short_descr(Fd, No, RealType, ShortDescr, Date, Width,
DateWidth);
WantedType == RealType ->
- print_short_descr(No, RealType, ShortDescr, Date, Width,
+ print_short_descr(Fd, No, RealType, ShortDescr, Date, Width,
DateWidth);
true -> ok
end.
-print_short_descr(No, Type, ShortDescr, Date, Width, DateWidth) ->
+print_short_descr(Fd, No, Type, ShortDescr, Date, Width, DateWidth) ->
Format = lists:concat(["~4w~20w ~", Width, "s~", DateWidth,"s~n"]),
- io:format(Format, [No,
+ io:format(Fd, Format, [No,
Type,
io_lib:format("~s", [ShortDescr]),
Date]).
diff --git a/lib/sasl/test/rb_SUITE.erl b/lib/sasl/test/rb_SUITE.erl
index b0e43be3a2..453f992850 100644
--- a/lib/sasl/test/rb_SUITE.erl
+++ b/lib/sasl/test/rb_SUITE.erl
@@ -362,18 +362,48 @@ start_stop_log(Config) ->
StdioResult = [_|_] = capture(fun() -> rb:show(1) end),
{ok,<<>>} = file:read_file(OutFile),
- %% Start log and check that show is printed to log and not to standad_io
+ %% Start log and check that show is printed to log and not to standard_io
ok = rb:start_log(OutFile),
[] = capture(fun() -> rb:show(1) end),
{ok,Bin} = file:read_file(OutFile),
true = (Bin =/= <<>>),
+ %% Start log with atom standard_io and check that show is printed to standard_io
+ ok = rb:stop_log(),
+ ok = file:write_file(OutFile,[]),
+ ok = rb:start_log(standard_io),
+ StdioResult = [_|_] = capture(fun() -> rb:show(1) end),
+ {ok,<<>>} = file:read_file(OutFile),
+
+ %% Start log and check that show is printed to iodevice log and not to standard_io
+ ok = rb:stop_log(),
+ ok = file:write_file(OutFile,[]),
+ {ok, IoOutFile} = file:open(OutFile,[write]),
+ ok = rb:start_log(IoOutFile),
+ [] = capture(fun() -> rb:show(1) end),
+ {ok,Bin} = file:read_file(OutFile),
+ true = (Bin =/= <<>>),
+ ok = file:close(IoOutFile),
+
%% Stop log and check that show is printed to standard_io and not to log
ok = rb:stop_log(),
ok = file:write_file(OutFile,[]),
StdioResult = capture(fun() -> rb:show(1) end),
{ok,<<>>} = file:read_file(OutFile),
+ %% Start log and check that list is printed to log and not to standard_io
+ ok = file:write_file(OutFile,[]),
+ ok = rb:start_log(OutFile),
+ [] = capture(fun() -> rb:log_list() end),
+ {ok,Bin2} = file:read_file(OutFile),
+ true = (Bin2 =/= <<>>),
+
+ %% Stop log and check that list is printed to standard_io and not to log
+ ok = rb:stop_log(),
+ ok = file:write_file(OutFile,[]),
+ StdioResult2 = capture(fun() -> rb:log_list() end),
+ {ok,<<>>} = file:read_file(OutFile),
+
%% Test that standard_io is used if log file can not be opened
ok = rb:start_log(filename:join(nonexistingdir,"newfile.txt")),
StdioResult = capture(fun() -> rb:show(1) end),
diff --git a/lib/ssh/doc/src/ssh.xml b/lib/ssh/doc/src/ssh.xml
index bd0d3d49dd..141d3df38e 100644
--- a/lib/ssh/doc/src/ssh.xml
+++ b/lib/ssh/doc/src/ssh.xml
@@ -88,6 +88,7 @@
number for SSH.</d>
<v>Options = [{Option, Value}]</v>
<v>Timeout = infinity | integer(milliseconds)</v>
+ <d>Negotiation timeout, for connection timeout use the option <c>{connect_timeout, timeout()}</c>.</d>
</type>
<desc>
<p>Connects to an SSH server. No channel is started. This is done
diff --git a/lib/ssh/src/ssh.hrl b/lib/ssh/src/ssh.hrl
index da5750b6c3..4fd347ba8f 100644
--- a/lib/ssh/src/ssh.hrl
+++ b/lib/ssh/src/ssh.hrl
@@ -127,7 +127,8 @@
userauth_supported_methods , %
userauth_methods,
userauth_preference,
- available_host_keys
+ available_host_keys,
+ authenticated = false
}).
-record(alg,
diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl
index df6175e27c..9de4dd5967 100644
--- a/lib/ssh/src/ssh_connection_handler.erl
+++ b/lib/ssh/src/ssh_connection_handler.erl
@@ -426,10 +426,10 @@ userauth(#ssh_msg_userauth_info_response{} = Msg,
language = "en"}, State)
end;
-userauth(#ssh_msg_userauth_success{}, #state{ssh_params = #ssh{role = client},
+userauth(#ssh_msg_userauth_success{}, #state{ssh_params = #ssh{role = client} = Ssh,
manager = Pid} = State) ->
Pid ! ssh_connected,
- {next_state, connected, next_packet(State)};
+ {next_state, connected, next_packet(State#state{ssh_params = Ssh#ssh{authenticated = true}})};
userauth(#ssh_msg_userauth_failure{},
#state{ssh_params = #ssh{role = client,
diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl
index beaffdc025..682d766d99 100644
--- a/lib/ssh/src/ssh_transport.erl
+++ b/lib/ssh/src/ssh_transport.erl
@@ -206,6 +206,7 @@ key_exchange_init_msg(Ssh0) ->
kex_init(#ssh{role = Role, opts = Opts, available_host_keys = HostKeyAlgs}) ->
Random = ssh_bits:random(16),
Compression = case proplists:get_value(compression, Opts, none) of
+ openssh_zlib -> ["[email protected]", "none"];
zlib -> ["zlib", "none"];
none -> ["none", "zlib"]
end,
@@ -855,13 +856,14 @@ decrypt(#ssh{decrypt = 'aes128-cbc', decrypt_keys = Key,
IV = crypto:next_iv(aes_cbc, Data),
{Ssh#ssh{decrypt_ctx = IV}, Dec}.
-
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Compression
%%
-%% none REQUIRED no compression
-%% zlib OPTIONAL ZLIB (LZ77) compression
+%% none REQUIRED no compression
+%% zlib OPTIONAL ZLIB (LZ77) compression
+%% openssh_zlib OPTIONAL ZLIB (LZ77) compression
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
compress_init(SSH) ->
compress_init(SSH, 1).
@@ -870,19 +872,32 @@ compress_init(#ssh{compress = none} = Ssh, _) ->
compress_init(#ssh{compress = zlib} = Ssh, Level) ->
Zlib = zlib:open(),
ok = zlib:deflateInit(Zlib, Level),
+ {ok, Ssh#ssh{compress_ctx = Zlib}};
+compress_init(#ssh{compress = '[email protected]'} = Ssh, Level) ->
+ Zlib = zlib:open(),
+ ok = zlib:deflateInit(Zlib, Level),
{ok, Ssh#ssh{compress_ctx = Zlib}}.
-
compress_final(#ssh{compress = none} = Ssh) ->
{ok, Ssh};
compress_final(#ssh{compress = zlib, compress_ctx = Context} = Ssh) ->
zlib:close(Context),
+ {ok, Ssh#ssh{compress = none, compress_ctx = undefined}};
+compress_final(#ssh{compress = '[email protected]', authenticated = false} = Ssh) ->
+ {ok, Ssh};
+compress_final(#ssh{compress = '[email protected]', compress_ctx = Context, authenticated = true} = Ssh) ->
+ zlib:close(Context),
{ok, Ssh#ssh{compress = none, compress_ctx = undefined}}.
compress(#ssh{compress = none} = Ssh, Data) ->
{Ssh, Data};
compress(#ssh{compress = zlib, compress_ctx = Context} = Ssh, Data) ->
Compressed = zlib:deflate(Context, Data, sync),
+ {Ssh, list_to_binary(Compressed)};
+compress(#ssh{compress = '[email protected]', authenticated = false} = Ssh, Data) ->
+ {Ssh, Data};
+compress(#ssh{compress = '[email protected]', compress_ctx = Context, authenticated = true} = Ssh, Data) ->
+ Compressed = zlib:deflate(Context, Data, sync),
{Ssh, list_to_binary(Compressed)}.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -894,18 +909,32 @@ decompress_init(#ssh{decompress = none} = Ssh) ->
decompress_init(#ssh{decompress = zlib} = Ssh) ->
Zlib = zlib:open(),
ok = zlib:inflateInit(Zlib),
+ {ok, Ssh#ssh{decompress_ctx = Zlib}};
+decompress_init(#ssh{decompress = '[email protected]'} = Ssh) ->
+ Zlib = zlib:open(),
+ ok = zlib:inflateInit(Zlib),
{ok, Ssh#ssh{decompress_ctx = Zlib}}.
decompress_final(#ssh{decompress = none} = Ssh) ->
{ok, Ssh};
decompress_final(#ssh{decompress = zlib, decompress_ctx = Context} = Ssh) ->
zlib:close(Context),
+ {ok, Ssh#ssh{decompress = none, decompress_ctx = undefined}};
+decompress_final(#ssh{decompress = '[email protected]', authenticated = false} = Ssh) ->
+ {ok, Ssh};
+decompress_final(#ssh{decompress = '[email protected]', decompress_ctx = Context, authenticated = true} = Ssh) ->
+ zlib:close(Context),
{ok, Ssh#ssh{decompress = none, decompress_ctx = undefined}}.
decompress(#ssh{decompress = none} = Ssh, Data) ->
{Ssh, Data};
decompress(#ssh{decompress = zlib, decompress_ctx = Context} = Ssh, Data) ->
Decompressed = zlib:inflate(Context, Data),
+ {Ssh, list_to_binary(Decompressed)};
+decompress(#ssh{decompress = '[email protected]', authenticated = false} = Ssh, Data) ->
+ {Ssh, Data};
+decompress(#ssh{decompress = '[email protected]', decompress_ctx = Context, authenticated = true} = Ssh, Data) ->
+ Decompressed = zlib:inflate(Context, Data),
{Ssh, list_to_binary(Decompressed)}.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl
index 93029c5038..0aa60624bf 100644
--- a/lib/ssh/test/ssh_basic_SUITE.erl
+++ b/lib/ssh/test/ssh_basic_SUITE.erl
@@ -48,8 +48,8 @@ all() ->
close].
groups() ->
- [{dsa_key, [], [send, exec, exec_compressed, shell, known_hosts, idle_time, rekey]},
- {rsa_key, [], [send, exec, exec_compressed, shell, known_hosts, idle_time, rekey]},
+ [{dsa_key, [], [send, exec, exec_compressed, shell, known_hosts, idle_time, rekey, openssh_zlib_basic_test]},
+ {rsa_key, [], [send, exec, exec_compressed, shell, known_hosts, idle_time, rekey, openssh_zlib_basic_test]},
{dsa_pass_key, [], [pass_phrase]},
{rsa_pass_key, [], [pass_phrase]},
{internal_error, [], [internal_error]}
@@ -493,7 +493,24 @@ close(Config) when is_list(Config) ->
exit(CM, {shutdown, normal}),
ok = ssh:close(CM).
-
+
+openssh_zlib_basic_test() ->
+ [{doc, "Test basic connection with openssh_zlib"}].
+openssh_zlib_basic_test(Config) ->
+ SystemDir = filename:join(?config(priv_dir, Config), system),
+ UserDir = ?config(priv_dir, Config),
+
+ {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
+ {user_dir, UserDir},
+ {failfun, fun ssh_test_lib:failfun/2}]),
+ ConnectionRef =
+ ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+ {user_dir, UserDir},
+ {user_interaction, false},
+ {compression, openssh_zlib}]),
+ ok = ssh:close(ConnectionRef),
+ ssh:stop_daemon(Pid).
+
%%--------------------------------------------------------------------
%% Internal functions ------------------------------------------------
%%--------------------------------------------------------------------
diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml
index 8875d07535..301ff21068 100644
--- a/lib/ssl/doc/src/notes.xml
+++ b/lib/ssl/doc/src/notes.xml
@@ -25,7 +25,6 @@
<file>notes.xml</file>
</header>
<p>This document describes the changes made to the SSL application.</p>
-
<section><title>SSL 5.3</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -100,7 +99,6 @@
</section>
<section><title>SSL 5.2.1</title>
-
<section><title>Improvements and New Features</title>
<list>
<item>
@@ -126,9 +124,20 @@
</section>
</section>
-
+<section><title>SSL 5.1.2.1</title>
+<section><title>Improvements and New Features</title>
+<list>
+ <item>
+ <p>
+ Make log_alert configurable as option in ssl, SSLLogLevel
+ added as option to inets conf file</p>
+ <p>
+ Own Id: OTP-11259</p>
+ </item>
+</list>
+</section>
+</section>
<section><title>SSL 5.2</title>
-
<section><title>Fixed Bugs and Malfunctions</title>
<list>
<item>
diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml
index 1645eb15f3..6029a09730 100644
--- a/lib/ssl/doc/src/ssl.xml
+++ b/lib/ssl/doc/src/ssl.xml
@@ -86,7 +86,8 @@
{user_lookup_fun, {fun(), term()}}, {psk_identity, string()}, {srp_identity, {string(), string()}} |
{ssl_imp, ssl_imp()} | {reuse_sessions, boolean()} | {reuse_session, fun()}
{next_protocols_advertised, [binary()]} |
- {client_preferred_next_protocols, client | server, [binary()]}
+ {client_preferred_next_protocols, client | server, [binary()]} |
+ {log_alert, boolean()}
</c></p>
<p><c>transportoption() = {cb_info, {CallbackModule::atom(), DataTag::atom(), ClosedTag::atom(), ErrTag:atom()}}
@@ -446,7 +447,8 @@ fun(srp, Username :: string(), UserState :: term()) ->
<tag>{psk_identity, string()}</tag>
<item>Specifies the server identity hint the server presents to the client.
</item>
-
+ <tag>{log_alert, boolean()}</tag>
+ <item>If false, error reports will not be displayed.</item>
</taglist>
</section>
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index 0c1e47311d..dc6898d001 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -219,4 +219,3 @@ format_error(Error) ->
random_bytes(N) ->
tls:random_bytes(N).
-
diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl
index 14db4a6067..de8d20d399 100644
--- a/lib/ssl/src/ssl_internal.hrl
+++ b/lib/ssl/src/ssl_internal.hrl
@@ -111,7 +111,8 @@
%% This option should only be set to true by inet_tls_dist
erl_dist = false,
next_protocols_advertised = undefined, %% [binary()],
- next_protocol_selector = undefined %% fun([binary()]) -> binary())
+ next_protocol_selector = undefined, %% fun([binary()]) -> binary())
+ log_alert
}).
-record(socket_options,
diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl
index 7af4a68461..4d5eaeb607 100644
--- a/lib/ssl/src/ssl_manager.erl
+++ b/lib/ssl/src/ssl_manager.erl
@@ -30,7 +30,7 @@
lookup_trusted_cert/4,
new_session_id/1, clean_cert_db/2,
register_session/2, register_session/3, invalidate_session/2,
- invalidate_session/3, clear_pem_cache/0]).
+ invalidate_session/3, clear_pem_cache/0, manager_name/1]).
% Spawn export
-export([init_session_validator/1]).
@@ -64,6 +64,18 @@
%%====================================================================
%% API
%%====================================================================
+
+%%--------------------------------------------------------------------
+-spec manager_name(normal | dist) -> atom().
+%%
+%% Description: Returns the registered name of the ssl manager process
+%% in the operation modes 'normal' and 'dist'.
+%%--------------------------------------------------------------------
+manager_name(normal) ->
+ ?MODULE;
+manager_name(dist) ->
+ list_to_atom(atom_to_list(?MODULE) ++ "dist").
+
%%--------------------------------------------------------------------
-spec start_link(list()) -> {ok, pid()} | ignore | {error, term()}.
%%
@@ -71,7 +83,8 @@
%% and certificate caching.
%%--------------------------------------------------------------------
start_link(Opts) ->
- gen_server:start_link({local, ?MODULE}, ?MODULE, [?MODULE, Opts], []).
+ DistMangerName = manager_name(normal),
+ gen_server:start_link({local, DistMangerName}, ?MODULE, [DistMangerName, Opts], []).
%%--------------------------------------------------------------------
-spec start_link_dist(list()) -> {ok, pid()} | ignore | {error, term()}.
@@ -80,7 +93,8 @@ start_link(Opts) ->
%% be used by the erlang distribution. Note disables soft upgrade!
%%--------------------------------------------------------------------
start_link_dist(Opts) ->
- gen_server:start_link({local, ssl_manager_dist}, ?MODULE, [ssl_manager_dist, Opts], []).
+ DistMangerName = manager_name(dist),
+ gen_server:start_link({local, DistMangerName}, ?MODULE, [DistMangerName, Opts], []).
%%--------------------------------------------------------------------
-spec connection_init(binary()| {der, list()}, client | server) ->
@@ -100,7 +114,7 @@ connection_init(Trustedcerts, Role) ->
%%--------------------------------------------------------------------
-spec cache_pem_file(binary(), term()) -> {ok, term()} | {error, reason()}.
%%
-%% Description: Cach a pem file and return its content.
+%% Description: Cache a pem file and return its content.
%%--------------------------------------------------------------------
cache_pem_file(File, DbHandle) ->
MD5 = crypto:hash(md5, File),
@@ -120,7 +134,7 @@ cache_pem_file(File, DbHandle) ->
%%--------------------------------------------------------------------
clear_pem_cache() ->
%% Not supported for distribution at the moement, should it be?
- put(ssl_manager, ssl_manager),
+ put(ssl_manager, manager_name(normal)),
call(unconditionally_clear_pem_cache).
%%--------------------------------------------------------------------
diff --git a/lib/ssl/src/tls.erl b/lib/ssl/src/tls.erl
index bb02695c12..b220a48f73 100644
--- a/lib/ssl/src/tls.erl
+++ b/lib/ssl/src/tls.erl
@@ -663,7 +663,8 @@ handle_options(Opts0, _Role) ->
handle_option(next_protocols_advertised, Opts, undefined),
next_protocol_selector =
make_next_protocol_selector(
- handle_option(client_preferred_next_protocols, Opts, undefined))
+ handle_option(client_preferred_next_protocols, Opts, undefined)),
+ log_alert = handle_option(log_alert, Opts, true)
},
CbInfo = proplists:get_value(cb_info, Opts, {gen_tcp, tcp, tcp_closed, tcp_error}),
@@ -675,7 +676,7 @@ handle_options(Opts0, _Role) ->
reuse_session, reuse_sessions, ssl_imp,
cb_info, renegotiate_at, secure_renegotiate, hibernate_after,
erl_dist, next_protocols_advertised,
- client_preferred_next_protocols],
+ client_preferred_next_protocols, log_alert],
SockOpts = lists:foldl(fun(Key, PropList) ->
proplists:delete(Key, PropList)
@@ -840,6 +841,9 @@ validate_option(client_preferred_next_protocols = Opt, {Precedence, PreferredPro
validate_option(client_preferred_next_protocols, undefined) ->
undefined;
+validate_option(log_alert, Value) when Value == true;
+ Value == false ->
+ Value;
validate_option(next_protocols_advertised = Opt, Value) when is_list(Value) ->
case tls_record:highest_protocol_version([]) of
{3,0} ->
diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl
index 51551eab11..77f49c5d2a 100644
--- a/lib/ssl/src/tls_connection.erl
+++ b/lib/ssl/src/tls_connection.erl
@@ -89,7 +89,6 @@
cert_db_ref, % ref()
bytes_to_read, % integer(), # bytes to read in passive mode
user_data_buffer, % binary()
- log_alert, % boolean()
renegotiation, % {boolean(), From | internal | peer}
start_or_recv_from, % "gen_fsm From"
timer, % start_or_recv_timer
@@ -1240,9 +1239,9 @@ ssl_init(SslOpts, Role) ->
{ok, CertDbRef, CertDbHandle, FileRefHandle, CacheHandle, OwnCert, PrivateKey, DHParams}.
init_manager_name(false) ->
- put(ssl_manager, ssl_manager);
+ put(ssl_manager, ssl_manager:manager_name(normal));
init_manager_name(true) ->
- put(ssl_manager, ssl_manager_dist).
+ put(ssl_manager, ssl_manager:manager_name(dist)).
init_certificates(#ssl_options{cacerts = CaCerts,
cacertfile = CACertFile,
@@ -2679,7 +2678,6 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User,
tls_cipher_texts = [],
user_application = {Monitor, User},
user_data_buffer = <<>>,
- log_alert = true,
session_cache_cb = SessionCacheCb,
renegotiation = {false, first},
start_or_recv_from = undefined,
@@ -2780,12 +2778,11 @@ handle_alerts([Alert | Alerts], {next_state, StateName, State, _Timeout}) ->
handle_alerts(Alerts, handle_alert(Alert, StateName, State)).
handle_alert(#alert{level = ?FATAL} = Alert, StateName,
- #state{socket = Socket, transport_cb = Transport,
- start_or_recv_from = From, host = Host,
+ #state{socket = Socket, transport_cb = Transport, ssl_options = SslOpts, start_or_recv_from = From, host = Host,
port = Port, session = Session, user_application = {_Mon, Pid},
- log_alert = Log, role = Role, socket_options = Opts} = State) ->
+ role = Role, socket_options = Opts} = State) ->
invalidate_session(Role, Host, Port, Session),
- log_alert(Log, StateName, Alert),
+ log_alert(SslOpts#ssl_options.log_alert, StateName, Alert),
alert_user(Transport, Socket, StateName, Opts, Pid, From, Alert, Role),
{stop, normal, State};
@@ -2795,21 +2792,21 @@ handle_alert(#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} = Alert,
{stop, {shutdown, peer_close}, State};
handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName,
- #state{log_alert = Log, renegotiation = {true, internal}} = State) ->
- log_alert(Log, StateName, Alert),
+ #state{ssl_options = SslOpts, renegotiation = {true, internal}} = State) ->
+ log_alert(SslOpts#ssl_options.log_alert, StateName, Alert),
handle_normal_shutdown(Alert, StateName, State),
{stop, {shutdown, peer_close}, State};
handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName,
- #state{log_alert = Log, renegotiation = {true, From}} = State0) ->
- log_alert(Log, StateName, Alert),
+ #state{ssl_options = SslOpts, renegotiation = {true, From}} = State0) ->
+ log_alert(SslOpts#ssl_options.log_alert, StateName, Alert),
gen_fsm:reply(From, {error, renegotiation_rejected}),
{Record, State} = next_record(State0),
next_state(StateName, connection, Record, State);
handle_alert(#alert{level = ?WARNING, description = ?USER_CANCELED} = Alert, StateName,
- #state{log_alert = Log} = State0) ->
- log_alert(Log, StateName, Alert),
+ #state{ssl_options = SslOpts} = State0) ->
+ log_alert(SslOpts#ssl_options.log_alert, StateName, Alert),
{Record, State} = next_record(State0),
next_state(StateName, StateName, Record, State).
@@ -2847,7 +2844,7 @@ handle_own_alert(Alert, Version, StateName,
#state{transport_cb = Transport,
socket = Socket,
connection_states = ConnectionStates,
- log_alert = Log} = State) ->
+ ssl_options = SslOpts} = State) ->
try %% Try to tell the other side
{BinMsg, _} =
encode_alert(Alert, Version, ConnectionStates),
@@ -2857,7 +2854,7 @@ handle_own_alert(Alert, Version, StateName,
ignore
end,
try %% Try to tell the local user
- log_alert(Log, StateName, Alert),
+ log_alert(SslOpts#ssl_options.log_alert, StateName, Alert),
handle_normal_shutdown(Alert,StateName, State)
catch _:_ ->
ok
diff --git a/lib/stdlib/doc/src/io_lib.xml b/lib/stdlib/doc/src/io_lib.xml
index 3dac259477..68352ffeb1 100644
--- a/lib/stdlib/doc/src/io_lib.xml
+++ b/lib/stdlib/doc/src/io_lib.xml
@@ -54,6 +54,9 @@
<name name="fread_error"/>
</datatype>
<datatype>
+ <name name="fread_item"/>
+ </datatype>
+ <datatype>
<name name="latin1_string"/>
</datatype>
</datatypes>
diff --git a/lib/stdlib/src/dets.erl b/lib/stdlib/src/dets.erl
index 50812cc532..68b157c13c 100644
--- a/lib/stdlib/src/dets.erl
+++ b/lib/stdlib/src/dets.erl
@@ -951,10 +951,10 @@ do_trav(Proc, Acc, Fun) ->
Error
end.
-do_trav(#dets_cont{bin = eof}, _Proc, Acc, _Fun) ->
- Acc;
do_trav(State, Proc, Acc, Fun) ->
case req(Proc, {match_init, State, safe}) of
+ '$end_of_table'->
+ Acc;
{cont, {Bins, NewState}} ->
do_trav_bins(NewState, Proc, Acc, Fun, lists:reverse(Bins));
Error ->
diff --git a/lib/stdlib/src/digraph.erl b/lib/stdlib/src/digraph.erl
index e3f87d2c57..78f74631dc 100644
--- a/lib/stdlib/src/digraph.erl
+++ b/lib/stdlib/src/digraph.erl
@@ -36,7 +36,7 @@
-export([get_short_path/3, get_short_cycle/2]).
--export_type([digraph/0, d_type/0, vertex/0]).
+-export_type([digraph/0, d_type/0, vertex/0, edge/0]).
-record(digraph, {vtab = notable :: ets:tab(),
etab = notable :: ets:tab(),
diff --git a/lib/stdlib/src/edlin.erl b/lib/stdlib/src/edlin.erl
index 3192879f09..f5998c54fd 100644
--- a/lib/stdlib/src/edlin.erl
+++ b/lib/stdlib/src/edlin.erl
@@ -79,6 +79,14 @@ edit([C|Cs], P, {Bef,Aft}, Prefix, Rs0) ->
case key_map(C, Prefix) of
meta ->
edit(Cs, P, {Bef,Aft}, meta, Rs0);
+ meta_o ->
+ edit(Cs, P, {Bef,Aft}, meta_o, Rs0);
+ meta_csi ->
+ edit(Cs, P, {Bef,Aft}, meta_csi, Rs0);
+ meta_meta ->
+ edit(Cs, P, {Bef,Aft}, meta_meta, Rs0);
+ {csi, _} = Csi ->
+ edit(Cs, P, {Bef,Aft}, Csi, Rs0);
meta_left_sq_bracket ->
edit(Cs, P, {Bef,Aft}, meta_left_sq_bracket, Rs0);
search_meta ->
@@ -178,6 +186,7 @@ key_map($\^U, none) -> ctlu;
key_map($\^], none) -> auto_blink;
key_map($\^X, none) -> ctlx;
key_map($\^Y, none) -> yank;
+key_map($\^W, none) -> backward_kill_word;
key_map($\e, none) -> meta;
key_map($), Prefix) when Prefix =/= meta,
Prefix =/= search,
@@ -198,11 +207,29 @@ key_map($d, meta) -> kill_word;
key_map($f, meta) -> forward_word;
key_map($t, meta) -> transpose_word;
key_map($y, meta) -> yank_pop;
+key_map($O, meta) -> meta_o;
+key_map($H, meta_o) -> beginning_of_line;
+key_map($F, meta_o) -> end_of_line;
key_map($\177, none) -> backward_delete_char;
key_map($\177, meta) -> backward_kill_word;
key_map($[, meta) -> meta_left_sq_bracket;
key_map($D, meta_left_sq_bracket) -> backward_char;
key_map($C, meta_left_sq_bracket) -> forward_char;
+% support a few <CTRL>+<CURSOR LEFT|RIGHT> combinations...
+% - forward: \e\e[C, \e[5C, \e[1;5C
+% - backward: \e\e[D, \e[5D, \e[1;5D
+key_map($\e, meta) -> meta_meta;
+key_map($[, meta_meta) -> meta_csi;
+key_map($C, meta_csi) -> forward_word;
+key_map($D, meta_csi) -> backward_word;
+key_map($1, meta_left_sq_bracket) -> {csi, "1"};
+key_map($5, meta_left_sq_bracket) -> {csi, "5"};
+key_map($5, {csi, "1;"}) -> {csi, "1;5"};
+key_map($C, {csi, "5"}) -> forward_word;
+key_map($C, {csi, "1;5"}) -> forward_word;
+key_map($D, {csi, "5"}) -> backward_word;
+key_map($D, {csi, "1;5"}) -> backward_word;
+key_map($;, {csi, "1"}) -> {csi, "1;"};
key_map(C, none) when C >= $\s ->
{insert,C};
%% for search, we need smarter line handling and so
@@ -363,6 +390,9 @@ do_op(end_of_line, Bef, [C|Aft], Rs) ->
{{reverse(Aft, [C|Bef]),[]},[{move_rel,length(Aft)+1}|Rs]};
do_op(end_of_line, Bef, [], Rs) ->
{{Bef,[]},Rs};
+do_op(ctlu, Bef, Aft, Rs) ->
+ put(kill_buffer, Bef),
+ {{[], Aft}, [{delete_chars, -length(Bef)} | Rs]};
do_op(beep, Bef, Aft, Rs) ->
{{Bef,Aft},[beep|Rs]};
do_op(_, Bef, Aft, Rs) ->
diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl
index 08b8541014..8f07750b9b 100644
--- a/lib/stdlib/src/erl_lint.erl
+++ b/lib/stdlib/src/erl_lint.erl
@@ -3219,7 +3219,8 @@ modify_line(T, F0) ->
%% Forms.
modify_line1({function,F,A}, _Mf) -> {function,F,A};
-modify_line1({function,M,F,A}, _Mf) -> {function,M,F,A};
+modify_line1({function,M,F,A}, Mf) ->
+ {function,modify_line1(M, Mf),modify_line1(F, Mf),modify_line1(A, Mf)};
modify_line1({attribute,L,record,{Name,Fields}}, Mf) ->
{attribute,Mf(L),record,{Name,modify_line1(Fields, Mf)}};
modify_line1({attribute,L,spec,{Fun,Types}}, Mf) ->
diff --git a/lib/stdlib/src/io_lib.erl b/lib/stdlib/src/io_lib.erl
index a9b6d4131e..92a086b077 100644
--- a/lib/stdlib/src/io_lib.erl
+++ b/lib/stdlib/src/io_lib.erl
@@ -83,7 +83,8 @@
-export([write_unicode_string/1, write_unicode_char/1,
deep_unicode_char_list/1]).
--export_type([chars/0, latin1_string/0, continuation/0, fread_error/0]).
+-export_type([chars/0, latin1_string/0, continuation/0,
+ fread_error/0, fread_item/0]).
%%----------------------------------------------------------------------
@@ -106,6 +107,8 @@
| 'string'
| 'unsigned'.
+-type fread_item() :: string() | atom() | integer() | float().
+
%%----------------------------------------------------------------------
%% Interface calls to sub-modules.
@@ -120,7 +123,7 @@ fwrite(Format, Args) ->
-spec fread(Format, String) -> Result when
Format :: string(),
String :: string(),
- Result :: {'ok', InputList :: [term()], LeftOverChars :: string()}
+ Result :: {'ok', InputList :: [fread_item()], LeftOverChars :: string()}
| {'more', RestFormat :: string(),
Nchars :: non_neg_integer(),
InputStack :: chars()}
@@ -135,7 +138,7 @@ fread(Chars, Format) ->
Format :: string(),
Return :: {'more', Continuation1 :: continuation()}
| {'done', Result, LeftOverChars :: string()},
- Result :: {'ok', InputList :: [term()]}
+ Result :: {'ok', InputList :: [fread_item()]}
| 'eof'
| {'error', {'fread', What :: fread_error()}}.
diff --git a/lib/stdlib/src/io_lib_fread.erl b/lib/stdlib/src/io_lib_fread.erl
index 92a34995b8..491e1f40d7 100644
--- a/lib/stdlib/src/io_lib_fread.erl
+++ b/lib/stdlib/src/io_lib_fread.erl
@@ -41,9 +41,9 @@
Format :: string(),
Return :: {'more', Continuation1 :: io_lib:continuation()}
| {'done', Result, LeftOverChars :: string()},
- Result :: {'ok', InputList :: io_lib:chars()}
+ Result :: {'ok', InputList :: [io_lib:fread_item()]}
| 'eof'
- | {'error', {'read', What :: io_lib:fread_error()}}.
+ | {'error', {'fread', What :: io_lib:fread_error()}}.
fread([], Chars, Format) ->
%%io:format("FREAD: ~w `~s'~n", [Format,Chars]),
@@ -101,11 +101,12 @@ fread_line(Format0, Line, N0, Results0, More, Newline) ->
-spec fread(Format, String) -> Result when
Format :: string(),
String :: string(),
- Result :: {'ok', InputList :: io_lib:chars(), LeftOverChars :: string()}
+ Result :: {'ok', InputList :: [io_lib:fread_item()],
+ LeftOverChars :: string()}
| {'more', RestFormat :: string(),
Nchars :: non_neg_integer(),
InputStack :: io_lib:chars()}
- | {'error', What :: term()}.
+ | {'error', {'fread', What :: io_lib:fread_error()}}.
fread(Format, Line) ->
fread(Format, Line, 0, []).
diff --git a/lib/stdlib/test/dets_SUITE.erl b/lib/stdlib/test/dets_SUITE.erl
index 8ff7c3ccc9..059d553b00 100644
--- a/lib/stdlib/test/dets_SUITE.erl
+++ b/lib/stdlib/test/dets_SUITE.erl
@@ -52,7 +52,7 @@
simultaneous_open/1, insert_new/1, repair_continuation/1,
otp_5487/1, otp_6206/1, otp_6359/1, otp_4738/1, otp_7146/1,
otp_8070/1, otp_8856/1, otp_8898/1, otp_8899/1, otp_8903/1,
- otp_8923/1, otp_9282/1]).
+ otp_8923/1, otp_9282/1, otp_11245/1]).
-export([dets_dirty_loop/0]).
@@ -109,7 +109,7 @@ all() ->
many_clients, otp_4906, otp_5402, simultaneous_open,
insert_new, repair_continuation, otp_5487, otp_6206,
otp_6359, otp_4738, otp_7146, otp_8070, otp_8856, otp_8898,
- otp_8899, otp_8903, otp_8923, otp_9282
+ otp_8899, otp_8903, otp_8923, otp_9282, otp_11245
].
groups() ->
@@ -3898,6 +3898,28 @@ some_calls(Tab, Config) ->
file:delete(File).
+otp_11245(doc) ->
+ ["OTP-11245. Tables remained fixed after traversal"];
+otp_11245(suite) ->
+ [];
+otp_11245(Config) when is_list(Config) ->
+ Tab = otp_11245,
+ File = filename(Tab, Config),
+ {ok, Tab} = dets:open_file(Tab, [{file,File}]),
+ N = 1024,
+ ins(Tab, N),
+ N = length(dets:match(Tab, '_')),
+ false = dets:info(Tab, safe_fixed),
+ dets:traverse(Tab, fun(_) -> continue end),
+ false = dets:info(Tab, safe_fixed),
+ N = dets:foldl(fun(_, N2) -> N2+1 end, 0, Tab),
+ false = dets:info(Tab, safe_fixed),
+ N = dets:foldr(fun(_, N2) -> N2+1 end, 0, Tab),
+ false = dets:info(Tab, safe_fixed),
+ ok = dets:close(Tab),
+ file:delete(File),
+ ok.
+
%%
%% Parts common to several test cases
%%
diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl
index f8345559c4..4dc7a44064 100644
--- a/lib/stdlib/test/erl_lint_SUITE.erl
+++ b/lib/stdlib/test/erl_lint_SUITE.erl
@@ -50,7 +50,7 @@
unsafe_vars_try/1,
guard/1, otp_4886/1, otp_4988/1, otp_5091/1, otp_5276/1, otp_5338/1,
otp_5362/1, otp_5371/1, otp_7227/1, otp_5494/1, otp_5644/1, otp_5878/1,
- otp_5917/1, otp_6585/1, otp_6885/1, otp_10436/1,
+ otp_5917/1, otp_6585/1, otp_6885/1, otp_10436/1, otp_11254/1,
export_all/1,
bif_clash/1,
behaviour_basic/1, behaviour_multiple/1,
@@ -82,7 +82,7 @@ all() ->
unsafe_vars, unsafe_vars2, unsafe_vars_try, guard,
otp_4886, otp_4988, otp_5091, otp_5276, otp_5338,
otp_5362, otp_5371, otp_7227, otp_5494, otp_5644,
- otp_5878, otp_5917, otp_6585, otp_6885, otp_10436, export_all,
+ otp_5878, otp_5917, otp_6585, otp_6885, otp_10436, otp_11254,export_all,
bif_clash, behaviour_basic, behaviour_multiple,
otp_7550, otp_8051, format_warn, {group, on_load},
too_many_arguments, basic_errors, bin_syntax_errors].
@@ -2418,6 +2418,20 @@ otp_10436(Config) when is_list(Config) ->
run_test2(Config, Ts2, []),
ok.
+otp_11254(doc) ->
+ "OTP-11254. Warnings for opaque types.";
+otp_11254(suite) -> [];
+otp_11254(Config) when is_list(Config) ->
+ Ts = <<"-module(p2).
+ -export([manifest/2]).
+ manifest(Module, Name) ->
+ fun Module:Nine/1.
+ ">>,
+ {error,[{4,erl_lint,{unbound_var,'Nine'}}],
+ [{3,erl_lint,{unused_var,'Name'}}]} =
+ run_test2(Config, Ts, []),
+ ok.
+
export_all(doc) ->
"OTP-7392. Warning for export_all.";
export_all(Config) when is_list(Config) ->
diff --git a/lib/tools/emacs/erlang.el b/lib/tools/emacs/erlang.el
index 3ecda1d285..624042204c 100644
--- a/lib/tools/emacs/erlang.el
+++ b/lib/tools/emacs/erlang.el
@@ -5368,7 +5368,7 @@ There exists two workarounds for this bug:
(inferior-erlang-prepare-for-input)
(let* ((dir (inferior-erlang-compile-outdir))
;;; (file (file-name-nondirectory (buffer-file-name)))
- (noext (substring (buffer-file-name) 0 -4))
+ (noext (substring (erlang-local-buffer-file-name) 0 -4))
(opts (append (list (cons 'outdir dir))
(if current-prefix-arg
(list 'debug_info 'export_all))
@@ -5406,7 +5406,7 @@ unless the optional NO-DISPLAY is non-nil."
(defun inferior-erlang-compile-outdir ()
"Return the directory to compile the current buffer into."
(let* ((buffer-dir (directory-file-name
- (file-name-directory (buffer-file-name))))
+ (file-name-directory (erlang-local-buffer-file-name))))
(parent-dir (directory-file-name
(file-name-directory buffer-dir)))
(ebin-dir (concat (file-name-as-directory parent-dir) "ebin"))
@@ -5424,11 +5424,11 @@ unless the optional NO-DISPLAY is non-nil."
(res (inferior-erlang-compute-erl-compile-command module-name opts))
ccfn-entry
done)
- (if (not (null (buffer-file-name)))
+ (if (not (null (erlang-local-buffer-file-name)))
(while (and (not done) (not (null ccfn)))
(setq ccfn-entry (car ccfn))
(setq ccfn (cdr ccfn))
- (if (string-match (car ccfn-entry) (buffer-file-name))
+ (if (string-match (car ccfn-entry) (erlang-local-buffer-file-name))
(let ((c-fn (cdr ccfn-entry)))
(setq done t)
(if (not (null c-fn))
@@ -5460,7 +5460,7 @@ unless the optional NO-DISPLAY is non-nil."
tmpvar tmpvar tmpvar2)))))
(defun inferior-erlang-compute-leex-compile-command (module-name opts)
- (let ((file-name (buffer-file-name))
+ (let ((file-name (erlang-local-buffer-file-name))
(erl-compile-expr (inferior-erlang-remove-any-trailing-dot
(inferior-erlang-compute-erl-compile-command
module-name opts))))
@@ -5479,7 +5479,7 @@ unless the optional NO-DISPLAY is non-nil."
erl-compile-expr)))
(defun inferior-erlang-compute-yecc-compile-command (module-name opts)
- (let ((file-name (buffer-file-name))
+ (let ((file-name (erlang-local-buffer-file-name))
(erl-compile-expr (inferior-erlang-remove-any-trailing-dot
(inferior-erlang-compute-erl-compile-command
module-name opts))))
@@ -5530,6 +5530,36 @@ unless the optional NO-DISPLAY is non-nil."
(setq strs (cdr strs)))
result))
+(defun erlang-local-buffer-file-name ()
+ ;; When editing a file remotely via tramp,
+ ;; the buffer's file name may be for example
+ ;; "/ssh:host.example.com:/some/path/x.erl"
+ ;;
+ ;; If I try to compile such a file using C-c C-k, an
+ ;; erlang shell on the remote host is automatically
+ ;; started if needed, but for it to successfully compile
+ ;; the file, the c(...) command that is sent must contain
+ ;; the file name "/some/path/x.erl" without the
+ ;; tramp-prefix "/ssh:host.example.com:".
+ (cond ((null (buffer-file-name))
+ nil)
+ ((erlang-tramp-remote-file-p)
+ (erlang-tramp-get-localname))
+ (t
+ (buffer-file-name))))
+
+(defun erlang-tramp-remote-file-p ()
+ (and (fboundp 'tramp-tramp-file-p)
+ (tramp-tramp-file-p (buffer-file-name))))
+
+(defun erlang-tramp-get-localname ()
+ (let ((tramp-info (tramp-dissect-file-name (buffer-file-name))))
+ (if (fboundp 'tramp-file-name-localname)
+ (tramp-file-name-localname tramp-info)
+ ;; In old versions of tramp, it was `tramp-file-name-path'
+ ;; instead of the newer `tramp-file-name-localname'
+ (tramp-file-name-path tramp-info))))
+
;; `next-error' only accepts buffers with major mode `compilation-mode'
;; or with the minor mode `compilation-minor-mode' activated.
;; (To activate the minor mode is out of the question, since it will
@@ -5564,7 +5594,7 @@ Capable of finding error messages in an inferior Erlang buffer."
"Make the inferior Erlang change directory.
The default is to go to the directory of the current buffer."
(interactive)
- (or dir (setq dir (file-name-directory (buffer-file-name))))
+ (or dir (setq dir (file-name-directory (erlang-local-buffer-file-name))))
(or (inferior-erlang-running-p)
(error "No inferior Erlang is running"))
(inferior-erlang-display-buffer)