diff options
-rw-r--r-- | deps/rabbit/apps/rabbitmq_prelaunch/src/rabbit_boot_state_systemd.erl | 173 |
1 files changed, 30 insertions, 143 deletions
diff --git a/deps/rabbit/apps/rabbitmq_prelaunch/src/rabbit_boot_state_systemd.erl b/deps/rabbit/apps/rabbitmq_prelaunch/src/rabbit_boot_state_systemd.erl index a58d6d93ed..5627a65daa 100644 --- a/deps/rabbit/apps/rabbitmq_prelaunch/src/rabbit_boot_state_systemd.erl +++ b/deps/rabbit/apps/rabbitmq_prelaunch/src/rabbit_boot_state_systemd.erl @@ -21,42 +21,20 @@ terminate/2, code_change/3]). --record(state, {mechanism, - sd_notify_module, - socket}). - -define(LOG_PREFIX, "Boot state/systemd: "). start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). init([]) -> - case os:type() of - {unix, _} -> - case code:load_file(sd_notify) of - {module, sd_notify} -> - {ok, #state{mechanism = legacy, - sd_notify_module = sd_notify}}; - {error, _} -> - case os:getenv("NOTIFY_SOCKET") of - false -> - ignore; - "" -> - ignore; - Socket -> - {ok, #state{mechanism = socat, - socket = Socket}} - end - end; - _ -> - ignore - end. + {ok, _} = application:ensure_all_started(systemd), + {ok, #{}}. handle_call(_Request, _From, State) -> {noreply, State}. handle_cast({notify_boot_state, BootState}, State) -> - notify_boot_state(BootState, State), + _ = notify_boot_state(BootState), {noreply, State}. terminate(normal, _State) -> @@ -65,124 +43,33 @@ terminate(normal, _State) -> code_change(_OldVsn, State, _Extra) -> {ok, State}. -%%% Private +%% Private -notify_boot_state(ready = BootState, - #state{mechanism = legacy, sd_notify_module = SDNotify}) -> - ?LOG_DEBUG( - ?LOG_PREFIX "notifying of state `~s` (via native module)", - [BootState], - #{domain => ?RMQLOG_DOMAIN_PRELAUNCH}), - sd_notify_legacy(SDNotify); -notify_boot_state(ready = BootState, - #state{mechanism = socat, socket = Socket}) -> +notify_boot_state(BootState) + when BootState =:= ready orelse BootState =:= stopping -> + Status = boot_state_to_desc(BootState), ?LOG_DEBUG( - ?LOG_PREFIX "notifying of state `~s` (via socat(1))", - [BootState], - #{domain => ?RMQLOG_DOMAIN_PRELAUNCH}), - sd_notify_socat(Socket); -notify_boot_state(BootState, _) -> - ?LOG_DEBUG( - ?LOG_PREFIX "ignoring state `~s`", - [BootState], - #{domain => ?RMQLOG_DOMAIN_PRELAUNCH}), - ok. - -sd_notify_message() -> - "READY=1\nSTATUS=Initialized\nMAINPID=" ++ os:getpid() ++ "\n". - -sd_notify_legacy(SDNotify) -> - SDNotify:sd_notify(0, sd_notify_message()). - -%% socat(1) is the most portable way the sd_notify could be -%% implemented in erlang, without introducing some NIF. Currently the -%% following issues prevent us from implementing it in a more -%% reasonable way: -%% - systemd-notify(1) is unstable for non-root users -%% - erlang doesn't support unix domain sockets. -%% -%% Some details on how we ended with such a solution: -%% https://github.com/rabbitmq/rabbitmq-server/issues/664 -sd_notify_socat(Socket) -> - case sd_current_unit() of - {ok, Unit} -> - ?LOG_DEBUG( - ?LOG_PREFIX "systemd unit for activation check: \"~s\"", - [Unit], - #{domain => ?RMQLOG_DOMAIN_PRELAUNCH}), - sd_notify_socat(Socket, Unit); - _ -> - ok - end. - -sd_notify_socat(Socket, Unit) -> - try sd_open_port(Socket) of - Port -> - Port ! {self(), {command, sd_notify_message()}}, - Result = sd_wait_activation(Port, Unit), - port_close(Port), - Result - catch - Class:Reason -> - ?LOG_DEBUG( - ?LOG_PREFIX "Failed to start socat(1): ~p:~p", - [Class, Reason], - #{domain => ?RMQLOG_DOMAIN_PRELAUNCH}), - false - end. - -sd_current_unit() -> - CmdOut = os:cmd("ps -o unit= -p " ++ os:getpid()), - Ret = (catch re:run(CmdOut, - "([-.@0-9a-zA-Z]+)", - [unicode, {capture, all_but_first, list}])), - case Ret of - {'EXIT', _} -> error; - {match, [Unit]} -> {ok, Unit}; - _ -> error - end. - -socat_socket_arg("@" ++ AbstractUnixSocket) -> - "abstract-sendto:" ++ AbstractUnixSocket; -socat_socket_arg(UnixSocket) -> - "unix-sendto:" ++ UnixSocket. - -sd_open_port(Socket) -> - open_port( - {spawn_executable, os:find_executable("socat")}, - [{args, [socat_socket_arg(Socket), "STDIO"]}, - use_stdio, out]). - -sd_wait_activation(Port, Unit) -> - case os:find_executable("systemctl") of - false -> - ?LOG_DEBUG( - ?LOG_PREFIX "systemctl(1) unavailable, falling back to sleep", - [], - #{domain => ?RMQLOG_DOMAIN_PRELAUNCH}), - timer:sleep(5000), - ok; - _ -> - sd_wait_activation(Port, Unit, 10) - end. - -sd_wait_activation(_, _, 0) -> + ?LOG_PREFIX "notifying of state `~s`", + [BootState], + #{domain => ?RMQLOG_DOMAIN_PRELAUNCH}), + systemd:notify([BootState, {status, Status}]); +notify_boot_state(BootState) -> + Status = boot_state_to_desc(BootState), ?LOG_DEBUG( - ?LOG_PREFIX "service still in 'activating' state, bailing out", - [], - #{domain => ?RMQLOG_DOMAIN_PRELAUNCH}), - ok; -sd_wait_activation(Port, Unit, AttemptsLeft) -> - Ret = os:cmd("systemctl show --property=ActiveState -- '" ++ Unit ++ "'"), - case Ret of - "ActiveState=activating\n" -> - timer:sleep(1000), - sd_wait_activation(Port, Unit, AttemptsLeft - 1); - "ActiveState=" ++ _ -> - ok; - _ = Err -> - ?LOG_DEBUG( - ?LOG_PREFIX "unexpected status from systemd: ~p", [Err], - #{domain => ?RMQLOG_DOMAIN_PRELAUNCH}), - ok - end. + ?LOG_PREFIX "sending non-systemd state (~s) as status description: ~s", + [BootState, Status], + #{domain => ?RMQLOG_DOMAIN_PRELAUNCH}), + systemd:notify({status, Status}). + +boot_state_to_desc(stopped) -> + ""; +boot_state_to_desc(booting) -> + "Startup in progress"; +boot_state_to_desc(core_started) -> + "Startup in progress (core ready, starting plugins)"; +boot_state_to_desc(ready) -> + ""; +boot_state_to_desc(stopping) -> + ""; +boot_state_to_desc(BootState) -> + atom_to_list(BootState). |