emqx_machine_terminator.erl 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. %%--------------------------------------------------------------------
  2. %% Copyright (c) 2021-2022 EMQ Technologies Co., Ltd. All Rights Reserved.
  3. %%
  4. %% Licensed under the Apache License, Version 2.0 (the "License");
  5. %% you may not use this file except in compliance with the License.
  6. %% You may obtain a copy of the License at
  7. %%
  8. %% http://www.apache.org/licenses/LICENSE-2.0
  9. %%
  10. %% Unless required by applicable law or agreed to in writing, software
  11. %% distributed under the License is distributed on an "AS IS" BASIS,
  12. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. %% See the License for the specific language governing permissions and
  14. %% limitations under the License.
  15. %%--------------------------------------------------------------------
  16. -module(emqx_machine_terminator).
  17. -behaviour(gen_server).
  18. -export([ start_link/0
  19. , graceful/0
  20. , graceful_wait/0
  21. , is_running/0
  22. ]).
  23. -export([init/1, format_status/2,
  24. handle_cast/2, handle_call/3, handle_info/2,
  25. terminate/2, code_change/3]).
  26. -include_lib("emqx/include/logger.hrl").
  27. -define(TERMINATOR, ?MODULE).
  28. -define(DO_IT, graceful_shutdown).
  29. %% @doc This API is called to shutdown the Erlang VM by RPC call from remote shell node.
  30. %% The shutown of apps is delegated to a to a process instead of doing it in the RPC spawned
  31. %% process which has a remote group leader.
  32. start_link() ->
  33. {ok, _} = gen_server:start_link({local, ?TERMINATOR}, ?MODULE, [], []).
  34. is_running() -> is_pid(whereis(?TERMINATOR)).
  35. %% @doc Call `emqx_machine_terminator' to stop applications
  36. %% then call init:stop() stop beam.
  37. graceful() ->
  38. try
  39. _ = gen_server:call(?TERMINATOR, ?DO_IT, infinity)
  40. catch
  41. _ : _ ->
  42. %% failed to notify terminator, probably due to not started yet
  43. %% or node is going down, either case, the caller
  44. %% should issue a shutdown to be sure
  45. %% NOTE: not exit_loop here because we do not want to
  46. %% block erl_signal_server
  47. ?ELOG("Shutdown before node is ready?~n", []),
  48. init:stop()
  49. end,
  50. ok.
  51. %% @doc Shutdown the Erlang VM and wait indefinitely.
  52. graceful_wait() ->
  53. ok = graceful(),
  54. exit_loop().
  55. exit_loop() ->
  56. timer:sleep(100),
  57. init:stop(),
  58. exit_loop().
  59. init(_) ->
  60. ok = emqx_machine_signal_handler:start(),
  61. {ok, #{}}.
  62. handle_info(_, State) ->
  63. {noreply, State}.
  64. handle_cast(_Cast, State) ->
  65. {noreply, State}.
  66. handle_call(?DO_IT, _From, State) ->
  67. try
  68. emqx_machine_boot:stop_apps()
  69. catch
  70. C : E : St ->
  71. Apps = [element(1, A) || A <- application:which_applications()],
  72. ?SLOG(error, #{msg => "failed_to_stop_apps",
  73. exception => C,
  74. reason => E,
  75. stacktrace => St,
  76. remaining_apps => Apps
  77. })
  78. after
  79. init:stop()
  80. end,
  81. {reply, ok, State};
  82. handle_call(_Call, _From, State) ->
  83. {noreply, State}.
  84. format_status(_Opt, [_Pdict,_S]) ->
  85. ok.
  86. code_change(_OldVsn, State, _Extra) ->
  87. {ok, State}.
  88. terminate(_Args, _State) ->
  89. ok.