summaryrefslogtreecommitdiff
path: root/src/rabbit_prelaunch.erl
blob: 4cc9cd12f13dc48822e2bb71ac23b814b32768ef (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
%% The contents of this file are subject to the Mozilla Public License
%% Version 1.1 (the "License"); you may not use this file except in
%% compliance with the License. You may obtain a copy of the License
%% at http://www.mozilla.org/MPL/
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and
%% limitations under the License.
%%
%% The Original Code is RabbitMQ.
%%
%% The Initial Developer of the Original Code is GoPivotal, Inc.
%% Copyright (c) 2007-2014 GoPivotal, Inc.  All rights reserved.
%%

-module(rabbit_prelaunch).

-export([start/0, stop/0]).

-import(rabbit_misc, [pget/2, pget/3]).

-include("rabbit.hrl").

-define(DIST_PORT_NOT_CONFIGURED, 0).
-define(ERROR_CODE, 1).
-define(DIST_PORT_CONFIGURED, 2).

%%----------------------------------------------------------------------------
%% Specs
%%----------------------------------------------------------------------------

-ifdef(use_specs).

-spec(start/0 :: () -> no_return()).
-spec(stop/0 :: () -> 'ok').

-endif.

%%----------------------------------------------------------------------------

start() ->
    case init:get_plain_arguments() of
        [NodeStr] ->
            Node = rabbit_nodes:make(NodeStr),
            {NodeName, NodeHost} = rabbit_nodes:parts(Node),
            ok = duplicate_node_check(NodeName, NodeHost),
            ok = dist_port_set_check(),
            ok = dist_port_use_check(NodeHost);
        [] ->
            %% Ignore running node while installing windows service
            ok = dist_port_set_check(),
            ok
    end,
    rabbit_misc:quit(?DIST_PORT_NOT_CONFIGURED),
    ok.

stop() ->
    ok.

%%----------------------------------------------------------------------------

%% Check whether a node with the same name is already running
duplicate_node_check(NodeName, NodeHost) ->
    case rabbit_nodes:names(NodeHost) of
        {ok, NamePorts}  ->
            case proplists:is_defined(NodeName, NamePorts) of
                true -> io:format(
                          "ERROR: node with name ~p already running on ~p~n",
                          [NodeName, NodeHost]),
                        rabbit_misc:quit(?ERROR_CODE);
                false -> ok
            end;
        {error, EpmdReason} ->
            io:format("ERROR: epmd error for host ~s: ~s~n",
                      [NodeHost, rabbit_misc:format_inet_error(EpmdReason)]),
            rabbit_misc:quit(?ERROR_CODE)
    end.

dist_port_set_check() ->
    case os:getenv("RABBITMQ_CONFIG_FILE") of
        false ->
            ok;
        File ->
            case file:consult(File ++ ".config") of
                {ok, [Config]} ->
                    Kernel = pget(kernel, Config, []),
                    case {pget(inet_dist_listen_min, Kernel, none),
                          pget(inet_dist_listen_max, Kernel, none)} of
                        {none, none} -> ok;
                        _            -> rabbit_misc:quit(?DIST_PORT_CONFIGURED)
                    end;
                {error, _} ->
                    %% TODO can we present errors more nicely here
                    %% than after -config has failed?
                    ok
            end
    end.

dist_port_use_check(NodeHost) ->
    case os:getenv("RABBITMQ_DIST_PORT") of
        false   -> ok;
        PortStr -> Port = list_to_integer(PortStr),
                   case gen_tcp:listen(Port, [inet, {reuseaddr, true}]) of
                       {ok, Sock} -> gen_tcp:close(Sock);
                       {error, _} -> dist_port_use_check_fail(Port, NodeHost)
                   end
    end.

-ifdef(use_specs).
-spec(dist_port_use_check_fail/2 :: (non_neg_integer(), string()) ->
                                         no_return()).
-endif.
dist_port_use_check_fail(Port, Host) ->
    {ok, Names} = rabbit_nodes:names(Host),
    case [N || {N, P} <- Names, P =:= Port] of
        []     -> io:format("ERROR: distribution port ~b in use on ~s "
                            "(by non-Erlang process?)~n", [Port, Host]);
        [Name] -> io:format("ERROR: distribution port ~b in use by ~s@~s~n",
                            [Port, Name, Host])
    end,
    rabbit_misc:quit(?ERROR_CODE).