]> granicus.if.org Git - ejabberd/commitdiff
Improve the applications start-up
authorEvgeniy Khramtsov <ekhramtsov@process-one.net>
Sat, 6 Jul 2013 16:11:01 +0000 (02:11 +1000)
committerEvgeniy Khramtsov <ekhramtsov@process-one.net>
Sat, 6 Jul 2013 16:19:51 +0000 (02:19 +1000)
* Check if all modules present for every application loaded.
* Get rid of now obsoleted 'ejabberd_check' module.

src/ejabberd.erl
src/ejabberd_app.erl
src/ejabberd_check.erl [deleted file]
src/ejabberd_rdbms.erl

index 7b83f19a1460bf7c1eb24c55b6c7b3cf1a5f321e..78ef100892805b50e0d020a567b5df7492e52a3a 100644 (file)
@@ -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.
index 4a95a11f8d3ca417db356a6bda1d4fbb40e6fc77..ed67f033595d00bb4da9581d8ee10992f9e98f49 100644 (file)
@@ -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 (file)
index 5dde191..0000000
+++ /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.
index b35786cdec77d6841d6e93b8a190042bc2edfce1..a7324e4cabbd64ef3c41897f9a77bda06993a0c5 100644 (file)
@@ -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.