From a9ddb1e0f116e8fad9b13c7ba7884472c00c2667 Mon Sep 17 00:00:00 2001
From: Dan Gudmundsson <dgud@erlang.org>
Date: Fri, 17 Aug 2018 08:59:48 +0200
Subject: Clean up bag index tables

Index records for bag tables with ram_copies was not deleted
after "real" objects where deleted and thus a memory leak.
---
 lib/mnesia/src/mnesia_index.erl               |  2 +-
 lib/mnesia/test/mnesia_evil_coverage_test.erl | 58 +++++++++++++++++++++++++--
 2 files changed, 56 insertions(+), 4 deletions(-)

diff --git a/lib/mnesia/src/mnesia_index.erl b/lib/mnesia/src/mnesia_index.erl
index d121bd01e9..f8b3b9cd02 100644
--- a/lib/mnesia/src/mnesia_index.erl
+++ b/lib/mnesia/src/mnesia_index.erl
@@ -155,7 +155,7 @@ del_object_bag_([IxK|IxKs], Found, Type, Tab, Key, Obj, Ixt) ->
 		bag ->
                     db_match_erase(Ixt, {IxK, Key});
 		ordered ->
-		    db_erase(Ixt, {{IxK, Key}})
+		    db_erase(Ixt, {IxK, Key})
 	    end;
         _ ->
 	    ok
diff --git a/lib/mnesia/test/mnesia_evil_coverage_test.erl b/lib/mnesia/test/mnesia_evil_coverage_test.erl
index 506b1a9372..1a561154a7 100644
--- a/lib/mnesia/test/mnesia_evil_coverage_test.erl
+++ b/lib/mnesia/test/mnesia_evil_coverage_test.erl
@@ -35,7 +35,7 @@
          replica_management/1, clear_table_during_load/1,
          schema_availability/1, local_content/1,
          replica_location/1, user_properties/1, unsupp_user_props/1,
-         sorted_ets/1,
+         sorted_ets/1, index_cleanup/1,
          change_table_access_mode/1, change_table_load_order/1,
          set_master_nodes/1, offline_set_master_nodes/1,
          dump_tables/1, dump_log/1, wait_for_tables/1, force_load_table/1,
@@ -49,7 +49,7 @@
          record_name_dirty_access_disc_only/1,
          record_name_dirty_access_xets/1]).
 
--export([info_check/8]).
+-export([info_check/8, index_size/1]).
 
 -define(cleanup(N, Config),
 	mnesia_test_lib:prepare_test_case([{reload_appls, [mnesia]}],
@@ -73,7 +73,7 @@ all() ->
      {group, table_sync}, user_properties, unsupp_user_props,
      {group, record_name}, {group, snmp_access},
      {group, subscriptions}, {group, iteration},
-     {group, debug_support}, sorted_ets,
+     {group, debug_support}, sorted_ets, index_cleanup,
      {mnesia_dirty_access_test, all},
      {mnesia_trans_access_test, all},
      {mnesia_evil_backup, all}].
@@ -2559,3 +2559,55 @@ sorted_ets(Config) when is_list(Config) ->
     ?match({atomic, [{rec,1,1}, {rec,2,1}]}, mnesia:transaction(TestIt)).
 
 
+index_cleanup(Config) when is_list(Config) ->
+    [N1, N2] = All = ?acquire_nodes(2, Config),
+    ?match({atomic, ok}, mnesia:create_table(i_set, [{type, set}, {ram_copies, [N1]}, {index, [val]},
+                                                     {disc_only_copies, [N2]}])),
+    ?match({atomic, ok}, mnesia:create_table(i_bag, [{type, bag}, {ram_copies, [N1]}, {index, [val]},
+                                                     {disc_only_copies, [N2]}])),
+    ?match({atomic, ok}, mnesia:create_table(i_oset, [{type, ordered_set}, {ram_copies, [N1, N2]},
+                                                      {index, [val]}])),
+
+    Tabs = [i_set, i_bag, i_oset],
+
+    Add = fun(Tab) ->
+                  Write = fun(Tab) ->
+                                  Recs = [{Tab, N, N rem 5} || N <- lists:seq(1,10)],
+                                  [ok = mnesia:write(Rec) || Rec <- Recs],
+                                  Recs
+                          end,
+                  {atomic, Recs} = mnesia:sync_transaction(Write, [Tab]),
+                  lists:sort(Recs)
+          end,
+
+    IRead = fun(Tab) ->
+                    Read = fun(Tab) ->
+                                   [mnesia:index_read(Tab, N, val) || N <- lists:seq(0,4)]
+                           end,
+                    {atomic, Recs} = mnesia:transaction(Read, [Tab]),
+                    lists:sort(lists:flatten(Recs))
+           end,
+
+    Delete = fun(Rec) ->
+                     Del = fun() -> mnesia:delete_object(Rec) end,
+                     {atomic, ok} = mnesia:sync_transaction(Del),
+                     ok
+             end,
+
+
+    Recs = [Add(Tab) || Tab <- Tabs],
+    ?match(Recs, [IRead(Tab) || Tab <- Tabs]),
+    [Delete(Rec) || Rec <- lists:flatten(Recs)],
+
+    [?match({Tab,0}, {Tab,mnesia:table_info(Tab, size)}) || Tab <- Tabs],
+
+    [?match({Tab,Node,0, _}, rpc:call(Node, ?MODULE, index_size, [Tab]))
+     || Node <- All, Tab <- Tabs],
+    ?verify_mnesia(All, []).
+
+index_size(Tab) ->
+    %% White box testing
+    case mnesia:table_info(Tab, index_info) of
+        {index, _, [{_, {ram, Ref}}=Dbg]} -> {Tab, node(), ets:info(Ref, size), Dbg};
+        {index, _, [{_, {dets, Ref}}=Dbg]} -> {Tab, node(), dets:info(Ref, size), Dbg}
+    end.
-- 
cgit v1.2.3


From 5b51f9d9875750f27f2fa8a75f22e0fcb9a6bec0 Mon Sep 17 00:00:00 2001
From: Lars Thorsen <lars@erlang.org>
Date: Wed, 15 Aug 2018 12:26:44 +0200
Subject: Updated the engine load functionality

- engine_load/3/4 can be called multiple times for same engine
  if it allows it (eg doesn't contain global data)
- ensure_engine_loaded/2/3 is new functions that guarantees that the engine
  is just loaded once by adding it to OpenSSL internal engine list and check that
  before loading.
- ensure_engine_unloaded/1/2 is new functions that is used to unload engines loaded with
  ensure_engine_loaded (remove it from OpenSSL internal engine list and then unload).
- new utility functions engine_by_id/1, engine_add/1, engine_remove/1,
  engine_get_id/1 and engine_get_name/1
---
 lib/crypto/c_src/crypto.c          |  56 ++++++--
 lib/crypto/doc/src/crypto.xml      | 263 ++++++++++++++++++++++++++++++----
 lib/crypto/doc/src/engine_load.xml |  31 +++-
 lib/crypto/src/crypto.erl          | 174 +++++++++++++++++++----
 lib/crypto/test/engine_SUITE.erl   | 280 ++++++++++++++++++++++++++++++++-----
 5 files changed, 694 insertions(+), 110 deletions(-)

diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c
index 6e855939f7..6dd263adb2 100644
--- a/lib/crypto/c_src/crypto.c
+++ b/lib/crypto/c_src/crypto.c
@@ -1,7 +1,7 @@
 /*
  * %CopyrightBegin%
  *
- * Copyright Ericsson AB 2010-2017. All Rights Reserved.
+ * Copyright Ericsson AB 2010-2018. All Rights Reserved.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -527,6 +527,7 @@ static ERL_NIF_TERM engine_remove_nif(ErlNifEnv* env, int argc, const ERL_NIF_TE
 static ERL_NIF_TERM engine_get_first_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
 static ERL_NIF_TERM engine_get_next_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
 static ERL_NIF_TERM engine_get_id_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM engine_get_name_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
 static ERL_NIF_TERM engine_get_all_methods_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
 
 /* helpers */
@@ -613,6 +614,7 @@ static ErlNifFunc nif_funcs[] = {
     {"engine_get_first_nif", 0, engine_get_first_nif},
     {"engine_get_next_nif", 1, engine_get_next_nif},
     {"engine_get_id_nif", 1, engine_get_id_nif},
+    {"engine_get_name_nif", 1, engine_get_name_nif},
     {"engine_get_all_methods_nif", 0, engine_get_all_methods_nif}
 
 };
@@ -1016,7 +1018,7 @@ static int initialize(ErlNifEnv* env, ERL_NIF_TERM load_info)
 	 */
 	return 0;
     }
-#endif 
+#endif
 
     atom_true  = enif_make_atom(env,"true");
     atom_false = enif_make_atom(env,"false");
@@ -4727,7 +4729,7 @@ static ERL_NIF_TERM pkey_crypt_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM
         } else {
             /* non-evp rsa private decrypt */
             i = RSA_private_decrypt(in_bin.size, in_bin.data,
-                                    out_bin.data, rsa, crypt_opt.rsa_padding);       
+                                    out_bin.data, rsa, crypt_opt.rsa_padding);
             if (i > 0) {
                 ERL_VALGRIND_MAKE_MEM_DEFINED(out_bin.data, i);
                 enif_realloc_binary(&out_bin, i);
@@ -4745,7 +4747,7 @@ static ERL_NIF_TERM pkey_crypt_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM
         } else {
             /* non-evp rsa public decrypt */
             i = RSA_public_decrypt(in_bin.size, in_bin.data,
-                                   out_bin.data, rsa, crypt_opt.rsa_padding);    
+                                   out_bin.data, rsa, crypt_opt.rsa_padding);
             if (i > 0) {
                 ERL_VALGRIND_MAKE_MEM_DEFINED(out_bin.data, i);
                 enif_realloc_binary(&out_bin, i);
@@ -4863,7 +4865,7 @@ static ERL_NIF_TERM privkey_to_pubkey_nif(ErlNifEnv* env, int argc, const ERL_NI
           / * Example of result:
                {
                  Curve =  {Field, Prime, Point, Order, CoFactor} =
-                    { 
+                    {
                       Field =  {prime_field,<<255,...,255>>},
                       Prime = {<<255,...,252>>,
                                <<90,...,75>>,
@@ -4876,9 +4878,9 @@ static ERL_NIF_TERM privkey_to_pubkey_nif(ErlNifEnv* env, int argc, const ERL_NI
                 Key = <<151,...,62>>
                 }
               or
-              { 
+              {
                 Curve =
-                    {characteristic_two_field, 
+                    {characteristic_two_field,
                      M,
                      Basis = {tpbasis, _}
                            | {ppbasis, k1, k2, k3}
@@ -4891,7 +4893,7 @@ static ERL_NIF_TERM privkey_to_pubkey_nif(ErlNifEnv* env, int argc, const ERL_NI
         */
 #endif
     }
-    
+
     if (pkey) EVP_PKEY_free(pkey);
     return enif_make_badarg(env);
 }
@@ -5072,7 +5074,7 @@ static ERL_NIF_TERM engine_ctrl_cmd_strings_nif(ErlNifEnv* env, int argc, const
 
  error:
     for(i = 0; cmds != NULL && cmds[i] != NULL; i++)
-        enif_free(cmds[i]);    
+        enif_free(cmds[i]);
     enif_free(cmds);
     return ret;
 #else
@@ -5390,7 +5392,7 @@ static ERL_NIF_TERM engine_get_id_nif(ErlNifEnv* env, int argc, const ERL_NIF_TE
     if (!engine_id) {
         enif_alloc_binary(0, &engine_id_bin);
         engine_id_bin.size = 0;
-        return enif_make_tuple2(env, atom_ok, enif_make_binary(env, &engine_id_bin));
+        return enif_make_binary(env, &engine_id_bin);
     }
 
     size = strlen(engine_id);
@@ -5398,7 +5400,39 @@ static ERL_NIF_TERM engine_get_id_nif(ErlNifEnv* env, int argc, const ERL_NIF_TE
     engine_id_bin.size = size;
     memcpy(engine_id_bin.data, engine_id, size);
 
-    return enif_make_tuple2(env, atom_ok, enif_make_binary(env, &engine_id_bin));
+    return enif_make_binary(env, &engine_id_bin);
+#else
+    return atom_notsup;
+#endif
+}
+
+static ERL_NIF_TERM engine_get_name_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{/* (Engine) */
+#ifdef HAS_ENGINE_SUPPORT
+    ErlNifBinary engine_name_bin;
+    const char *engine_name;
+    int size;
+    struct engine_ctx *ctx;
+
+    // Get Engine
+    if (!enif_get_resource(env, argv[0], engine_ctx_rtype, (void**)&ctx)) {
+        PRINTF_ERR0("engine_get_id_nif Leaved: Parameter not an engine resource object");
+        return enif_make_badarg(env);
+    }
+
+    engine_name = ENGINE_get_name(ctx->engine);
+    if (!engine_name) {
+        enif_alloc_binary(0, &engine_name_bin);
+        engine_name_bin.size = 0;
+        return enif_make_binary(env, &engine_name_bin);
+    }
+
+    size = strlen(engine_name);
+    enif_alloc_binary(size, &engine_name_bin);
+    engine_name_bin.size = size;
+    memcpy(engine_name_bin.data, engine_name, size);
+
+    return enif_make_binary(env, &engine_name_bin);
 #else
     return atom_notsup;
 #endif
diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml
index 464799b320..8eb414b9bf 100644
--- a/lib/crypto/doc/src/crypto.xml
+++ b/lib/crypto/doc/src/crypto.xml
@@ -4,7 +4,7 @@
 <erlref>
   <header>
     <copyright>
-      <year>1999</year><year>2017</year>
+      <year>1999</year><year>2018</year>
       <holder>Ericsson AB. All Rights Reserved.</holder>
     </copyright>
     <legalnotice>
@@ -142,7 +142,7 @@
                                password => password()}</code>
 
     <code>engine_ref() = term()</code>
-    <p>The result of a call to <seealso marker="#engine_load-3">engine_load/3</seealso>.
+    <p>The result of a call to for example <seealso marker="#engine_load-3">engine_load/3</seealso>.
     </p>
 
     <code>key_id() = string() | binary()</code>
@@ -628,7 +628,7 @@
       <desc>
 	<p>Fetches the corresponding public key from a private key stored in an Engine.
 	The key must be of the type indicated by the Type parameter.
-	</p>	
+	</p>
       </desc>
     </func>
 
@@ -953,8 +953,8 @@ _FloatValue = rand:uniform().     % [0.0; 1.0[</pre>
 	<p>
 	  Returns a list of all possible engine methods.
 	</p>
-	<p>	  
-	  May throw exception notsup in case there is 
+	<p>
+	  May throw exception notsup in case there is
 	  no engine support in the underlying OpenSSL implementation.
 	</p>
 	<p>
@@ -970,18 +970,18 @@ _FloatValue = rand:uniform().     % [0.0; 1.0[</pre>
       <type>
 	<v>EngineId = unicode:chardata()</v>
 	<v>PreCmds, PostCmds = [{unicode:chardata(), unicode:chardata()}]</v>
-	<v>Result = {ok, Engine::term()} | {error, Reason::term()}</v>
+	<v>Result = {ok, Engine::engine_ref()} | {error, Reason::term()}</v>
       </type>
       <desc>
 	<p>
 	  Loads the OpenSSL engine given by <c>EngineId</c> if it is available and then returns ok and
-	  an engine handle. This function is the same as calling <c>engine_load/4</c> with 
-	  <c>EngineMethods</c> set to a list of all the possible methods.  An error tuple is 
+	  an engine handle. This function is the same as calling <c>engine_load/4</c> with
+	  <c>EngineMethods</c> set to a list of all the possible methods.  An error tuple is
 	  returned if the engine can't be loaded.
 	</p>
 	<p>
 	  The function throws a badarg if the parameters are in wrong format.
-	  It may also throw the exception notsup in case there is 
+	  It may also throw the exception notsup in case there is
 	  no engine support in the underlying OpenSSL implementation.
 	</p>
 	<p>
@@ -998,7 +998,7 @@ _FloatValue = rand:uniform().     % [0.0; 1.0[</pre>
 	<v>EngineId = unicode:chardata()</v>
 	<v>PreCmds, PostCmds = [{unicode:chardata(), unicode:chardata()}]</v>
 	<v>EngineMethods = [engine_method_type()]</v>
-	<v>Result = {ok, Engine::term()} | {error, Reason::term()}</v>
+	<v>Result = {ok, Engine::engine_ref()} | {error, Reason::term()}</v>
       </type>
       <desc>
 	<p>
@@ -1007,7 +1007,7 @@ _FloatValue = rand:uniform().     % [0.0; 1.0[</pre>
 	</p>
 	<p>
 	  The function throws a badarg if the parameters are in wrong format.
-	  It may also throw the exception notsup in case there is 
+	  It may also throw the exception notsup in case there is
 	  no engine support in the underlying OpenSSL implementation.
 	</p>
 	<p>
@@ -1021,17 +1021,17 @@ _FloatValue = rand:uniform().     % [0.0; 1.0[</pre>
       <name>engine_unload(Engine) -> Result</name>
       <fsummary>Dynamical load an encryption engine</fsummary>
       <type>
-	<v>Engine = term()</v>
+	<v>Engine = engine_ref()</v>
 	<v>Result = ok | {error, Reason::term()}</v>
       </type>
       <desc>
 	<p>
-	  Unloads the OpenSSL engine given by <c>EngineId</c>.
+	  Unloads the OpenSSL engine given by <c>Engine</c>.
 	  An error tuple is returned if the engine can't be unloaded.
 	</p>
 	<p>
 	  The function throws a badarg if the parameter is in wrong format.
-	  It may also throw the exception notsup in case there is 
+	  It may also throw the exception notsup in case there is
 	  no engine support in the underlying OpenSSL implementation.
 	</p>
 	<p>
@@ -1042,19 +1042,24 @@ _FloatValue = rand:uniform().     % [0.0; 1.0[</pre>
     </func>
 
     <func>
-      <name>engine_list() -> Result</name>
-      <fsummary>List the known engine ids</fsummary>
+      <name>engine_by_id(EngineId) -> Result</name>
+      <fsummary>Get a reference to an already loaded engine</fsummary>
       <type>
-	<v>Result = [EngineId::unicode:chardata()]</v>
+	<v>EngineID = unicode:chardata()engine_ref()</v>
+	<v>Result = {ok, Engine::engine_ref()} | {error, Reason::term()}</v>
       </type>
       <desc>
-	<p>List the id's of all engines in OpenSSL's internal list.</p>
 	<p>
-	  It may also throw the exception notsup in case there is 
+	  Get a reference to an already loaded engine with <c>EngineId</c>.
+	  An error tuple is returned if the engine can't be unloaded.
+	</p>
+	<p>
+	  The function throws a badarg if the parameter is in wrong format.
+	  It may also throw the exception notsup in case there is
 	  no engine support in the underlying OpenSSL implementation.
 	</p>
 	<p>
-	  See also the chapter <seealso marker="crypto:engine_load#engine_load">Engine Load</seealso> 
+	  See also the chapter <seealso marker="crypto:engine_load#engine_load">Engine Load</seealso>
 	  in the User's Guide.
 	</p>
       </desc>
@@ -1064,7 +1069,7 @@ _FloatValue = rand:uniform().     % [0.0; 1.0[</pre>
       <name>engine_ctrl_cmd_string(Engine, CmdName, CmdArg) -> Result</name>
       <fsummary>Sends ctrl commands to an OpenSSL engine</fsummary>
       <type>
-	<v>Engine = term()</v>
+	<v>Engine = engine_ref()</v>
 	<v>CmdName = unicode:chardata()</v>
 	<v>CmdArg = unicode:chardata()</v>
 	<v>Result = ok | {error, Reason::term()}</v>
@@ -1072,12 +1077,12 @@ _FloatValue = rand:uniform().     % [0.0; 1.0[</pre>
       <desc>
 	<p>
 	  Sends ctrl commands to the OpenSSL engine given by <c>Engine</c>.
-	  This function is the same as calling <c>engine_ctrl_cmd_string/4</c> with 
+	  This function is the same as calling <c>engine_ctrl_cmd_string/4</c> with
 	  <c>Optional</c> set to <c>false</c>.
 	</p>
 	<p>
 	  The function throws a badarg if the parameters are in wrong format.
-	  It may also throw the exception notsup in case there is 
+	  It may also throw the exception notsup in case there is
 	  no engine support in the underlying OpenSSL implementation.
 	</p>
       </desc>
@@ -1087,7 +1092,7 @@ _FloatValue = rand:uniform().     % [0.0; 1.0[</pre>
       <name>engine_ctrl_cmd_string(Engine, CmdName, CmdArg, Optional) -> Result</name>
       <fsummary>Sends ctrl commands to an OpenSSL engine</fsummary>
       <type>
-	<v>Engine = term()</v>
+	<v>Engine = engine_ref()</v>
 	<v>CmdName = unicode:chardata()</v>
 	<v>CmdArg = unicode:chardata()</v>
 	<v>Optional = boolean()</v>
@@ -1096,18 +1101,218 @@ _FloatValue = rand:uniform().     % [0.0; 1.0[</pre>
       <desc>
 	<p>
 	  Sends ctrl commands to the OpenSSL engine given by <c>Engine</c>.
-	  <c>Optional</c> is a boolean argument that can relax the semantics of the function. 
-	  If set to <c>true</c> it will only return failure if the ENGINE supported the given 
-	  command name but failed while executing it, if the ENGINE doesn't support the command 
-	  name it will simply return success without doing anything. In this case we assume 
+	  <c>Optional</c> is a boolean argument that can relax the semantics of the function.
+	  If set to <c>true</c> it will only return failure if the ENGINE supported the given
+	  command name but failed while executing it, if the ENGINE doesn't support the command
+	  name it will simply return success without doing anything. In this case we assume
 	  the user is only supplying commands specific to the given ENGINE so we set this to
 	  <c>false</c>.
 	</p>
 	<p>
 	  The function throws a badarg if the parameters are in wrong format.
-	  It may also throw the exception notsup in case there is 
+	  It may also throw the exception notsup in case there is
+	  no engine support in the underlying OpenSSL implementation.
+	</p>
+      </desc>
+    </func>
+
+    <func>
+      <name>engine_add(Engine) -> Result</name>
+      <fsummary>Add engine to OpenSSL internal list</fsummary>
+      <type>
+	<v>Engine = engine_ref()</v>
+	<v>Result = ok | {error, Reason::term()}</v>
+      </type>
+      <desc>
+	<p>Add the engine to OpenSSL's internal list.</p>
+	<p>
+	  The function throws a badarg if the parameters are in wrong format.
+	  It may also throw the exception notsup in case there is
+	  no engine support in the underlying OpenSSL implementation.
+	</p>
+      </desc>
+    </func>
+
+    <func>
+      <name>engine_remove(Engine) -> Result</name>
+      <fsummary>Remove engine to OpenSSL internal list</fsummary>
+      <type>
+	<v>Engine = engine_ref()</v>
+	<v>Result = ok | {error, Reason::term()}</v>
+      </type>
+      <desc>
+	<p>Remove the engine from OpenSSL's internal list.</p>
+	<p>
+	  The function throws a badarg if the parameters are in wrong format.
+	  It may also throw the exception notsup in case there is
+	  no engine support in the underlying OpenSSL implementation.
+	</p>
+      </desc>
+    </func>
+
+    <func>
+      <name>engine_get_id(Engine) -> EngineId</name>
+      <fsummary>Fetch engine ID</fsummary>
+      <type>
+	<v>Engine = engine_ref()</v>
+	<v>EngineId = unicode:chardata()</v>
+      </type>
+      <desc>
+	<p>Return the ID for the engine, or an empty binary if there is no id set.</p>
+	<p>
+	  The function throws a badarg if the parameters are in wrong format.
+	  It may also throw the exception notsup in case there is
+	  no engine support in the underlying OpenSSL implementation.
+	</p>
+      </desc>
+    </func>
+
+    <func>
+      <name>engine_get_name(Engine) -> EngineName</name>
+      <fsummary>Fetch engine name</fsummary>
+      <type>
+	<v>Engine = engine_ref()</v>
+	<v>EngineName = unicode:chardata()</v>
+      </type>
+      <desc>
+	<p>Return the name (eg a description) for the engine, or an empty binary if there is no name set.</p>
+	<p>
+	  The function throws a badarg if the parameters are in wrong format.
+	  It may also throw the exception notsup in case there is
+	  no engine support in the underlying OpenSSL implementation.
+	</p>
+      </desc>
+    </func>
+
+    <func>
+      <name>engine_list() -> Result</name>
+      <fsummary>List the known engine ids</fsummary>
+      <type>
+	<v>Result = [EngineId::unicode:chardata()]</v>
+      </type>
+      <desc>
+	<p>List the id's of all engines in OpenSSL's internal list.</p>
+	<p>
+	  It may also throw the exception notsup in case there is
+	  no engine support in the underlying OpenSSL implementation.
+	</p>
+	<p>
+	  See also the chapter <seealso marker="crypto:engine_load#engine_load">Engine Load</seealso>
+	  in the User's Guide.
+	</p>
+	<p>
+	  May throw exception notsup in case engine functionality is not supported by the underlying
+	  OpenSSL implementation.
+	</p>
+      </desc>
+    </func>
+
+    <func>
+      <name>ensure_engine_loaded(EngineId, LibPath) -> Result</name>
+      <fsummary>Ensure encryption engine just loaded once</fsummary>
+      <type>
+	<v>EngineId = unicode:chardata()</v>
+	<v>LibPath = unicode:chardata()</v>
+	<v>Result = {ok, Engine::engine_ref()} | {error, Reason::term()}</v>
+      </type>
+      <desc>
+	<p>
+	  Loads the OpenSSL engine given by <c>EngineId</c> and the path to the dynamic library
+	  implementing the engine. This function is the same as calling <c>ensure_engine_loaded/3</c> with
+	  <c>EngineMethods</c> set to a list of all the possible methods. An error tuple is
+	  returned if the engine can't be loaded.
+	</p>
+	<p>
+	  The function throws a badarg if the parameters are in wrong format.
+	  It may also throw the exception notsup in case there is
 	  no engine support in the underlying OpenSSL implementation.
 	</p>
+	<p>
+	  See also the chapter <seealso marker="crypto:engine_load#engine_load">Engine Load</seealso>
+	  in the User's Guide.
+	</p>
+      </desc>
+    </func>
+
+    <func>
+      <name>ensure_engine_loaded(EngineId, LibPath, EngineMethods) -> Result</name>
+      <fsummary>Ensure encryption engine just loaded once</fsummary>
+      <type>
+	<v>EngineId = unicode:chardata()</v>
+	<v>LibPath = unicode:chardata()</v>
+	<v>EngineMethods = [engine_method_type()]</v>
+	<v>Result = {ok, Engine::engine_ref()} | {error, Reason::term()}</v>
+      </type>
+      <desc>
+	<p>
+	  Loads the OpenSSL engine given by <c>EngineId</c> and the path to the dynamic library
+	  implementing the engine. This function differs from the normal engine_load in that sense it
+	  also add the engine id to the internal list in OpenSSL. Then in the following calls to the function
+	  it just fetch the reference to the engine instead of loading it again.
+	  An error tuple is returned if the engine can't be loaded.
+	</p>
+	<p>
+	  The function throws a badarg if the parameters are in wrong format.
+	  It may also throw the exception notsup in case there is
+	  no engine support in the underlying OpenSSL implementation.
+	</p>
+	<p>
+	  See also the chapter <seealso marker="crypto:engine_load#engine_load">Engine Load</seealso>
+	  in the User's Guide.
+	</p>
+      </desc>
+    </func>
+
+    <func>
+      <name>ensure_engine_unloaded(Engine) -> Result</name>
+      <fsummary>Unload an engine loaded with the ensure function</fsummary>
+      <type>
+	<v>Engine = engine_ref()</v>
+	<v>Result = ok | {error, Reason::term()}</v>
+      </type>
+      <desc>
+	<p>
+	  Unloads an engine loaded with the <c>ensure_engine_loaded</c> function.
+	  It both removes the label from the OpenSSL internal engine list and unloads the engine.
+	  This function is the same as calling <c>ensure_engine_unloaded/2</c> with
+	  <c>EngineMethods</c> set to a list of all the possible methods. An error tuple is
+	  returned if the engine can't be unloaded.
+	</p>
+	<p>
+	  The function throws a badarg if the parameters are in wrong format.
+	  It may also throw the exception notsup in case there is
+	  no engine support in the underlying OpenSSL implementation.
+	</p>
+	<p>
+	  See also the chapter <seealso marker="crypto:engine_load#engine_load">Engine Load</seealso>
+	  in the User's Guide.
+	</p>
+      </desc>
+    </func>
+
+    <func>
+      <name>ensure_engine_unloaded(Engine, EngineMethods) -> Result</name>
+      <fsummary>Unload an engine loaded with the ensure function</fsummary>
+      <type>
+	<v>Engine = engine_ref()</v>
+	<v>EngineMethods = [engine_method_type()]</v>
+	<v>Result = ok | {error, Reason::term()}</v>
+      </type>
+      <desc>
+	<p>
+	  Unloads an engine loaded with the <c>ensure_engine_loaded</c> function.
+	  It both removes the label from the OpenSSL internal engine list and unloads the engine.
+	  An error tuple is returned if the engine can't be unloaded.
+	</p>
+	<p>
+	  The function throws a badarg if the parameters are in wrong format.
+	  It may also throw the exception notsup in case there is
+	  no engine support in the underlying OpenSSL implementation.
+	</p>
+	<p>
+	  See also the chapter <seealso marker="crypto:engine_load#engine_load">Engine Load</seealso>
+	  in the User's Guide.
+	</p>
       </desc>
     </func>
 
diff --git a/lib/crypto/doc/src/engine_load.xml b/lib/crypto/doc/src/engine_load.xml
index e5c3f5d561..3d0aa0c32a 100644
--- a/lib/crypto/doc/src/engine_load.xml
+++ b/lib/crypto/doc/src/engine_load.xml
@@ -42,6 +42,9 @@
       operations. The hardware implementation usually offers improved performance over its
       software-based counterpart, which is known as cryptographic acceleration.
     </p>
+    <note>
+      <p>The file name requirement on the engine dynamic library can differ between SSL versions.</p>
+    </note>
   </section>
 
   <section>
@@ -54,9 +57,6 @@
       <code>
 1> {ok, Engine} = crypto:engine_load(&lt;&lt;"otp_test_engine">>, [], []).
  {ok, #Ref}</code>
-       <note>
-	<p>The file name requirement on the engine dynamic library can differ between SSL versions.</p>
-      </note>
     </section>
 
     <section>
@@ -72,9 +72,6 @@
                                        &lt;&lt;"LOAD">>],
                                       []).
  {ok, #Ref}</code>
-      <note>
-	<p>The dynamic engine is not supported in LibreSSL from version 2.2.1</p>
-      </note>
     </section>
 
     <section>
@@ -99,6 +96,28 @@ engine_method_pkey_meths, engine_method_pkey_asn1_meths].
  {ok, #Ref}</code>
     </section>
 
+    <section>
+      <title>Load with the ensure loaded function</title>
+      <p>
+	This function makes sure the engine is loaded just once and the ID is added to the internal
+	engine list of OpenSSL. The following calls to the function will check if the ID is loaded
+	and then just get a new reference to the engine.
+      </p>
+      <code>
+ 5> {ok, Engine} = crypto:ensure_engine_loaded(&lt;&lt;"MD5">>,
+                                               &lt;&lt;"/some/path/otp_test_engine.so">>).
+ {ok, #Ref}</code>
+      <p>
+	To unload it use crypto:ensure_engine_unloaded/1 which removes the ID from the internal list
+	before unloading the engine.
+      </p>
+      <code>
+ 6> crypto:ensure_engine_unloaded(&lt;&lt;"MD5">>).
+ ok</code>
+    </section>
+
+
+
     <section>
       <title>List all engines currently loaded</title>
       <code>
diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl
index 1a1b4f98b5..0d85b94b57 100644
--- a/lib/crypto/src/crypto.erl
+++ b/lib/crypto/src/crypto.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 1999-2017. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2018. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -51,9 +51,18 @@
          engine_load/3,
          engine_load/4,
          engine_unload/1,
+         engine_by_id/1,
          engine_list/0,
          engine_ctrl_cmd_string/3,
-         engine_ctrl_cmd_string/4
+         engine_ctrl_cmd_string/4,
+         engine_add/1,
+         engine_remove/1,
+         engine_get_id/1,
+         engine_get_name/1,
+         ensure_engine_loaded/2,
+         ensure_engine_loaded/3,
+         ensure_engine_unloaded/1,
+         ensure_engine_unloaded/2
         ]).
 
 -export_type([engine_ref/0,
@@ -444,7 +453,7 @@ sign(Algorithm, Type, Data, Key, Options) ->
 -type engine_key_ref() :: #{engine :=   engine_ref(),
                             key_id :=   key_id(),
                             password => password(),
-                            term() => term() 
+                            term() => term()
                            }.
 
 -type pk_algs() :: rsa | ecdsa | dss .
@@ -604,7 +613,7 @@ compute_key(ecdh, Others, My, Curve) ->
 -type engine_method_type() :: engine_method_rsa | engine_method_dsa | engine_method_dh |
                               engine_method_rand | engine_method_ecdh | engine_method_ecdsa |
                               engine_method_ciphers | engine_method_digests | engine_method_store |
-                              engine_method_pkey_meths | engine_method_pkey_asn1_meths | 
+                              engine_method_pkey_meths | engine_method_pkey_asn1_meths |
                               engine_method_ec.
 
 -type engine_ref() :: term().
@@ -621,7 +630,8 @@ engine_get_all_methods() ->
                   PreCmds::[{unicode:chardata(), unicode:chardata()}],
                   PostCmds::[{unicode:chardata(), unicode:chardata()}]) ->
     {ok, Engine::engine_ref()} | {error, Reason::term()}.
-engine_load(EngineId, PreCmds, PostCmds) when is_list(PreCmds), is_list(PostCmds) ->
+engine_load(EngineId, PreCmds, PostCmds) when is_list(PreCmds),
+                                              is_list(PostCmds) ->
     engine_load(EngineId, PreCmds, PostCmds, engine_get_all_methods()).
 
 %%----------------------------------------------------------------------
@@ -638,28 +648,26 @@ engine_load(EngineId, PreCmds, PostCmds, EngineMethods) when is_list(PreCmds),
         ok = notsup_to_error(engine_load_dynamic_nif()),
         case notsup_to_error(engine_by_id_nif(ensure_bin_chardata(EngineId))) of
             {ok, Engine} ->
-                ok = engine_load_1(Engine, PreCmds, PostCmds, EngineMethods),
-                {ok, Engine};
+                engine_load_1(Engine, PreCmds, PostCmds, EngineMethods);
             {error, Error1} ->
                 {error, Error1}
         end
     catch
-       throw:Error2 ->
-          Error2
+        throw:Error2 ->
+            Error2
     end.
 
 engine_load_1(Engine, PreCmds, PostCmds, EngineMethods) ->
     try
         ok = engine_nif_wrapper(engine_ctrl_cmd_strings_nif(Engine, ensure_bin_cmds(PreCmds), 0)),
-        ok = engine_nif_wrapper(engine_add_nif(Engine)),
         ok = engine_nif_wrapper(engine_init_nif(Engine)),
         engine_load_2(Engine, PostCmds, EngineMethods),
-        ok
+        {ok, Engine}
     catch
-       throw:Error ->
-          %% The engine couldn't initialise, release the structural reference
-          ok = engine_free_nif(Engine),
-          throw(Error)
+        throw:Error ->
+            %% The engine couldn't initialise, release the structural reference
+            ok = engine_free_nif(Engine),
+            throw(Error)
     end.
 
 engine_load_2(Engine, PostCmds, EngineMethods) ->
@@ -689,7 +697,6 @@ engine_unload(Engine, EngineMethods) ->
     try
         [ok = engine_nif_wrapper(engine_unregister_nif(Engine, engine_method_atom_to_int(Method))) ||
             Method <- EngineMethods],
-        ok = engine_nif_wrapper(engine_remove_nif(Engine)),
         %% Release the functional reference from engine_init_nif
         ok = engine_nif_wrapper(engine_finish_nif(Engine)),
         %% Release the structural reference from engine_by_id_nif
@@ -699,6 +706,41 @@ engine_unload(Engine, EngineMethods) ->
           Error
     end.
 
+%%----------------------------------------------------------------------
+%% Function: engine_by_id/1
+%%----------------------------------------------------------------------
+engine_by_id(EngineId) ->
+    try
+        notsup_to_error(engine_by_id_nif(ensure_bin_chardata(EngineId)))
+    catch
+       throw:Error ->
+          Error
+    end.
+
+%%----------------------------------------------------------------------
+%% Function: engine_add/1
+%%----------------------------------------------------------------------
+engine_add(Engine) ->
+    notsup_to_error(engine_add_nif(Engine)).
+
+%%----------------------------------------------------------------------
+%% Function: engine_remove/1
+%%----------------------------------------------------------------------
+engine_remove(Engine) ->
+    notsup_to_error(engine_remove_nif(Engine)).
+
+%%----------------------------------------------------------------------
+%% Function: engine_get_id/1
+%%----------------------------------------------------------------------
+engine_get_id(Engine) ->
+    notsup_to_error(engine_get_id_nif(Engine)).
+
+%%----------------------------------------------------------------------
+%% Function: engine_get_name/1
+%%----------------------------------------------------------------------
+engine_get_name(Engine) ->
+    notsup_to_error(engine_get_name_nif(Engine)).
+
 %%----------------------------------------------------------------------
 %% Function: engine_list/0
 %%----------------------------------------------------------------------
@@ -710,9 +752,9 @@ engine_list() ->
             [];
         {ok, Engine} ->
             case notsup_to_error(engine_get_id_nif(Engine)) of
-                {ok, <<>>} ->
+                <<>> ->
                     engine_list(Engine, []);
-                {ok, EngineId} ->
+                EngineId ->
                     engine_list(Engine, [EngineId])
             end
     end.
@@ -723,9 +765,9 @@ engine_list(Engine0, IdList) ->
             lists:reverse(IdList);
         {ok, Engine1} ->
             case notsup_to_error(engine_get_id_nif(Engine1)) of
-                {ok, <<>>} ->
+                <<>> ->
                     engine_list(Engine1, IdList);
-                {ok, EngineId} ->
+                EngineId ->
                     engine_list(Engine1, [EngineId |IdList])
             end
     end.
@@ -734,7 +776,7 @@ engine_list(Engine0, IdList) ->
 %% Function: engine_ctrl_cmd_string/3
 %%----------------------------------------------------------------------
 -spec engine_ctrl_cmd_string(Engine::term(),
-                      CmdName::unicode:chardata(), 
+                      CmdName::unicode:chardata(),
                       CmdArg::unicode:chardata()) ->
     ok | {error, Reason::term()}.
 engine_ctrl_cmd_string(Engine, CmdName, CmdArg) ->
@@ -744,13 +786,13 @@ engine_ctrl_cmd_string(Engine, CmdName, CmdArg) ->
 %% Function: engine_ctrl_cmd_string/4
 %%----------------------------------------------------------------------
 -spec engine_ctrl_cmd_string(Engine::term(),
-                      CmdName::unicode:chardata(), 
+                      CmdName::unicode:chardata(),
                       CmdArg::unicode:chardata(),
                       Optional::boolean()) ->
     ok | {error, Reason::term()}.
 engine_ctrl_cmd_string(Engine, CmdName, CmdArg, Optional) ->
-    case engine_ctrl_cmd_strings_nif(Engine, 
-                                     ensure_bin_cmds([{CmdName, CmdArg}]), 
+    case engine_ctrl_cmd_strings_nif(Engine,
+                                     ensure_bin_cmds([{CmdName, CmdArg}]),
                                      bool_to_int(Optional)) of
         ok ->
             ok;
@@ -760,6 +802,82 @@ engine_ctrl_cmd_string(Engine, CmdName, CmdArg, Optional) ->
             {error, Error}
     end.
 
+%%----------------------------------------------------------------------
+%% Function: ensure_engine_loaded/2
+%% Special version of load that only uses dynamic engine to load
+%%----------------------------------------------------------------------
+ensure_engine_loaded(EngineId, LibPath) ->
+    ensure_engine_loaded(EngineId, LibPath, engine_get_all_methods()).
+
+%%----------------------------------------------------------------------
+%% Function: ensure_engine_loaded/3
+%% Special version of load that only uses dynamic engine to load
+%%----------------------------------------------------------------------
+ensure_engine_loaded(EngineId, LibPath, EngineMethods) ->
+    try
+        List = crypto:engine_list(),
+        case lists:member(EngineId, List) of
+            true ->
+                notsup_to_error(engine_by_id_nif(ensure_bin_chardata(EngineId)));
+            false ->
+                ok = notsup_to_error(engine_load_dynamic_nif()),
+                case notsup_to_error(engine_by_id_nif(ensure_bin_chardata(<<"dynamic">>))) of
+                    {ok, Engine} ->
+                        PreCommands = [{<<"SO_PATH">>, ensure_bin_chardata(LibPath)},
+                                       {<<"ID">>, ensure_bin_chardata(EngineId)},
+                                       <<"LOAD">>],
+                        ensure_engine_loaded_1(Engine, PreCommands, EngineMethods);
+                    {error, Error1} ->
+                        {error, Error1}
+                end
+        end
+    catch
+        throw:Error2 ->
+            Error2
+    end.
+
+ensure_engine_loaded_1(Engine, PreCmds, Methods) ->
+    try
+        ok = engine_nif_wrapper(engine_ctrl_cmd_strings_nif(Engine, ensure_bin_cmds(PreCmds), 0)),
+        ok = engine_nif_wrapper(engine_add_nif(Engine)),
+        ok = engine_nif_wrapper(engine_init_nif(Engine)),
+        ensure_engine_loaded_2(Engine, Methods),
+        {ok, Engine}
+    catch
+        throw:Error ->
+            %% The engine couldn't initialise, release the structural reference
+            ok = engine_free_nif(Engine),
+            throw(Error)
+    end.
+
+ensure_engine_loaded_2(Engine, Methods) ->
+    try
+        [ok = engine_nif_wrapper(engine_register_nif(Engine, engine_method_atom_to_int(Method))) ||
+            Method <- Methods],
+        ok
+    catch
+       throw:Error ->
+          %% The engine registration failed, release the functional reference
+          ok = engine_finish_nif(Engine),
+          throw(Error)
+    end.
+%%----------------------------------------------------------------------
+%% Function: ensure_engine_unloaded/1
+%%----------------------------------------------------------------------
+ensure_engine_unloaded(Engine) ->
+    ensure_engine_unloaded(Engine, engine_get_all_methods()).
+
+%%----------------------------------------------------------------------
+%% Function: ensure_engine_unloaded/2
+%%----------------------------------------------------------------------
+ensure_engine_unloaded(Engine, EngineMethods) ->
+    case engine_remove(Engine) of
+        ok ->
+            engine_unload(Engine, EngineMethods);
+        {error, E} ->
+            {error, E}
+    end.
+
 %%--------------------------------------------------------------------
 %%% On load
 %%--------------------------------------------------------------------
@@ -827,7 +945,7 @@ path2bin(Path) when is_list(Path) ->
 max_bytes() ->
     ?MAX_BYTES_TO_NIF.
 
-notsup_to_error(notsup) -> 
+notsup_to_error(notsup) ->
     erlang:error(notsup);
 notsup_to_error(Other) ->
     Other.
@@ -1104,7 +1222,7 @@ privkey_to_pubkey(Alg, EngineMap) when Alg == rsa; Alg == dss; Alg == ecdsa ->
         error:notsup ->
             {error, notsup}
     end.
-            
+
 privkey_to_pubkey_nif(_Alg, _EngineMap) -> ?nif_stub.
 
 
@@ -1266,6 +1384,7 @@ engine_unregister_nif(_Engine, _EngineMethod) -> ?nif_stub.
 engine_get_first_nif() -> ?nif_stub.
 engine_get_next_nif(_Engine) -> ?nif_stub.
 engine_get_id_nif(_Engine) -> ?nif_stub.
+engine_get_name_nif(_Engine) -> ?nif_stub.
 engine_get_all_methods_nif() -> ?nif_stub.
 
 %%--------------------------------------------------------------------
@@ -1323,7 +1442,7 @@ get_test_engine() ->
     Type = erlang:system_info(system_architecture),
     LibDir = filename:join([code:priv_dir(crypto), "lib"]),
     ArchDir = filename:join([LibDir, Type]),
-    case filelib:is_dir(ArchDir) of        
+    case filelib:is_dir(ArchDir) of
 	true  -> check_otp_test_engine(ArchDir);
 	false -> check_otp_test_engine(LibDir)
     end.
@@ -1341,4 +1460,3 @@ check_otp_test_engine(LibDir) ->
                     {error, notexist}
             end
     end.
-            
diff --git a/lib/crypto/test/engine_SUITE.erl b/lib/crypto/test/engine_SUITE.erl
index f410542f72..891eaff23b 100644
--- a/lib/crypto/test/engine_SUITE.erl
+++ b/lib/crypto/test/engine_SUITE.erl
@@ -1,7 +1,7 @@
 %%
 %% %CopyrightBegin%
 %%
-%% Copyright Ericsson AB 2017-2017. All Rights Reserved.
+%% Copyright Ericsson AB 2017-2018. All Rights Reserved.
 %%
 %% Licensed under the Apache License, Version 2.0 (the "License");
 %% you may not use this file except in compliance with the License.
@@ -39,6 +39,10 @@ all() ->
      get_all_possible_methods,
      engine_load_all_methods,
      engine_load_some_methods,
+     multiple_engine_load,
+     engine_list,
+     get_id_and_name,
+     engine_by_id,
      bad_arguments,
      unknown_engine,
      pre_command_fail_bad_value,
@@ -46,6 +50,7 @@ all() ->
      failed_engine_init,
      ctrl_cmd_string,
      ctrl_cmd_string_optional,
+     ensure_load,
      {group, engine_stored_key}
     ].
 
@@ -95,7 +100,7 @@ init_per_group(engine_stored_key, Config) ->
         {error, notexist} ->
             {skip, "OTP Test engine not found"};
         {error, notsup} ->
-            {skip, "Engine not supported on this OpenSSL version"};
+            {skip, "Engine not supported on this SSL version"};
         {error, bad_engine_id} ->
             {skip, "Dynamic Engine not supported"};
         Other ->
@@ -130,11 +135,12 @@ get_all_possible_methods() ->
 get_all_possible_methods(Config) when is_list(Config) ->
     try
         List = crypto:engine_get_all_methods(),
+        true = erlang:is_list(List),
         ct:log("crypto:engine_get_all_methods() -> ~p\n", [List]),
         ok
     catch
         error:notsup ->
-            {skip, "Engine not supported on this OpenSSL version"}
+            {skip, "Engine not supported on this SSL version"}
     end.
 
 engine_load_all_methods()->
@@ -147,13 +153,12 @@ engine_load_all_methods(Config) when is_list(Config) ->
         {error, notexist} ->
             {skip, "OTP Test engine not found"};
         {ok, Engine} ->
-            try 
+            try
                 Md5Hash1 =  <<106,30,3,246,166,222,229,158,244,217,241,179,50,232,107,109>>,
                 Md5Hash1 = crypto:hash(md5, "Don't panic"),
                 Md5Hash2 =  <<0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15>>,
                 case crypto:engine_load(<<"dynamic">>,
                                         [{<<"SO_PATH">>, Engine},
-                                         {<<"ID">>, <<"MD5">>},
                                          <<"LOAD">>],
                                         []) of
                     {ok, E} ->
@@ -179,7 +184,7 @@ engine_load_all_methods(Config) when is_list(Config) ->
                 end
            catch
                error:notsup ->
-                  {skip, "Engine not supported on this OpenSSL version"}
+                  {skip, "Engine not supported on this SSL version"}
            end
     end.
 
@@ -193,21 +198,20 @@ engine_load_some_methods(Config) when is_list(Config) ->
         {error, notexist} ->
             {skip, "OTP Test engine not found"};
         {ok, Engine} ->
-            try 
+            try
                 Md5Hash1 =  <<106,30,3,246,166,222,229,158,244,217,241,179,50,232,107,109>>,
                 Md5Hash1 = crypto:hash(md5, "Don't panic"),
                 Md5Hash2 =  <<0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15>>,
                 EngineMethods = crypto:engine_get_all_methods() --
-                    [engine_method_dh,engine_method_rand,
+                    [engine_method_dh, engine_method_rand,
                      engine_method_ciphers, engine_method_store,
                      engine_method_pkey_meths, engine_method_pkey_asn1_meths],
                 case crypto:engine_load(<<"dynamic">>,
                                         [{<<"SO_PATH">>, Engine},
-                                         {<<"ID">>, <<"MD5">>},
                                          <<"LOAD">>],
                                         [],
                                         EngineMethods) of
-                    {ok, E} ->        
+                    {ok, E} ->
                         case crypto:hash(md5, "Don't panic") of
                             Md5Hash1 ->
                                 ct:fail(fail_to_load_engine_still_original);
@@ -230,7 +234,168 @@ engine_load_some_methods(Config) when is_list(Config) ->
                 end
            catch
                error:notsup ->
-                  {skip, "Engine not supported on this OpenSSL version"}
+                  {skip, "Engine not supported on this SSL version"}
+           end
+    end.
+
+multiple_engine_load()->
+    [{doc, "Use a dummy md5 engine that does not implement md5"
+      "but rather returns a static binary to test that crypto:engine_load "
+      "functions works when called multiple times."}].
+
+multiple_engine_load(Config) when is_list(Config) ->
+    case crypto:get_test_engine() of
+        {error, notexist} ->
+            {skip, "OTP Test engine not found"};
+        {ok, Engine} ->
+            try
+                Md5Hash1 =  <<106,30,3,246,166,222,229,158,244,217,241,179,50,232,107,109>>,
+                Md5Hash1 = crypto:hash(md5, "Don't panic"),
+                Md5Hash2 =  <<0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15>>,
+                case crypto:engine_load(<<"dynamic">>,
+                                        [{<<"SO_PATH">>, Engine},
+                                         <<"LOAD">>],
+                                        []) of
+                    {ok, E} ->
+                        {ok, E1} = crypto:engine_load(<<"dynamic">>,
+                                        [{<<"SO_PATH">>, Engine},
+                                         <<"LOAD">>],
+                                        []),
+                        {ok, E2} = crypto:engine_load(<<"dynamic">>,
+                                        [{<<"SO_PATH">>, Engine},
+                                         <<"LOAD">>],
+                                        []),
+                        case crypto:hash(md5, "Don't panic") of
+                            Md5Hash1 ->
+                                ct:fail(fail_to_load_still_original_engine);
+                            Md5Hash2 ->
+                                ok;
+                            _ ->
+                                ct:fail(fail_to_load_engine)
+                        end,
+                        ok = crypto:engine_unload(E2),
+                        case crypto:hash(md5, "Don't panic") of
+                            Md5Hash1 ->
+                                ct:fail(fail_to_load_still_original_engine);
+                            Md5Hash2 ->
+                                ok;
+                            _ ->
+                                ct:fail(fail_to_load_engine)
+                        end,
+                        ok = crypto:engine_unload(E),
+                        case crypto:hash(md5, "Don't panic") of
+                            Md5Hash1 ->
+                                ct:fail(fail_to_load_still_original_engine);
+                            Md5Hash2 ->
+                                ok;
+                            _ ->
+                                ct:fail(fail_to_load_engine)
+                        end,
+                        ok = crypto:engine_unload(E1),
+                        case crypto:hash(md5, "Don't panic") of
+                            Md5Hash2 ->
+                                ct:fail(fail_to_unload_still_test_engine);
+                            Md5Hash1 ->
+                                ok;
+                            _ ->
+                                ct:fail(fail_to_unload_engine)
+                        end;
+                    {error, bad_engine_id} ->
+                        {skip, "Dynamic Engine not supported"}
+                end
+           catch
+               error:notsup ->
+                  {skip, "Engine not supported on this SSL version"}
+           end
+    end.
+
+engine_list()->
+    [{doc, "Test add and remove engine ID to the SSL internal engine list."}].
+
+engine_list(Config) when is_list(Config) ->
+    case crypto:get_test_engine() of
+        {error, notexist} ->
+            {skip, "OTP Test engine not found"};
+        {ok, Engine} ->
+            try
+                EngineList0 = crypto:engine_list(),
+                case crypto:engine_load(<<"dynamic">>,
+                                        [{<<"SO_PATH">>, Engine},
+                                         <<"LOAD">>],
+                                        []) of
+                    {ok, E} ->
+                        EngineList0 = crypto:engine_list(),
+                        ok = crypto:engine_add(E),
+                        [<<"MD5">>] = lists:subtract(crypto:engine_list(), EngineList0),
+                        ok = crypto:engine_remove(E),
+                        EngineList0 = crypto:engine_list(),
+                        ok = crypto:engine_unload(E);
+                    {error, bad_engine_id} ->
+                        {skip, "Dynamic Engine not supported"}
+                end
+           catch
+               error:notsup ->
+                  {skip, "Engine not supported on this SSL version"}
+           end
+    end.
+
+get_id_and_name()->
+    [{doc, "Test fetching id and name from an engine."}].
+
+get_id_and_name(Config) when is_list(Config) ->
+    case crypto:get_test_engine() of
+        {error, notexist} ->
+            {skip, "OTP Test engine not found"};
+        {ok, Engine} ->
+            try
+                case crypto:engine_load(<<"dynamic">>,
+                                        [{<<"SO_PATH">>, Engine},
+                                         <<"LOAD">>],
+                                        []) of
+                    {ok, E} ->
+                        <<"MD5">> = crypto:engine_get_id(E),
+                        <<"MD5 test engine">> = crypto:engine_get_name(E),
+                        ok = crypto:engine_unload(E);
+                    {error, bad_engine_id} ->
+                        {skip, "Dynamic Engine not supported"}
+                end
+           catch
+               error:notsup ->
+                  {skip, "Engine not supported on this SSL version"}
+           end
+    end.
+
+engine_by_id()->
+    [{doc, "Test fetching a new reference the the engine when the"
+     "engine id is added to the SSL engine list."}].
+
+engine_by_id(Config) when is_list(Config) ->
+    case crypto:get_test_engine() of
+        {error, notexist} ->
+            {skip, "OTP Test engine not found"};
+        {ok, Engine} ->
+            try
+                case crypto:engine_load(<<"dynamic">>,
+                                        [{<<"SO_PATH">>, Engine},
+                                         <<"LOAD">>],
+                                        []) of
+                    {ok, E} ->
+                        case crypto:engine_by_id(<<"MD5">>) of
+                            {error,bad_engine_id} ->
+                                ok;
+                            {ok, _} ->
+                                ct:fail(fail_engine_found)
+                        end,
+                        ok = crypto:engine_add(E),
+                        {ok, _E1} = crypto:engine_by_id(<<"MD5">>),
+                        ok = crypto:engine_remove(E),
+                        ok = crypto:engine_unload(E);
+                    {error, bad_engine_id} ->
+                        {skip, "Dynamic Engine not supported"}
+                end
+           catch
+               error:notsup ->
+                  {skip, "Engine not supported on this SSL version"}
            end
     end.
 
@@ -244,7 +409,7 @@ bad_arguments(Config) when is_list(Config) ->
         {error, notexist} ->
             {skip, "OTP Test engine not found"};
         {ok, Engine} ->
-            try 
+            try
                 try
                     crypto:engine_load(fail_engine, [], [])
                 catch
@@ -274,7 +439,7 @@ bad_arguments(Config) when is_list(Config) ->
                 end
           catch
               error:notsup ->
-                 {skip, "Engine not supported on this OpenSSL version"}
+                 {skip, "Engine not supported on this SSL version"}
           end
     end.
 
@@ -287,7 +452,7 @@ unknown_engine(Config) when is_list(Config) ->
         ok
     catch
         error:notsup ->
-           {skip, "Engine not supported on this OpenSSL version"}
+           {skip, "Engine not supported on this SSL version"}
     end.
 
 pre_command_fail_bad_value() ->
@@ -309,7 +474,7 @@ pre_command_fail_bad_value(Config) when is_list(Config) ->
         end
     catch
         error:notsup ->
-           {skip, "Engine not supported on this OpenSSL version"}
+           {skip, "Engine not supported on this SSL version"}
     end.
 
 pre_command_fail_bad_key() ->
@@ -332,9 +497,9 @@ pre_command_fail_bad_key(Config) when is_list(Config) ->
                         {skip, "Dynamic Engine not supported"}
                 end
         end
-   catch 
+   catch
        error:notsup ->
-          {skip, "Engine not supported on this OpenSSL version"}
+          {skip, "Engine not supported on this SSL version"}
    end.
 
 failed_engine_init()->
@@ -350,18 +515,20 @@ failed_engine_init(Config) when is_list(Config) ->
                                         [{<<"SO_PATH">>, Engine},
                                          {<<"ID">>, <<"MD5">>}],
                                         []) of
-                    {error, add_engine_failed} ->
+                    {error, engine_init_failed} ->
                         ok;
                     {error, bad_engine_id} ->
                         {skip, "Dynamic Engine not supported"}
                 end
         end
-   catch 
+   catch
        error:notsup ->
-          {skip, "Engine not supported on this OpenSSL version"}
+          {skip, "Engine not supported on this SSL version"}
    end.
 
 
+%%-------------------------------------------------------------------------
+%% Test the optional flag in ctrl comands
 ctrl_cmd_string()->
     [{doc, "Test that a not known optional ctrl comand do not fail"}].
 ctrl_cmd_string(Config) when is_list(Config) ->
@@ -375,22 +542,22 @@ ctrl_cmd_string(Config) when is_list(Config) ->
                                          {<<"ID">>, <<"MD5">>},
                                          <<"LOAD">>],
                                         []) of
-                    {ok, E} ->        
+                    {ok, E} ->
                         case crypto:engine_ctrl_cmd_string(E, <<"TEST">>, <<"17">>) of
                             ok ->
                                 ct:fail(fail_ctrl_cmd_should_fail);
-                            {error,ctrl_cmd_failed} -> 
+                            {error,ctrl_cmd_failed} ->
                                 ok
                         end,
-                        ok = crypto:engine_unload(E);              
+                        ok = crypto:engine_unload(E);
                     {error, bad_engine_id} ->
                         {skip, "Dynamic Engine not supported"}
                 end
         end
-   catch 
+   catch
        error:notsup ->
-          {skip, "Engine not supported on this OpenSSL version"}
-   end.        
+          {skip, "Engine not supported on this SSL version"}
+   end.
 
 ctrl_cmd_string_optional()->
     [{doc, "Test that a not known optional ctrl comand do not fail"}].
@@ -405,22 +572,63 @@ ctrl_cmd_string_optional(Config) when is_list(Config) ->
                                          {<<"ID">>, <<"MD5">>},
                                          <<"LOAD">>],
                                         []) of
-                    {ok, E} ->        
+                    {ok, E} ->
                         case crypto:engine_ctrl_cmd_string(E, <<"TEST">>, <<"17">>, true) of
                             ok ->
                                 ok;
-                            _ -> 
+                            _ ->
                                 ct:fail(fail_ctrl_cmd_string)
                         end,
-                        ok = crypto:engine_unload(E);              
+                        ok = crypto:engine_unload(E);
                     {error, bad_engine_id} ->
                         {skip, "Dynamic Engine not supported"}
                 end
         end
-   catch 
+   catch
        error:notsup ->
-          {skip, "Engine not supported on this OpenSSL version"}
-   end.        
+          {skip, "Engine not supported on this SSL version"}
+   end.
+
+ensure_load()->
+    [{doc, "Test the special ensure load function."}].
+
+ensure_load(Config) when is_list(Config) ->
+    case crypto:get_test_engine() of
+        {error, notexist} ->
+            {skip, "OTP Test engine not found"};
+        {ok, Engine} ->
+            try
+                Md5Hash1 =  <<106,30,3,246,166,222,229,158,244,217,241,179,50,232,107,109>>,
+                Md5Hash1 = crypto:hash(md5, "Don't panic"),
+                Md5Hash2 =  <<0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15>>,
+                case crypto:ensure_engine_loaded(<<"MD5">>, Engine) of
+                    {ok, E} ->
+                        {ok, _E1} = crypto:ensure_engine_loaded(<<"MD5">>, Engine),
+                        case crypto:hash(md5, "Don't panic") of
+                            Md5Hash1 ->
+                                ct:fail(fail_to_load_still_original_engine);
+                            Md5Hash2 ->
+                                ok;
+                            _ ->
+                                ct:fail(fail_to_load_engine)
+                        end,
+                        ok = crypto:ensure_engine_unloaded(E),
+                        case crypto:hash(md5, "Don't panic") of
+                            Md5Hash2 ->
+                                ct:fail(fail_to_unload_still_test_engine);
+                            Md5Hash1 ->
+                                ok;
+                            _ ->
+                                ct:fail(fail_to_unload_engine)
+                        end;
+                    {error, bad_engine_id} ->
+                        {skip, "Dynamic Engine not supported"}
+                end
+           catch
+               error:notsup ->
+                  {skip, "Engine not supported on this SSL version"}
+           end
+    end.
 
 %%%----------------------------------------------------------------
 %%% Pub/priv key storage tests.  Thoose are for testing the crypto.erl
@@ -465,7 +673,7 @@ sign_verify_rsa_pwd_bad_pwd(Config) ->
         _ -> {fail, "PWD prot pubkey sign succeded with no pwd!"}
     catch
         error:badarg -> ok
-    end.  
+    end.
 
 priv_encrypt_pub_decrypt_rsa(Config) ->
     Priv = #{engine => engine_ref(Config),
@@ -538,7 +746,7 @@ get_pub_from_priv_key_rsa_pwd_no_pwd(Config) ->
             {skip, "RSA not supported"};
         {error, Error} ->
             {fail, {wrong_error,Error}};
-        Pub -> 
+        Pub ->
             ct:log("rsa Pub = ~p",[Pub]),
             {fail, "PWD prot pubkey fetch succeded although no pwd!"}
     end.
@@ -554,7 +762,7 @@ get_pub_from_priv_key_rsa_pwd_bad_pwd(Config) ->
             {skip, "RSA not supported"};
         {error, Error} ->
             {fail, {wrong_error,Error}};
-        Pub -> 
+        Pub ->
             ct:log("rsa Pub = ~p",[Pub]),
             {fail, "PWD prot pubkey fetch succeded with bad pwd!"}
     end.
@@ -588,7 +796,7 @@ get_pub_from_priv_key_ecdsa(Config) ->
             ct:log("ecdsa Pub = ~p",[Pub]),
             sign_verify(ecdsa, sha, Priv, Pub)
     end.
-    
+
 %%%================================================================
 %%% Help for engine_stored_pub_priv_keys* test cases
 %%%
-- 
cgit v1.2.3


From 7baf76378a2a2f8e0c5ded7d7b9278f48e2b2573 Mon Sep 17 00:00:00 2001
From: Erlang/OTP <otp@erlang.org>
Date: Thu, 23 Aug 2018 08:00:44 +0200
Subject: Prepare release

---
 lib/crypto/doc/src/notes.xml | 31 +++++++++++++++++++++++++++++++
 lib/crypto/vsn.mk            |  2 +-
 lib/mnesia/doc/src/notes.xml | 18 +++++++++++++++++-
 lib/mnesia/vsn.mk            |  2 +-
 4 files changed, 50 insertions(+), 3 deletions(-)

diff --git a/lib/crypto/doc/src/notes.xml b/lib/crypto/doc/src/notes.xml
index 0f8c50a35d..21ce2891dc 100644
--- a/lib/crypto/doc/src/notes.xml
+++ b/lib/crypto/doc/src/notes.xml
@@ -31,6 +31,37 @@
   </header>
   <p>This document describes the changes made to the Crypto application.</p>
 
+<section><title>Crypto 4.2.2.2</title>
+
+    <section><title>Improvements and New Features</title>
+      <list>
+        <item>
+	    <p> Update the crypto engine functions to handle multiple
+	    loads of an engine. </p> <p><c>engine_load/3/4</c> is
+	    updated so it doesn't add the engine ID to OpenSSLs
+	    internal list of engines which makes it possible to run
+	    the engine_load more than once if it doesn't contain
+	    global data.</p> <p>Added <c>ensure_engine_loaded/2/3</c>
+	    which guarantees that the engine just is loaded once and
+	    the following calls just returns a reference to it. This
+	    is done by add the ID to the internal OpenSSL list and
+	    check if it is already registered when the function is
+	    called.</p> <p>Added <c>ensure_engine_unloaded/1/2</c> to
+	    unload engines loaded with ensure_engine_loaded.</p>
+	    <p>Then some more utility functions are added.</p>
+	    <p><c>engine_add/1</c>, adds the engine to OpenSSL
+	    internal list</p> <p><c>engine_remove/1</c>, remove the
+	    engine from OpenSSL internal list</p>
+	    <p><c>engine_get_id/1</c>, fetch the engines id</p>
+	    <p><c>engine_get_name/1</c>, fetch the engine name</p>
+          <p>
+	    Own Id: OTP-15233</p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
 <section><title>Crypto 4.2.2.1</title>
 
     <section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/crypto/vsn.mk b/lib/crypto/vsn.mk
index 93a97ec88b..fe9c19b971 100644
--- a/lib/crypto/vsn.mk
+++ b/lib/crypto/vsn.mk
@@ -1 +1 @@
-CRYPTO_VSN = 4.2.2.1
+CRYPTO_VSN = 4.2.2.2
diff --git a/lib/mnesia/doc/src/notes.xml b/lib/mnesia/doc/src/notes.xml
index 151c9f6f21..2b715f8f47 100644
--- a/lib/mnesia/doc/src/notes.xml
+++ b/lib/mnesia/doc/src/notes.xml
@@ -39,7 +39,23 @@
     thus constitutes one section in this document. The title of each
     section is the version number of Mnesia.</p>
 
-  <section><title>Mnesia 4.15.3.1</title>
+  <section><title>Mnesia 4.15.3.2</title>
+
+    <section><title>Fixed Bugs and Malfunctions</title>
+      <list>
+        <item>
+          <p>
+	    Fixed a bug where the bag table index data was not
+	    deleted when objects were deleted.</p>
+          <p>
+	    Own Id: OTP-15243</p>
+        </item>
+      </list>
+    </section>
+
+</section>
+
+<section><title>Mnesia 4.15.3.1</title>
 
     <section><title>Fixed Bugs and Malfunctions</title>
       <list>
diff --git a/lib/mnesia/vsn.mk b/lib/mnesia/vsn.mk
index 73118464d1..9079a6f02a 100644
--- a/lib/mnesia/vsn.mk
+++ b/lib/mnesia/vsn.mk
@@ -1 +1 @@
-MNESIA_VSN = 4.15.3.1
+MNESIA_VSN = 4.15.3.2
-- 
cgit v1.2.3


From 8bb0d5d4049b6b2ec41f6cc3e154e9f688029bef Mon Sep 17 00:00:00 2001
From: Erlang/OTP <otp@erlang.org>
Date: Thu, 23 Aug 2018 08:00:46 +0200
Subject: Updated OTP version

---
 OTP_VERSION        | 2 +-
 otp_versions.table | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/OTP_VERSION b/OTP_VERSION
index 5958997c80..3f862fbc89 100644
--- a/OTP_VERSION
+++ b/OTP_VERSION
@@ -1 +1 @@
-20.3.8.6
+20.3.8.7
diff --git a/otp_versions.table b/otp_versions.table
index 05a456446c..5a90b0dc55 100644
--- a/otp_versions.table
+++ b/otp_versions.table
@@ -1,3 +1,4 @@
+OTP-20.3.8.7 : crypto-4.2.2.2 mnesia-4.15.3.2 # asn1-5.0.5.1 common_test-1.15.4 compiler-7.1.5.1 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 debugger-4.2.4 dialyzer-3.2.4 diameter-2.1.4 edoc-0.9.2 eldap-1.2.3 erl_docgen-0.7.3 erl_interface-3.10.2.1 erts-9.3.3.3 et-1.6.1 eunit-2.3.5 hipe-3.17.1 ic-4.4.4.2 inets-6.5.2.3 jinterface-1.8.1 kernel-5.4.3.2 megaco-3.18.3 observer-2.7 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 runtime_tools-1.12.5 sasl-3.1.2 snmp-5.2.11 ssh-4.6.9.1 ssl-8.2.6.2 stdlib-3.4.5 syntax_tools-2.1.4.1 tools-2.11.2 wx-1.8.3 xmerl-1.3.16 :
 OTP-20.3.8.6 : inets-6.5.2.3 # asn1-5.0.5.1 common_test-1.15.4 compiler-7.1.5.1 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 crypto-4.2.2.1 debugger-4.2.4 dialyzer-3.2.4 diameter-2.1.4 edoc-0.9.2 eldap-1.2.3 erl_docgen-0.7.3 erl_interface-3.10.2.1 erts-9.3.3.3 et-1.6.1 eunit-2.3.5 hipe-3.17.1 ic-4.4.4.2 jinterface-1.8.1 kernel-5.4.3.2 megaco-3.18.3 mnesia-4.15.3.1 observer-2.7 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 runtime_tools-1.12.5 sasl-3.1.2 snmp-5.2.11 ssh-4.6.9.1 ssl-8.2.6.2 stdlib-3.4.5 syntax_tools-2.1.4.1 tools-2.11.2 wx-1.8.3 xmerl-1.3.16 :
 OTP-20.3.8.5 : compiler-7.1.5.1 crypto-4.2.2.1 erts-9.3.3.3 mnesia-4.15.3.1 ssl-8.2.6.2 # asn1-5.0.5.1 common_test-1.15.4 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 debugger-4.2.4 dialyzer-3.2.4 diameter-2.1.4 edoc-0.9.2 eldap-1.2.3 erl_docgen-0.7.3 erl_interface-3.10.2.1 et-1.6.1 eunit-2.3.5 hipe-3.17.1 ic-4.4.4.2 inets-6.5.2.2 jinterface-1.8.1 kernel-5.4.3.2 megaco-3.18.3 observer-2.7 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 runtime_tools-1.12.5 sasl-3.1.2 snmp-5.2.11 ssh-4.6.9.1 stdlib-3.4.5 syntax_tools-2.1.4.1 tools-2.11.2 wx-1.8.3 xmerl-1.3.16 :
 OTP-20.3.8.4 : asn1-5.0.5.1 # common_test-1.15.4 compiler-7.1.5 cosEvent-2.2.2 cosEventDomain-1.2.2 cosFileTransfer-1.2.2 cosNotification-1.2.3 cosProperty-1.2.3 cosTime-1.2.3 cosTransactions-1.3.3 crypto-4.2.2 debugger-4.2.4 dialyzer-3.2.4 diameter-2.1.4 edoc-0.9.2 eldap-1.2.3 erl_docgen-0.7.3 erl_interface-3.10.2.1 erts-9.3.3.2 et-1.6.1 eunit-2.3.5 hipe-3.17.1 ic-4.4.4.2 inets-6.5.2.2 jinterface-1.8.1 kernel-5.4.3.2 megaco-3.18.3 mnesia-4.15.3 observer-2.7 odbc-2.12.1 orber-3.8.4 os_mon-2.4.4 otp_mibs-1.1.2 parsetools-2.1.6 public_key-1.5.2 reltool-0.7.5 runtime_tools-1.12.5 sasl-3.1.2 snmp-5.2.11 ssh-4.6.9.1 ssl-8.2.6.1 stdlib-3.4.5 syntax_tools-2.1.4.1 tools-2.11.2 wx-1.8.3 xmerl-1.3.16 :
-- 
cgit v1.2.3