mirror of
https://github.com/processone/ejabberd.git
synced 2024-11-28 16:34:13 +01:00
Improve the applications start-up
* Check if all modules present for every application loaded. * Get rid of now obsoleted 'ejabberd_check' module.
This commit is contained in:
parent
c262c08513
commit
0aca3a4585
@ -27,8 +27,8 @@
|
|||||||
-module(ejabberd).
|
-module(ejabberd).
|
||||||
-author('alexey@process-one.net').
|
-author('alexey@process-one.net').
|
||||||
|
|
||||||
-export([start/0, stop/0, start_app/1,
|
-export([start/0, stop/0, start_app/1, start_app/2,
|
||||||
get_pid_file/0]).
|
get_pid_file/0, check_app/1]).
|
||||||
|
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
|
|
||||||
@ -51,27 +51,93 @@ get_pid_file() ->
|
|||||||
Path
|
Path
|
||||||
end.
|
end.
|
||||||
|
|
||||||
start_app(App) when not is_list(App) ->
|
start_app(App) ->
|
||||||
start_app([App]);
|
start_app(App, temporary).
|
||||||
start_app([App|Apps]) ->
|
|
||||||
|
start_app(App, Type) ->
|
||||||
|
StartFlag = not is_loaded(),
|
||||||
|
start_app(App, Type, StartFlag).
|
||||||
|
|
||||||
|
check_app(App) ->
|
||||||
|
StartFlag = not is_loaded(),
|
||||||
|
spawn(fun() -> check_app_modules(App, StartFlag) end),
|
||||||
|
ok.
|
||||||
|
|
||||||
|
is_loaded() ->
|
||||||
|
Apps = application:which_applications(),
|
||||||
|
lists:keymember(ejabberd, 1, Apps).
|
||||||
|
|
||||||
|
start_app(App, Type, StartFlag) when not is_list(App) ->
|
||||||
|
start_app([App], Type, StartFlag);
|
||||||
|
start_app([App|Apps], Type, StartFlag) ->
|
||||||
case application:start(App) of
|
case application:start(App) of
|
||||||
ok ->
|
ok ->
|
||||||
start_app(Apps);
|
spawn(fun() -> check_app_modules(App, StartFlag) end),
|
||||||
|
start_app(Apps, Type, StartFlag);
|
||||||
{error, {already_started, _}} ->
|
{error, {already_started, _}} ->
|
||||||
start_app(Apps);
|
start_app(Apps, Type, StartFlag);
|
||||||
{error, {not_started, DepApp}} ->
|
{error, {not_started, DepApp}} ->
|
||||||
case lists:member(DepApp, [App|Apps]) of
|
case lists:member(DepApp, [App|Apps]) of
|
||||||
true ->
|
true ->
|
||||||
?CRITICAL_MSG("failed to start application '~p': "
|
Reason = io_lib:format(
|
||||||
|
"failed to start application '~p': "
|
||||||
"circular dependency on '~p' detected",
|
"circular dependency on '~p' detected",
|
||||||
[App, DepApp]),
|
[App, DepApp]),
|
||||||
erlang:error(application_start_failed);
|
exit_or_halt(Reason, StartFlag);
|
||||||
false ->
|
false ->
|
||||||
start_app([DepApp,App|Apps])
|
start_app([DepApp,App|Apps], Type, StartFlag)
|
||||||
end;
|
end;
|
||||||
Err ->
|
Err ->
|
||||||
?CRITICAL_MSG("failed to start application '~p': ~p", [App, Err]),
|
Reason = io_lib:format("failed to start application '~p': ~p",
|
||||||
erlang:error(application_start_failed)
|
[App, Err]),
|
||||||
|
exit_or_halt(Reason, StartFlag)
|
||||||
end;
|
end;
|
||||||
start_app([]) ->
|
start_app([], _Type, _StartFlag) ->
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
|
check_app_modules(App, StartFlag) ->
|
||||||
|
{A, B, C} = now(),
|
||||||
|
random:seed(A, B, C),
|
||||||
|
sleep(5000),
|
||||||
|
case application:get_key(App, modules) of
|
||||||
|
{ok, Mods} ->
|
||||||
|
lists:foreach(
|
||||||
|
fun(Mod) ->
|
||||||
|
case code:which(Mod) of
|
||||||
|
non_existing ->
|
||||||
|
File = get_module_file(App, Mod),
|
||||||
|
Reason = io_lib:format(
|
||||||
|
"couldn't find module ~s "
|
||||||
|
"needed for application '~p'",
|
||||||
|
[File, App]),
|
||||||
|
exit_or_halt(Reason, StartFlag);
|
||||||
|
_ ->
|
||||||
|
sleep(10)
|
||||||
|
end
|
||||||
|
end, Mods);
|
||||||
|
_ ->
|
||||||
|
%% No modules? This is strange
|
||||||
|
ok
|
||||||
|
end.
|
||||||
|
|
||||||
|
exit_or_halt(Reason, StartFlag) ->
|
||||||
|
?CRITICAL_MSG(Reason, []),
|
||||||
|
if StartFlag ->
|
||||||
|
%% Wait for the critical message is written in the console/log
|
||||||
|
timer:sleep(1000),
|
||||||
|
halt(string:substr(lists:flatten(Reason), 1, 199));
|
||||||
|
true ->
|
||||||
|
erlang:error(application_start_failed)
|
||||||
|
end.
|
||||||
|
|
||||||
|
sleep(N) ->
|
||||||
|
timer:sleep(random:uniform(N)).
|
||||||
|
|
||||||
|
get_module_file(App, Mod) ->
|
||||||
|
BaseName = atom_to_list(Mod),
|
||||||
|
case code:lib_dir(App, ebin) of
|
||||||
|
{error, _} ->
|
||||||
|
BaseName;
|
||||||
|
Dir ->
|
||||||
|
filename:join([Dir, BaseName ++ ".beam"])
|
||||||
|
end.
|
||||||
|
@ -42,6 +42,7 @@ start(normal, _Args) ->
|
|||||||
ejabberd_logger:start(),
|
ejabberd_logger:start(),
|
||||||
write_pid_file(),
|
write_pid_file(),
|
||||||
start_apps(),
|
start_apps(),
|
||||||
|
ejabberd:check_app(ejabberd),
|
||||||
randoms:start(),
|
randoms:start(),
|
||||||
db_init(),
|
db_init(),
|
||||||
start(),
|
start(),
|
||||||
@ -52,7 +53,6 @@ start(normal, _Args) ->
|
|||||||
ejabberd_admin:start(),
|
ejabberd_admin:start(),
|
||||||
gen_mod:start(),
|
gen_mod:start(),
|
||||||
ejabberd_config:start(),
|
ejabberd_config:start(),
|
||||||
ejabberd_check:config(),
|
|
||||||
connect_nodes(),
|
connect_nodes(),
|
||||||
Sup = ejabberd_sup:start_link(),
|
Sup = ejabberd_sup:start_link(),
|
||||||
ejabberd_rdbms:start(),
|
ejabberd_rdbms:start(),
|
||||||
@ -112,7 +112,7 @@ db_init() ->
|
|||||||
_ ->
|
_ ->
|
||||||
ok
|
ok
|
||||||
end,
|
end,
|
||||||
application:start(mnesia, permanent),
|
ejabberd:start_app(mnesia, permanent),
|
||||||
mnesia:wait_for_tables(mnesia:system_info(local_tables), infinity).
|
mnesia:wait_for_tables(mnesia:system_info(local_tables), infinity).
|
||||||
|
|
||||||
%% Start all the modules in all the hosts
|
%% Start all the modules in all the hosts
|
||||||
|
@ -1,111 +0,0 @@
|
|||||||
%%%----------------------------------------------------------------------
|
|
||||||
%%% File : ejabberd_check.erl
|
|
||||||
%%% Author : Mickael Remond <mremond@process-one.net>
|
|
||||||
%%% Purpose : Check ejabberd configuration and
|
|
||||||
%%% Created : 27 Feb 2008 by Mickael Remond <mremond@process-one.net>
|
|
||||||
%%%
|
|
||||||
%%%
|
|
||||||
%%% ejabberd, Copyright (C) 2002-2013 ProcessOne
|
|
||||||
%%%
|
|
||||||
%%% This program is free software; you can redistribute it and/or
|
|
||||||
%%% modify it under the terms of the GNU General Public License as
|
|
||||||
%%% published by the Free Software Foundation; either version 2 of the
|
|
||||||
%%% License, or (at your option) any later version.
|
|
||||||
%%%
|
|
||||||
%%% This program is distributed in the hope that it will be useful,
|
|
||||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
%%% General Public License for more details.
|
|
||||||
%%%
|
|
||||||
%%% You should have received a copy of the GNU General Public License
|
|
||||||
%%% along with this program; if not, write to the Free Software
|
|
||||||
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
|
||||||
%%% 02111-1307 USA
|
|
||||||
%%%
|
|
||||||
%%%----------------------------------------------------------------------
|
|
||||||
|
|
||||||
-module(ejabberd_check).
|
|
||||||
|
|
||||||
-export([libs/0, config/0]).
|
|
||||||
|
|
||||||
-include("ejabberd.hrl").
|
|
||||||
-include("logger.hrl").
|
|
||||||
-include("ejabberd_config.hrl").
|
|
||||||
|
|
||||||
%% TODO:
|
|
||||||
%% We want to implement library checking at launch time to issue
|
|
||||||
%% human readable user messages.
|
|
||||||
libs() ->
|
|
||||||
ok.
|
|
||||||
|
|
||||||
%% @doc Consistency check on ejabberd configuration
|
|
||||||
config() ->
|
|
||||||
check_database_modules().
|
|
||||||
|
|
||||||
check_database_modules() ->
|
|
||||||
[check_database_module(M)||M<-get_db_used()].
|
|
||||||
|
|
||||||
check_database_module(odbc) ->
|
|
||||||
check_modules(odbc, [odbc, odbc_app, odbc_sup, ejabberd_odbc, ejabberd_odbc_sup, odbc_queries]);
|
|
||||||
check_database_module(mysql) ->
|
|
||||||
check_modules(mysql, [mysql, mysql_auth, mysql_conn, mysql_recv]);
|
|
||||||
check_database_module(pgsql) ->
|
|
||||||
check_modules(pgsql, [pgsql, pgsql_proto, pgsql_tcp, pgsql_util]).
|
|
||||||
|
|
||||||
%% @doc Issue a critical error and throw an exit if needing module is
|
|
||||||
%% missing.
|
|
||||||
check_modules(DB, Modules) ->
|
|
||||||
case get_missing_modules(Modules) of
|
|
||||||
[] ->
|
|
||||||
ok;
|
|
||||||
MissingModules when is_list(MissingModules) ->
|
|
||||||
?CRITICAL_MSG("ejabberd is configured to use '~p', but the following Erlang modules are not installed: '~p'", [DB, MissingModules]),
|
|
||||||
exit(database_module_missing)
|
|
||||||
end.
|
|
||||||
|
|
||||||
|
|
||||||
%% @doc Return the list of undefined modules
|
|
||||||
get_missing_modules(Modules) ->
|
|
||||||
lists:filter(fun(Module) ->
|
|
||||||
case catch Module:module_info() of
|
|
||||||
{'EXIT', {undef, _}} ->
|
|
||||||
true;
|
|
||||||
_ -> false
|
|
||||||
end
|
|
||||||
end, Modules).
|
|
||||||
|
|
||||||
%% @doc Return the list of databases used
|
|
||||||
get_db_used() ->
|
|
||||||
%% Retrieve domains with a database configured:
|
|
||||||
Domains =
|
|
||||||
ets:match(local_config, #local_config{key={odbc_server, '$1'},
|
|
||||||
value='$2'}),
|
|
||||||
%% Check that odbc is the auth method used for those domains:
|
|
||||||
%% and return the database name
|
|
||||||
DBs = lists:foldr(
|
|
||||||
fun([Domain, DB], Acc) ->
|
|
||||||
case check_odbc_option(
|
|
||||||
ejabberd_config:get_local_option(
|
|
||||||
{auth_method, Domain}, fun(V) -> V end)) of
|
|
||||||
true -> [get_db_type(DB)|Acc];
|
|
||||||
_ -> Acc
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
[], Domains),
|
|
||||||
lists:usort(DBs).
|
|
||||||
|
|
||||||
%% @doc Depending in the DB definition, return which type of DB this is.
|
|
||||||
%% Note that MSSQL is detected as ODBC.
|
|
||||||
%% @spec (DB) -> mysql | pgsql | odbc
|
|
||||||
get_db_type(DB) when is_tuple(DB) ->
|
|
||||||
element(1, DB);
|
|
||||||
get_db_type(DB) when is_list(DB) ->
|
|
||||||
odbc.
|
|
||||||
|
|
||||||
%% @doc Return true if odbc option is used
|
|
||||||
check_odbc_option(odbc) ->
|
|
||||||
true;
|
|
||||||
check_odbc_option(AuthMethods) when is_list(AuthMethods) ->
|
|
||||||
lists:member(odbc, AuthMethods);
|
|
||||||
check_odbc_option(_) ->
|
|
||||||
false.
|
|
@ -34,7 +34,8 @@
|
|||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
|
|
||||||
start() ->
|
start() ->
|
||||||
case lists:any(fun needs_odbc/1, ?MYHOSTS) of
|
case lists:any(fun(H) -> needs_odbc(H) /= false end,
|
||||||
|
?MYHOSTS) of
|
||||||
true ->
|
true ->
|
||||||
start_hosts();
|
start_hosts();
|
||||||
false ->
|
false ->
|
||||||
@ -45,14 +46,15 @@ start() ->
|
|||||||
start_hosts() ->
|
start_hosts() ->
|
||||||
lists:foreach(fun (Host) ->
|
lists:foreach(fun (Host) ->
|
||||||
case needs_odbc(Host) of
|
case needs_odbc(Host) of
|
||||||
true -> start_odbc(Host);
|
{true, App} -> start_odbc(Host, App);
|
||||||
false -> ok
|
false -> ok
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
?MYHOSTS).
|
?MYHOSTS).
|
||||||
|
|
||||||
%% Start the ODBC module on the given host
|
%% Start the ODBC module on the given host
|
||||||
start_odbc(Host) ->
|
start_odbc(Host, App) ->
|
||||||
|
ejabberd:start_app(App),
|
||||||
Supervisor_name = gen_mod:get_module_proc(Host,
|
Supervisor_name = gen_mod:get_module_proc(Host,
|
||||||
ejabberd_odbc_sup),
|
ejabberd_odbc_sup),
|
||||||
ChildSpec = {Supervisor_name,
|
ChildSpec = {Supervisor_name,
|
||||||
@ -64,11 +66,21 @@ start_odbc(Host) ->
|
|||||||
?ERROR_MSG("Start of supervisor ~p failed:~n~p~nRetrying."
|
?ERROR_MSG("Start of supervisor ~p failed:~n~p~nRetrying."
|
||||||
"..~n",
|
"..~n",
|
||||||
[Supervisor_name, _Error]),
|
[Supervisor_name, _Error]),
|
||||||
start_odbc(Host)
|
start_odbc(Host, App)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% Returns true if we have configured odbc_server for the given host
|
%% Returns {true, App} if we have configured odbc_server for the given host
|
||||||
needs_odbc(Host) ->
|
needs_odbc(Host) ->
|
||||||
LHost = jlib:nameprep(Host),
|
LHost = jlib:nameprep(Host),
|
||||||
ejabberd_config:get_local_option(
|
case ejabberd_config:get_local_option(
|
||||||
{odbc_server, LHost}, fun(_) -> true end, false).
|
{odbc_server, LHost}, fun(Res) -> Res end) of
|
||||||
|
{mysql, _, _, _, _} -> {true, p1_mysql};
|
||||||
|
{pgsql, _, _, _, _} -> {true, p1_pgsql};
|
||||||
|
{mysql, _, _, _, _, _} -> {true, p1_mysql};
|
||||||
|
{pgsql, _, _, _, _, _} -> {true, p1_pgsql};
|
||||||
|
S ->
|
||||||
|
case catch iolist_to_binary(S) of
|
||||||
|
{'EXIT', _} -> false;
|
||||||
|
_ -> true
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
Loading…
Reference in New Issue
Block a user