21
21
terminate /2 ,
22
22
code_change /3 ]).
23
23
24
- -record (state , {mechanism ,
25
- sd_notify_module ,
26
- socket }).
27
-
28
24
-define (LOG_PREFIX , " Boot state/systemd: " ).
29
25
30
26
start_link () ->
31
27
gen_server :start_link ({local , ? MODULE }, ? MODULE , [], []).
32
28
33
29
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 , #{}}.
54
32
55
33
handle_call (_Request , _From , State ) ->
56
34
{noreply , State }.
57
35
58
36
handle_cast ({notify_boot_state , BootState }, State ) ->
59
- notify_boot_state (BootState , State ),
37
+ _ = notify_boot_state (BootState ),
60
38
{noreply , State }.
61
39
62
40
terminate (normal , _State ) ->
@@ -65,124 +43,33 @@ terminate(normal, _State) ->
65
43
code_change (_OldVsn , State , _Extra ) ->
66
44
{ok , State }.
67
45
68
- % %% Private
46
+ % % Private
69
47
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 ),
79
51
? 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\n STATUS=Initialized\n MAINPID=" ++ 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 ),
170
58
? 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