1%% This Source Code Form is subject to the terms of the Mozilla Public 2%% License, v. 2.0. If a copy of the MPL was not distributed with this 3%% file, You can obtain one at https://mozilla.org/MPL/2.0/. 4%% 5%% Copyright (c) 2007-2021 VMware, Inc. or its affiliates. All rights reserved. 6%% 7 8-module(rabbit_epmd_monitor). 9 10-behaviour(gen_server). 11 12-export([start_link/0]). 13 14-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, 15 code_change/3]). 16 17-record(state, {timer, mod, me, host, port}). 18 19-define(SERVER, ?MODULE). 20-define(CHECK_FREQUENCY, 60000). 21 22%%---------------------------------------------------------------------------- 23%% It's possible for epmd to be killed out from underneath us. If that 24%% happens, then obviously clustering and rabbitmqctl stop 25%% working. This process checks up on epmd and restarts it / 26%% re-registers us with it if it has gone away. 27%% 28%% How could epmd be killed? 29%% 30%% 1) The most popular way for this to happen is when running as a 31%% Windows service. The user starts rabbitmqctl first, and this starts 32%% epmd under the user's account. When they log out epmd is killed. 33%% 34%% 2) Some packagings of (non-RabbitMQ?) Erlang apps might do "killall 35%% epmd" as a shutdown or uninstall step. 36%% ---------------------------------------------------------------------------- 37 38-spec start_link() -> rabbit_types:ok_pid_or_error(). 39 40start_link() -> 41 gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). 42 43init([]) -> 44 {Me, Host} = rabbit_nodes:parts(node()), 45 Mod = net_kernel:epmd_module(), 46 {ok, Port} = handle_port_please(init, Mod:port_please(Me, Host), Me, undefined), 47 State = #state{mod = Mod, me = Me, host = Host, port = Port}, 48 {ok, ensure_timer(State)}. 49 50handle_call(_Request, _From, State) -> 51 {noreply, State}. 52 53handle_cast(check, State0) -> 54 {ok, State1} = check_epmd(State0), 55 {noreply, ensure_timer(State1#state{timer = undefined})}; 56handle_cast(_Msg, State) -> 57 {noreply, State}. 58 59handle_info(check, State0) -> 60 {ok, State1} = check_epmd(State0), 61 {noreply, ensure_timer(State1#state{timer = undefined})}; 62 63handle_info(_Info, State) -> 64 {noreply, State}. 65 66terminate(_Reason, _State) -> 67 ok. 68 69code_change(_OldVsn, State, _Extra) -> 70 {ok, State}. 71 72%%---------------------------------------------------------------------------- 73 74ensure_timer(State) -> 75 rabbit_misc:ensure_timer(State, #state.timer, ?CHECK_FREQUENCY, check). 76 77check_epmd(State = #state{mod = Mod, 78 me = Me, 79 host = Host, 80 port = Port0}) -> 81 {ok, Port1} = handle_port_please(check, Mod:port_please(Me, Host), Me, Port0), 82 rabbit_nodes:ensure_epmd(), 83 Mod:register_node(Me, Port1), 84 {ok, State#state{port = Port1}}. 85 86handle_port_please(init, noport, Me, Port) -> 87 rabbit_log:info("epmd does not know us, re-registering as ~s", [Me]), 88 {ok, Port}; 89handle_port_please(check, noport, Me, Port) -> 90 rabbit_log:warning("epmd does not know us, re-registering ~s at port ~b", [Me, Port]), 91 {ok, Port}; 92handle_port_please(_, closed, _Me, Port) -> 93 rabbit_log:error("epmd monitor failed to retrieve our port from epmd: closed"), 94 {ok, Port}; 95handle_port_please(init, {port, NewPort, _Version}, _Me, _Port) -> 96 rabbit_log:info("epmd monitor knows us, inter-node communication (distribution) port: ~p", [NewPort]), 97 {ok, NewPort}; 98handle_port_please(check, {port, NewPort, _Version}, _Me, _Port) -> 99 {ok, NewPort}; 100handle_port_please(_, {error, Error}, _Me, Port) -> 101 rabbit_log:error("epmd monitor failed to retrieve our port from epmd: ~p", [Error]), 102 {ok, Port}. 103