summaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
authorJean-Sébastien Pédron <jean-sebastien@rabbitmq.com>2020-02-11 09:45:52 +0100
committerJean-Sébastien Pédron <jean-sebastien@rabbitmq.com>2020-02-20 17:41:07 +0100
commit02aa73c36b720ae713d182ede1b8fc59966fcb41 (patch)
tree2fafa0f0e77f4a003885956bbd4e8ebe96e98e77 /apps
parent3aa744ff56946e291e898bb6482248da626ea3a2 (diff)
downloadrabbitmq-server-git-02aa73c36b720ae713d182ede1b8fc59966fcb41.tar.gz
Only handle SIGHUP and SIGTSTP
Here is a summary of RabbitMQ signal handling: == SIGTERM == After #2180, `rabbit` is a regular Erlang application and `application:stop(rabbit)` terminates RabbitMQ gracefully. This means that `init:stop()` shuts the service down properly. Therefore, the default handling of SIGTERM, which calls `init:stop()`, is correct. rabbitmq-server(8) already relies on this mechanism. This commit restores the default signal handler which already does the right thing. No need to do it ourselves. == SIGHUP and SIGTSTP == SIHGUP is usually used to reload the configuration without restarting the service and/or reopen log files after log file rotation. SIGTSTP is sent when a user types Ctrl+Z to pause a program and get back to the shell. Both signals have common behavior we can't satisfy currently. Note that we don't handle SIGCONT which is the one used to resume a program after SIGTSTP. The system default behavior is already good (the signal is discarded). To be consistent with rabbitmq-server(8) signal handling, the signals are ignored until we can do something about them. == SIGQUIT == This signal is meant to terminate the process immediately and create a core dump. If possible, temporary files should even be kept around. The default behavior in Erlang is to call `erlang:halt()` which is a sane default: we should not stop RabbitMQ gracefully. This commit restores this behavior. == SIGUSR1 and SIGUSR2 == Erlang uses SIGUSR1 to crash the VM and create an `erl_crash.dump` file. We already used this in the past to debug RabbitMQ. Again, a sane default. This commit restores this behavior. == Other signals == We keep the default behavior of all other signals. None of them are meant to stop the program gracefully anyway. If a user wants to stop RabbitMQ, he will already use the common accepted signal for this purpose (i.e. SIGTERM). Another change in this commit is the way we setup the signal handler: * We don't replace the default Erlang signal handler, just add ours. * We do it very early in rabbitmq_prelaunch. Like other things configured by this application, we do not uninstall the handler when the application is stopped. This reverts: * commit 6a4d2721d06b8c70a36e29e6c51bbef6608def55 * commit fa607e4a25d6142bb17a90b44ef757572a923c09
Diffstat (limited to 'apps')
-rw-r--r--apps/rabbitmq_prelaunch/src/rabbit_prelaunch.erl3
-rw-r--r--apps/rabbitmq_prelaunch/src/rabbit_prelaunch_sighandler.erl85
2 files changed, 88 insertions, 0 deletions
diff --git a/apps/rabbitmq_prelaunch/src/rabbit_prelaunch.erl b/apps/rabbitmq_prelaunch/src/rabbit_prelaunch.erl
index b564fadf51..ef15f47a75 100644
--- a/apps/rabbitmq_prelaunch/src/rabbit_prelaunch.erl
+++ b/apps/rabbitmq_prelaunch/src/rabbit_prelaunch.erl
@@ -48,6 +48,9 @@ do_run() ->
%% Configure dbg if requested.
rabbit_prelaunch_early_logging:enable_quick_dbg(rabbit_env:dbg_config()),
+ %% Setup signal handler.
+ ok = rabbit_prelaunch_sighandler:setup(),
+
%% We assert Mnesia is stopped before we run the prelaunch
%% phases.
%%
diff --git a/apps/rabbitmq_prelaunch/src/rabbit_prelaunch_sighandler.erl b/apps/rabbitmq_prelaunch/src/rabbit_prelaunch_sighandler.erl
new file mode 100644
index 0000000000..a6b5d3c2f7
--- /dev/null
+++ b/apps/rabbitmq_prelaunch/src/rabbit_prelaunch_sighandler.erl
@@ -0,0 +1,85 @@
+-module(rabbit_prelaunch_sighandler).
+-behaviour(gen_event).
+
+-export([setup/0,
+ init/1,
+ handle_event/2,
+ handle_call/2,
+ handle_info/2,
+ terminate/2,
+ code_change/3]).
+
+%% CAUTION: Signal handling in this module must be kept consistent
+%% with the same handling in rabbitmq-server(8).
+
+%% #{signal => default | ignore | stop}.
+-define(SIGNALS_HANDLED_BY_US,
+ #{
+ %% SIGHUP is often used to reload the configuration or reopen
+ %% log files after they were rotated. We don't support any
+ %% of those two cases, so ignore it for now, until we can do
+ %% something about it.
+ sighup => ignore,
+
+ %% SIGTSTP is triggered by Ctrl+Z to pause a program. However
+ %% we can't handle SIGCONT, the signal used to resume the
+ %% program. Unfortunately, it makes a SIGTSTP handler less
+ %% useful here.
+ sigtstp => ignore
+ }).
+
+-define(SIGNAL_HANDLED_BY_ERLANG(Signal),
+ Signal =:= sigusr1 orelse
+ Signal =:= sigquit orelse
+ Signal =:= sigterm).
+
+-define(SERVER, erl_signal_server).
+
+setup() ->
+ case whereis(?SERVER) of
+ undefined ->
+ ok;
+ _ ->
+ case lists:member(?MODULE, gen_event:which_handlers(?SERVER)) of
+ true -> ok;
+ false -> gen_event:add_handler(?SERVER, ?MODULE, [])
+ end
+ end.
+
+init(_Args) ->
+ maps:fold(
+ fun
+ (Signal, _, Ret) when ?SIGNAL_HANDLED_BY_ERLANG(Signal) -> Ret;
+ (Signal, default, ok) -> os:set_signal(Signal, default);
+ (Signal, ignore, ok) -> os:set_signal(Signal, ignore);
+ (Signal, _, ok) -> os:set_signal(Signal, handle)
+ end, ok, ?SIGNALS_HANDLED_BY_US),
+ {ok, #{}}.
+
+handle_event(Signal, State) when ?SIGNAL_HANDLED_BY_ERLANG(Signal) ->
+ {ok, State};
+handle_event(Signal, State) ->
+ case ?SIGNALS_HANDLED_BY_US of
+ #{Signal := stop} ->
+ error_logger:info_msg(
+ "~s received - shutting down~n",
+ [string:uppercase(atom_to_list(Signal))]),
+ ok = init:stop();
+ _ ->
+ error_logger:info_msg(
+ "~s received - unhandled signal~n",
+ [string:uppercase(atom_to_list(Signal))])
+ end,
+ {ok, State}.
+
+handle_info(_, State) ->
+ {ok, State}.
+
+handle_call(_, State) ->
+ {ok, ok, State}.
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+terminate(_Args, _State) ->
+ ok.