mirror of
https://github.com/processone/ejabberd.git
synced 2024-11-20 16:15:59 +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).
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-export([start/0, stop/0, start_app/1,
|
||||
get_pid_file/0]).
|
||||
-export([start/0, stop/0, start_app/1, start_app/2,
|
||||
get_pid_file/0, check_app/1]).
|
||||
|
||||
-include("logger.hrl").
|
||||
|
||||
@ -51,27 +51,93 @@ get_pid_file() ->
|
||||
Path
|
||||
end.
|
||||
|
||||
start_app(App) when not is_list(App) ->
|
||||
start_app([App]);
|
||||
start_app([App|Apps]) ->
|
||||
start_app(App) ->
|
||||
start_app(App, temporary).
|
||||
|
||||
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
|
||||
ok ->
|
||||
start_app(Apps);
|
||||
spawn(fun() -> check_app_modules(App, StartFlag) end),
|
||||
start_app(Apps, Type, StartFlag);
|
||||
{error, {already_started, _}} ->
|
||||
start_app(Apps);
|
||||
start_app(Apps, Type, StartFlag);
|
||||
{error, {not_started, DepApp}} ->
|
||||
case lists:member(DepApp, [App|Apps]) of
|
||||
true ->
|
||||
?CRITICAL_MSG("failed to start application '~p': "
|
||||
"circular dependency on '~p' detected",
|
||||
[App, DepApp]),
|
||||
erlang:error(application_start_failed);
|
||||
Reason = io_lib:format(
|
||||
"failed to start application '~p': "
|
||||
"circular dependency on '~p' detected",
|
||||
[App, DepApp]),
|
||||
exit_or_halt(Reason, StartFlag);
|
||||
false ->
|
||||
start_app([DepApp,App|Apps])
|
||||
start_app([DepApp,App|Apps], Type, StartFlag)
|
||||
end;
|
||||
Err ->
|
||||
?CRITICAL_MSG("failed to start application '~p': ~p", [App, Err]),
|
||||
erlang:error(application_start_failed)
|
||||
Reason = io_lib:format("failed to start application '~p': ~p",
|
||||
[App, Err]),
|
||||
exit_or_halt(Reason, StartFlag)
|
||||
end;
|
||||
start_app([]) ->
|
||||
start_app([], _Type, _StartFlag) ->
|
||||
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(),
|
||||
write_pid_file(),
|
||||
start_apps(),
|
||||
ejabberd:check_app(ejabberd),
|
||||
randoms:start(),
|
||||
db_init(),
|
||||
start(),
|
||||
@ -52,7 +53,6 @@ start(normal, _Args) ->
|
||||
ejabberd_admin:start(),
|
||||
gen_mod:start(),
|
||||
ejabberd_config:start(),
|
||||
ejabberd_check:config(),
|
||||
connect_nodes(),
|
||||
Sup = ejabberd_sup:start_link(),
|
||||
ejabberd_rdbms:start(),
|
||||
@ -112,7 +112,7 @@ db_init() ->
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
application:start(mnesia, permanent),
|
||||
ejabberd:start_app(mnesia, permanent),
|
||||
mnesia:wait_for_tables(mnesia:system_info(local_tables), infinity).
|
||||
|
||||
%% 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").
|
||||
|
||||
start() ->
|
||||
case lists:any(fun needs_odbc/1, ?MYHOSTS) of
|
||||
case lists:any(fun(H) -> needs_odbc(H) /= false end,
|
||||
?MYHOSTS) of
|
||||
true ->
|
||||
start_hosts();
|
||||
false ->
|
||||
@ -45,14 +46,15 @@ start() ->
|
||||
start_hosts() ->
|
||||
lists:foreach(fun (Host) ->
|
||||
case needs_odbc(Host) of
|
||||
true -> start_odbc(Host);
|
||||
{true, App} -> start_odbc(Host, App);
|
||||
false -> ok
|
||||
end
|
||||
end,
|
||||
?MYHOSTS).
|
||||
|
||||
%% 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,
|
||||
ejabberd_odbc_sup),
|
||||
ChildSpec = {Supervisor_name,
|
||||
@ -64,11 +66,21 @@ start_odbc(Host) ->
|
||||
?ERROR_MSG("Start of supervisor ~p failed:~n~p~nRetrying."
|
||||
"..~n",
|
||||
[Supervisor_name, _Error]),
|
||||
start_odbc(Host)
|
||||
start_odbc(Host, App)
|
||||
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) ->
|
||||
LHost = jlib:nameprep(Host),
|
||||
ejabberd_config:get_local_option(
|
||||
{odbc_server, LHost}, fun(_) -> true end, false).
|
||||
case ejabberd_config:get_local_option(
|
||||
{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