1 |
|
%%%---------------------------------------------------------------------- |
2 |
|
%%% File : ejabberd_app.erl |
3 |
|
%%% Author : Alexey Shchepin <alexey@process-one.net> |
4 |
|
%%% Purpose : ejabberd's application callback module |
5 |
|
%%% Created : 31 Jan 2003 by Alexey Shchepin <alexey@process-one.net> |
6 |
|
%%% |
7 |
|
%%% |
8 |
|
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne |
9 |
|
%%% |
10 |
|
%%% This program is free software; you can redistribute it and/or |
11 |
|
%%% modify it under the terms of the GNU General Public License as |
12 |
|
%%% published by the Free Software Foundation; either version 2 of the |
13 |
|
%%% License, or (at your option) any later version. |
14 |
|
%%% |
15 |
|
%%% This program is distributed in the hope that it will be useful, |
16 |
|
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 |
|
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
18 |
|
%%% General Public License for more details. |
19 |
|
%%% |
20 |
|
%%% You should have received a copy of the GNU General Public License |
21 |
|
%%% along with this program; if not, write to the Free Software |
22 |
|
%%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
23 |
|
%%% |
24 |
|
%%%---------------------------------------------------------------------- |
25 |
|
|
26 |
|
-module(ejabberd_app). |
27 |
|
-author('alexey@process-one.net'). |
28 |
|
|
29 |
|
-behaviour(application). |
30 |
|
|
31 |
|
-export([start/2, prep_stop/1, stop/1]). |
32 |
|
|
33 |
|
-ignore_xref([prep_stop/1]). |
34 |
|
|
35 |
|
-include("mongoose.hrl"). |
36 |
|
|
37 |
|
|
38 |
|
%%% |
39 |
|
%%% Application API |
40 |
|
%%% |
41 |
|
|
42 |
|
start(normal, _Args) -> |
43 |
53 |
try |
44 |
53 |
do_start() |
45 |
|
catch Class:Reason:StackTrace -> |
46 |
|
%% Log a stacktrace because while proc_lib:crash_report/4 would report a crash reason, |
47 |
|
%% it would not report the stacktrace |
48 |
:-( |
?LOG_CRITICAL(#{what => app_failed_to_start, |
49 |
:-( |
class => Class, reason => Reason, stacktrace => StackTrace}), |
50 |
:-( |
erlang:raise(Class, Reason, StackTrace) |
51 |
|
end; |
52 |
|
start(_, _) -> |
53 |
:-( |
{error, badarg}. |
54 |
|
|
55 |
|
do_start() -> |
56 |
53 |
mongoose_fips:notify(), |
57 |
53 |
write_pid_file(), |
58 |
53 |
update_status_file(starting), |
59 |
53 |
mongoose_config:start(), |
60 |
53 |
mongoose_metrics:init(), |
61 |
53 |
mongoose_internal_databases:init(), |
62 |
53 |
mongoose_graphql:init(), |
63 |
53 |
translate:start(), |
64 |
53 |
mongoose_graphql_commands:start(), |
65 |
53 |
mongoose_router:start(), |
66 |
53 |
mongoose_logs:set_global_loglevel(mongoose_config:get_opt(loglevel)), |
67 |
53 |
mongoose_deprecations:start(), |
68 |
53 |
{ok, _} = Sup = ejabberd_sup:start_link(), |
69 |
53 |
mongoose_wpool:ensure_started(), |
70 |
53 |
mongoose_wpool:start_configured_pools(), |
71 |
|
%% ejabberd_sm is started separately because it may use one of the outgoing_pools |
72 |
|
%% but some outgoing_pools should be started only with ejabberd_sup already running |
73 |
53 |
ejabberd_sm:start(), |
74 |
53 |
ejabberd_auth:start(), |
75 |
53 |
mongoose_cluster_id:start(), |
76 |
53 |
mongoose_service:start(), |
77 |
53 |
mongoose_modules:start(), |
78 |
53 |
service_mongoose_system_metrics:verify_if_configured(), |
79 |
53 |
mongoose_listener:start(), |
80 |
53 |
mongoose_metrics:init_mongooseim_metrics(), |
81 |
53 |
gen_hook:reload_hooks(), |
82 |
53 |
update_status_file(started), |
83 |
53 |
?LOG_NOTICE(#{what => mongooseim_node_started, version => ?MONGOOSE_VERSION, node => node()}), |
84 |
53 |
Sup. |
85 |
|
|
86 |
|
%% @doc Prepare the application for termination. |
87 |
|
%% This function is called when an application is about to be stopped, |
88 |
|
%% before shutting down the processes of the application. |
89 |
|
prep_stop(State) -> |
90 |
54 |
mongoose_deprecations:stop(), |
91 |
54 |
broadcast_c2s_shutdown_listeners(), |
92 |
53 |
mongoose_listener:stop(), |
93 |
53 |
mongoose_modules:stop(), |
94 |
53 |
mongoose_service:stop(), |
95 |
53 |
broadcast_c2s_shutdown_sup(), |
96 |
53 |
mongoose_wpool:stop(), |
97 |
53 |
mongoose_metrics:remove_all_metrics(), |
98 |
53 |
mongoose_config:stop(), |
99 |
53 |
mongoose_graphql_commands:stop(), |
100 |
53 |
State. |
101 |
|
|
102 |
|
%% All the processes were killed when this function is called |
103 |
|
stop(_State) -> |
104 |
54 |
?LOG_NOTICE(#{what => mongooseim_node_stopped, version => ?MONGOOSE_VERSION, node => node()}), |
105 |
54 |
delete_pid_file(), |
106 |
54 |
update_status_file(stopped), |
107 |
|
%% We cannot stop other applications inside of the stop callback |
108 |
|
%% (because we would deadlock the application controller process). |
109 |
|
%% That is why we call mnesia:stop() inside of db_init_mnesia() instead. |
110 |
54 |
ok. |
111 |
|
|
112 |
|
%%% |
113 |
|
%%% Internal functions |
114 |
|
%%% |
115 |
|
|
116 |
|
-spec broadcast_c2s_shutdown_listeners() -> ok. |
117 |
|
broadcast_c2s_shutdown_listeners() -> |
118 |
54 |
Children = supervisor:which_children(mongoose_listener_sup), |
119 |
53 |
Listeners = [Ref || {Ref, _, _, [mongoose_c2s_listener]} <- Children], |
120 |
53 |
lists:foreach( |
121 |
|
fun(Listener) -> |
122 |
104 |
ranch:suspend_listener(Listener), |
123 |
104 |
[mongoose_c2s:exit(Pid, system_shutdown) || Pid <- ranch:procs(Listener, connections)], |
124 |
104 |
mongoose_lib:wait_until( |
125 |
|
fun() -> |
126 |
106 |
length(ranch:procs(Listener, connections)) |
127 |
|
end, |
128 |
|
0) |
129 |
|
end, |
130 |
|
Listeners). |
131 |
|
|
132 |
|
-spec broadcast_c2s_shutdown_sup() -> ok. |
133 |
|
broadcast_c2s_shutdown_sup() -> |
134 |
53 |
Children = supervisor:which_children(mongoose_c2s_sup), |
135 |
53 |
lists:foreach( |
136 |
|
fun({_, Pid, _, _}) -> |
137 |
:-( |
mongoose_c2s:exit(Pid, system_shutdown) |
138 |
|
end, |
139 |
|
Children), |
140 |
53 |
mongoose_lib:wait_until( |
141 |
|
fun() -> |
142 |
53 |
Res = supervisor:count_children(mongoose_c2s_sup), |
143 |
53 |
proplists:get_value(active, Res) |
144 |
|
end, |
145 |
|
0). |
146 |
|
|
147 |
|
%%% |
148 |
|
%%% PID file |
149 |
|
%%% |
150 |
|
|
151 |
|
-spec write_pid_file() -> 'ok' | {'error', atom()}. |
152 |
|
write_pid_file() -> |
153 |
53 |
case ejabberd:get_pid_file() of |
154 |
|
false -> |
155 |
:-( |
ok; |
156 |
|
PidFilename -> |
157 |
53 |
write_pid_file(os:getpid(), PidFilename) |
158 |
|
end. |
159 |
|
|
160 |
|
-spec write_pid_file(Pid :: string(), |
161 |
|
PidFilename :: nonempty_string() |
162 |
|
) -> 'ok' | {'error', atom()}. |
163 |
|
write_pid_file(Pid, PidFilename) -> |
164 |
53 |
case file:open(PidFilename, [write]) of |
165 |
|
{ok, Fd} -> |
166 |
53 |
io:format(Fd, "~s~n", [Pid]), |
167 |
53 |
file:close(Fd); |
168 |
|
{error, Reason} -> |
169 |
:-( |
?LOG_ERROR(#{what => cannot_write_to_pid_file, |
170 |
:-( |
pid_file => PidFilename, reason => Reason}), |
171 |
:-( |
throw({cannot_write_pid_file, PidFilename, Reason}) |
172 |
|
end. |
173 |
|
|
174 |
|
update_status_file(Status) -> |
175 |
160 |
case ejabberd:get_status_file() of |
176 |
|
false -> |
177 |
:-( |
ok; |
178 |
|
StatusFilename -> |
179 |
160 |
file:write_file(StatusFilename, atom_to_list(Status)) |
180 |
|
end. |
181 |
|
|
182 |
|
-spec delete_pid_file() -> 'ok' | {'error', atom()}. |
183 |
|
delete_pid_file() -> |
184 |
54 |
case ejabberd:get_pid_file() of |
185 |
|
false -> |
186 |
:-( |
ok; |
187 |
|
PidFilename -> |
188 |
54 |
file:delete(PidFilename) |
189 |
|
end. |