1-module(rabbit_prelaunch_dist). 2 3-include_lib("eunit/include/eunit.hrl"). 4-include_lib("kernel/include/logger.hrl"). 5 6-include_lib("rabbit_common/include/logging.hrl"). 7 8-export([setup/1]). 9 10setup(#{nodename := Node, nodename_type := NameType} = Context) -> 11 ?LOG_DEBUG( 12 "~n== Erlang distribution ==", [], 13 #{domain => ?RMQLOG_DOMAIN_PRELAUNCH}), 14 ?LOG_DEBUG( 15 "Rqeuested node name: ~s (type: ~s)", 16 [Node, NameType], 17 #{domain => ?RMQLOG_DOMAIN_PRELAUNCH}), 18 case node() of 19 nonode@nohost -> 20 ok = rabbit_nodes_common:ensure_epmd(), 21 ok = dist_port_range_check(Context), 22 ok = dist_port_use_check(Context), 23 ok = duplicate_node_check(Context), 24 25 ok = do_setup(Context); 26 Node -> 27 ?LOG_DEBUG( 28 "Erlang distribution already running", [], 29 #{domain => ?RMQLOG_DOMAIN_PRELAUNCH}), 30 ok; 31 Unexpected -> 32 throw({error, {erlang_dist_running_with_unexpected_nodename, 33 Unexpected, Node}}) 34 end, 35 ok. 36 37do_setup(#{nodename := Node, 38 nodename_type := NameType, 39 var_origins := Origins} = Config) -> 40 ?LOG_DEBUG( 41 "Starting Erlang distribution", 42 #{domain => ?RMQLOG_DOMAIN_PRELAUNCH}), 43 case application:get_env(kernel, net_ticktime) of 44 {ok, Ticktime} when is_integer(Ticktime) andalso Ticktime >= 1 -> 45 %% The value passed to net_kernel:start/1 is the 46 %% "minimum transition traffic interval" as defined in 47 %% net_kernel:set_net_ticktime/1. 48 MTTI = Ticktime * 1000 div 4, 49 {ok, _} = net_kernel:start([Node, NameType, MTTI]), 50 ok; 51 _ -> 52 {ok, _} = net_kernel:start([Node, NameType]), 53 ok 54 end, 55 56 %% Override the Erlang cookie if one was set in the environment. 57 case maps:get(erlang_cookie, Origins, default) of 58 environment -> 59 ?LOG_WARNING( 60 "Overriding Erlang cookie using the value set in the environment", 61 #{domain => ?RMQLOG_DOMAIN_PRELAUNCH}), 62 Cookie = maps:get(erlang_cookie, Config), 63 ?assert(is_atom(Cookie)), 64 true = erlang:set_cookie(node(), Cookie); 65 _ -> 66 ok 67 end, 68 ok. 69 70%% Check whether a node with the same name is already running 71duplicate_node_check(#{split_nodename := {NodeName, NodeHost}}) -> 72 ?LOG_DEBUG( 73 "Checking if node name ~s is already used", [NodeName], 74 #{domain => ?RMQLOG_DOMAIN_PRELAUNCH}), 75 PrelaunchName = rabbit_nodes_common:make( 76 {NodeName ++ "_prelaunch_" ++ os:getpid(), 77 "localhost"}), 78 {ok, _} = net_kernel:start([PrelaunchName, shortnames]), 79 case rabbit_nodes_common:names(NodeHost) of 80 {ok, NamePorts} -> 81 case proplists:is_defined(NodeName, NamePorts) of 82 true -> 83 throw({error, {duplicate_node_name, NodeName, NodeHost}}); 84 false -> 85 ok = net_kernel:stop(), 86 ok 87 end; 88 {error, EpmdReason} -> 89 throw({error, {epmd_error, NodeHost, EpmdReason}}) 90 end. 91 92dist_port_range_check(#{erlang_dist_tcp_port := DistTcpPort}) -> 93 ?LOG_DEBUG( 94 "Checking if TCP port ~b is valid", [DistTcpPort], 95 #{domain => ?RMQLOG_DOMAIN_PRELAUNCH}), 96 case DistTcpPort of 97 _ when DistTcpPort < 1 orelse DistTcpPort > 65535 -> 98 throw({error, {invalid_dist_port_range, DistTcpPort}}); 99 _ -> 100 ok 101 end. 102 103dist_port_use_check(#{split_nodename := {_, NodeHost}, 104 erlang_dist_tcp_port := DistTcpPort}) -> 105 ?LOG_DEBUG( 106 "Checking if TCP port ~b is available", [DistTcpPort], 107 #{domain => ?RMQLOG_DOMAIN_PRELAUNCH}), 108 dist_port_use_check_ipv4(NodeHost, DistTcpPort). 109 110dist_port_use_check_ipv4(NodeHost, Port) -> 111 case gen_tcp:listen(Port, [inet, {reuseaddr, true}]) of 112 {ok, Sock} -> gen_tcp:close(Sock); 113 {error, einval} -> dist_port_use_check_ipv6(NodeHost, Port); 114 {error, _} -> dist_port_use_check_fail(Port, NodeHost) 115 end. 116 117dist_port_use_check_ipv6(NodeHost, Port) -> 118 case gen_tcp:listen(Port, [inet6, {reuseaddr, true}]) of 119 {ok, Sock} -> gen_tcp:close(Sock); 120 {error, _} -> dist_port_use_check_fail(Port, NodeHost) 121 end. 122 123-spec dist_port_use_check_fail(non_neg_integer(), string()) -> 124 no_return(). 125 126dist_port_use_check_fail(Port, Host) -> 127 {ok, Names} = rabbit_nodes_common:names(Host), 128 case [N || {N, P} <- Names, P =:= Port] of 129 [] -> 130 throw({error, {dist_port_already_used, Port, not_erlang, Host}}); 131 [Name] -> 132 throw({error, {dist_port_already_used, Port, Name, Host}}) 133 end. 134