aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEvgeniy Khramtsov <ekhramtsov@process-one.net>2013-07-07 02:11:01 +1000
committerEvgeniy Khramtsov <ekhramtsov@process-one.net>2013-07-07 02:19:51 +1000
commit0aca3a4585a8f2308f22f74536bd775396fcaa17 (patch)
tree33c05b865e2619f31199d2ccfc6efdee0dec1a17
parentImprove the XMPP codec, fix the test suite accordingly (diff)
Improve the applications start-up
* Check if all modules present for every application loaded. * Get rid of now obsoleted 'ejabberd_check' module.
-rw-r--r--src/ejabberd.erl96
-rw-r--r--src/ejabberd_app.erl4
-rw-r--r--src/ejabberd_check.erl111
-rw-r--r--src/ejabberd_rdbms.erl26
4 files changed, 102 insertions, 135 deletions
diff --git a/src/ejabberd.erl b/src/ejabberd.erl
index 7b83f19a1..78ef10089 100644
--- a/src/ejabberd.erl
+++ b/src/ejabberd.erl
@@ -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.
diff --git a/src/ejabberd_app.erl b/src/ejabberd_app.erl
index 4a95a11f8..ed67f0335 100644
--- a/src/ejabberd_app.erl
+++ b/src/ejabberd_app.erl
@@ -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
diff --git a/src/ejabberd_check.erl b/src/ejabberd_check.erl
deleted file mode 100644
index 5dde19186..000000000
--- a/src/ejabberd_check.erl
+++ /dev/null
@@ -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.
diff --git a/src/ejabberd_rdbms.erl b/src/ejabberd_rdbms.erl
index b35786cde..a7324e4ca 100644
--- a/src/ejabberd_rdbms.erl
+++ b/src/ejabberd_rdbms.erl
@@ -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.