Skip to content

Commit 56035e4

Browse files
Merge pull request #2957 from rabbitmq/notify-systemd-using-erlang-systemd
Systemd: Use erlang-systemd to notify systemd of service status
2 parents e2893f4 + 4992b0c commit 56035e4

File tree

2 files changed

+31
-144
lines changed

2 files changed

+31
-144
lines changed

deps/rabbit/Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ PLT_APPS += mnesia
146146
dep_syslog = git https://github.com/schlagert/syslog 4.0.0
147147
dep_osiris = git https://github.com/rabbitmq/osiris master
148148
# TODO: Use systemd from Hex.pm, once there is a new post-0.6.0 release.
149-
dep_systemd = git https://github.com/hauleth/erlang-systemd 0ce748edffcb72bb028733e9ca4707cb30add853
149+
dep_systemd = git https://github.com/hauleth/erlang-systemd e732727b0b637eb29e8adc77a4eb46d7ebc0f41a
150150

151151
define usage_xml_to_erl
152152
$(subst __,_,$(patsubst $(DOCS_DIR)/rabbitmq%.1.xml, src/rabbit_%_usage.erl, $(subst -,_,$(1))))

deps/rabbit/apps/rabbitmq_prelaunch/src/rabbit_boot_state_systemd.erl

+30-143
Original file line numberDiff line numberDiff line change
@@ -21,42 +21,20 @@
2121
terminate/2,
2222
code_change/3]).
2323

24-
-record(state, {mechanism,
25-
sd_notify_module,
26-
socket}).
27-
2824
-define(LOG_PREFIX, "Boot state/systemd: ").
2925

3026
start_link() ->
3127
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
3228

3329
init([]) ->
34-
case os:type() of
35-
{unix, _} ->
36-
case code:load_file(sd_notify) of
37-
{module, sd_notify} ->
38-
{ok, #state{mechanism = legacy,
39-
sd_notify_module = sd_notify}};
40-
{error, _} ->
41-
case os:getenv("NOTIFY_SOCKET") of
42-
false ->
43-
ignore;
44-
"" ->
45-
ignore;
46-
Socket ->
47-
{ok, #state{mechanism = socat,
48-
socket = Socket}}
49-
end
50-
end;
51-
_ ->
52-
ignore
53-
end.
30+
{ok, _} = application:ensure_all_started(systemd),
31+
{ok, #{}}.
5432

5533
handle_call(_Request, _From, State) ->
5634
{noreply, State}.
5735

5836
handle_cast({notify_boot_state, BootState}, State) ->
59-
notify_boot_state(BootState, State),
37+
_ = notify_boot_state(BootState),
6038
{noreply, State}.
6139

6240
terminate(normal, _State) ->
@@ -65,124 +43,33 @@ terminate(normal, _State) ->
6543
code_change(_OldVsn, State, _Extra) ->
6644
{ok, State}.
6745

68-
%%% Private
46+
%% Private
6947

70-
notify_boot_state(ready = BootState,
71-
#state{mechanism = legacy, sd_notify_module = SDNotify}) ->
72-
?LOG_DEBUG(
73-
?LOG_PREFIX "notifying of state `~s` (via native module)",
74-
[BootState],
75-
#{domain => ?RMQLOG_DOMAIN_PRELAUNCH}),
76-
sd_notify_legacy(SDNotify);
77-
notify_boot_state(ready = BootState,
78-
#state{mechanism = socat, socket = Socket}) ->
48+
notify_boot_state(BootState)
49+
when BootState =:= ready orelse BootState =:= stopping ->
50+
Status = boot_state_to_desc(BootState),
7951
?LOG_DEBUG(
80-
?LOG_PREFIX "notifying of state `~s` (via socat(1))",
81-
[BootState],
82-
#{domain => ?RMQLOG_DOMAIN_PRELAUNCH}),
83-
sd_notify_socat(Socket);
84-
notify_boot_state(BootState, _) ->
85-
?LOG_DEBUG(
86-
?LOG_PREFIX "ignoring state `~s`",
87-
[BootState],
88-
#{domain => ?RMQLOG_DOMAIN_PRELAUNCH}),
89-
ok.
90-
91-
sd_notify_message() ->
92-
"READY=1\nSTATUS=Initialized\nMAINPID=" ++ os:getpid() ++ "\n".
93-
94-
sd_notify_legacy(SDNotify) ->
95-
SDNotify:sd_notify(0, sd_notify_message()).
96-
97-
%% socat(1) is the most portable way the sd_notify could be
98-
%% implemented in erlang, without introducing some NIF. Currently the
99-
%% following issues prevent us from implementing it in a more
100-
%% reasonable way:
101-
%% - systemd-notify(1) is unstable for non-root users
102-
%% - erlang doesn't support unix domain sockets.
103-
%%
104-
%% Some details on how we ended with such a solution:
105-
%% https://github.com/rabbitmq/rabbitmq-server/issues/664
106-
sd_notify_socat(Socket) ->
107-
case sd_current_unit() of
108-
{ok, Unit} ->
109-
?LOG_DEBUG(
110-
?LOG_PREFIX "systemd unit for activation check: \"~s\"",
111-
[Unit],
112-
#{domain => ?RMQLOG_DOMAIN_PRELAUNCH}),
113-
sd_notify_socat(Socket, Unit);
114-
_ ->
115-
ok
116-
end.
117-
118-
sd_notify_socat(Socket, Unit) ->
119-
try sd_open_port(Socket) of
120-
Port ->
121-
Port ! {self(), {command, sd_notify_message()}},
122-
Result = sd_wait_activation(Port, Unit),
123-
port_close(Port),
124-
Result
125-
catch
126-
Class:Reason ->
127-
?LOG_DEBUG(
128-
?LOG_PREFIX "Failed to start socat(1): ~p:~p",
129-
[Class, Reason],
130-
#{domain => ?RMQLOG_DOMAIN_PRELAUNCH}),
131-
false
132-
end.
133-
134-
sd_current_unit() ->
135-
CmdOut = os:cmd("ps -o unit= -p " ++ os:getpid()),
136-
Ret = (catch re:run(CmdOut,
137-
"([-.@0-9a-zA-Z]+)",
138-
[unicode, {capture, all_but_first, list}])),
139-
case Ret of
140-
{'EXIT', _} -> error;
141-
{match, [Unit]} -> {ok, Unit};
142-
_ -> error
143-
end.
144-
145-
socat_socket_arg("@" ++ AbstractUnixSocket) ->
146-
"abstract-sendto:" ++ AbstractUnixSocket;
147-
socat_socket_arg(UnixSocket) ->
148-
"unix-sendto:" ++ UnixSocket.
149-
150-
sd_open_port(Socket) ->
151-
open_port(
152-
{spawn_executable, os:find_executable("socat")},
153-
[{args, [socat_socket_arg(Socket), "STDIO"]},
154-
use_stdio, out]).
155-
156-
sd_wait_activation(Port, Unit) ->
157-
case os:find_executable("systemctl") of
158-
false ->
159-
?LOG_DEBUG(
160-
?LOG_PREFIX "systemctl(1) unavailable, falling back to sleep",
161-
[],
162-
#{domain => ?RMQLOG_DOMAIN_PRELAUNCH}),
163-
timer:sleep(5000),
164-
ok;
165-
_ ->
166-
sd_wait_activation(Port, Unit, 10)
167-
end.
168-
169-
sd_wait_activation(_, _, 0) ->
52+
?LOG_PREFIX "notifying of state `~s`",
53+
[BootState],
54+
#{domain => ?RMQLOG_DOMAIN_PRELAUNCH}),
55+
systemd:notify([BootState, {status, Status}]);
56+
notify_boot_state(BootState) ->
57+
Status = boot_state_to_desc(BootState),
17058
?LOG_DEBUG(
171-
?LOG_PREFIX "service still in 'activating' state, bailing out",
172-
[],
173-
#{domain => ?RMQLOG_DOMAIN_PRELAUNCH}),
174-
ok;
175-
sd_wait_activation(Port, Unit, AttemptsLeft) ->
176-
Ret = os:cmd("systemctl show --property=ActiveState -- '" ++ Unit ++ "'"),
177-
case Ret of
178-
"ActiveState=activating\n" ->
179-
timer:sleep(1000),
180-
sd_wait_activation(Port, Unit, AttemptsLeft - 1);
181-
"ActiveState=" ++ _ ->
182-
ok;
183-
_ = Err ->
184-
?LOG_DEBUG(
185-
?LOG_PREFIX "unexpected status from systemd: ~p", [Err],
186-
#{domain => ?RMQLOG_DOMAIN_PRELAUNCH}),
187-
ok
188-
end.
59+
?LOG_PREFIX "sending non-systemd state (~s) as status description: ~s",
60+
[BootState, Status],
61+
#{domain => ?RMQLOG_DOMAIN_PRELAUNCH}),
62+
systemd:notify({status, Status}).
63+
64+
boot_state_to_desc(stopped) ->
65+
"";
66+
boot_state_to_desc(booting) ->
67+
"Startup in progress";
68+
boot_state_to_desc(core_started) ->
69+
"Startup in progress (core ready, starting plugins)";
70+
boot_state_to_desc(ready) ->
71+
"";
72+
boot_state_to_desc(stopping) ->
73+
"";
74+
boot_state_to_desc(BootState) ->
75+
atom_to_list(BootState).

0 commit comments

Comments
 (0)