summaryrefslogtreecommitdiff
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
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
-rw-r--r--apps/rabbitmq_prelaunch/src/rabbit_prelaunch.erl3
-rw-r--r--apps/rabbitmq_prelaunch/src/rabbit_prelaunch_sighandler.erl85
-rwxr-xr-xscripts/rabbitmq-server11
-rw-r--r--src/rabbit.erl6
-rw-r--r--src/rabbit_os_signal_handler.erl60
5 files changed, 97 insertions, 68 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.
diff --git a/scripts/rabbitmq-server b/scripts/rabbitmq-server
index 5939c845d2..0976c5566a 100755
--- a/scripts/rabbitmq-server
+++ b/scripts/rabbitmq-server
@@ -141,7 +141,9 @@ else
# Signal handlers. They all stop RabbitMQ properly, using
# rabbitmqctl stop. This script will exit with different exit codes:
- # SIGHUP SIGTERM SIGTSTP
+ # SIGHUP, SIGTSTP + SIGCONT
+ # Ignored until we implement a useful behavior.
+ # SIGTERM
# Exits 0 since this is considered a normal process termination.
# SIGINT
# Exits 128 + $signal_number where $signal_number is 2 for SIGINT (see
@@ -150,7 +152,12 @@ else
# don't need to specify this exit code because the shell propagates it.
# Unfortunately, the signal handler doesn't work as expected in Dash,
# thus we need to explicitly restate the exit code.
- trap "stop_rabbitmq_server; exit 0" HUP TERM TSTP
+ #
+ # The behaviors below should remain consistent with the
+ # equivalent signal handlers in the Erlang code
+ # (see apps/rabbitmq_prelaunch/src/rabbit_prelaunch_sighandler.erl).
+ trap '' HUP TSTP CONT
+ trap "stop_rabbitmq_server; exit 0" TERM
trap "stop_rabbitmq_server; exit 130" INT
start_rabbitmq_server "$@" &
diff --git a/src/rabbit.erl b/src/rabbit.erl
index 0f98e50504..12b12bb222 100644
--- a/src/rabbit.erl
+++ b/src/rabbit.erl
@@ -235,12 +235,6 @@
{requires, pre_flight}
]}).
--rabbit_boot_step({os_signal_handler,
- [{description, "registers an OS signal handler"},
- {mfa, {rabbit_sup, start_restartable_child,
- [rabbit_os_signal_handler]}},
- {requires, pre_flight}]}).
-
-rabbit_boot_step({direct_client,
[{description, "direct client"},
{mfa, {rabbit_direct, boot, []}},
diff --git a/src/rabbit_os_signal_handler.erl b/src/rabbit_os_signal_handler.erl
deleted file mode 100644
index d221143379..0000000000
--- a/src/rabbit_os_signal_handler.erl
+++ /dev/null
@@ -1,60 +0,0 @@
--module(rabbit_os_signal_handler).
-
--behaviour(gen_event).
-
--export([start_link/0, init/1,
- handle_event/2, handle_call/2, handle_info/2,
- terminate/2]).
-
-%%
-%% API
-%%
-
-start_link() ->
- rabbit_log:info("Swapping OS signal event handler (erl_signal_server) for our own"),
- %% delete any previous incarnations, otherwise we would be accumulating
- %% handlers
- _ = gen_event:delete_handler(erl_signal_server, ?MODULE, []),
- %% swap the standard OTP signal handler if there is one
- ok = gen_event:swap_sup_handler(
- erl_signal_server,
- %% what to swap
- {erl_signal_handler, []},
- %% new event handler
- {?MODULE, []}),
- gen_event:start_link({local, ?MODULE}).
-
-init(_) ->
- {ok, #{}}.
-
-handle_event(sigterm, State) ->
- rabbit_log:info("Received a SIGTERM, will shut down gracefully"),
- rabbit:stop_and_halt(),
- {ok, State};
-handle_event(sigquit, State) ->
- rabbit_log:info("Received a SIGQUIT, will shut down gracefully"),
- rabbit:stop_and_halt(),
- {ok, State};
-handle_event(sigusr1, State) ->
- rabbit_log:info("Received a SIGUSR1, ignoring it"),
- {ok, State};
-handle_event(sigusr2, State) ->
- rabbit_log:info("Received a SIGUSR2, ignoring it"),
- {ok, State};
-%% note: SIGHUP can/will be handled by shells and process managers
-handle_event(sighup, State) ->
- rabbit_log:info("Received a SIGHUP, ignoring it"),
- {ok, State};
-handle_event(Msg, S) ->
- %% delegate all unknown events to the default OTP signal handler
- erl_signal_handler:handle_event(Msg, S),
- {ok, S}.
-
-handle_info(_, State) ->
- {ok, State}.
-
-handle_call(_Request, State) ->
- {ok, ok, State}.
-
-terminate(_Args, _State) ->
- ok.